最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

typescript - Certain parts of extension only update when refreshing the extension - Stack Overflow

programmeradmin2浏览0评论

I hope this is the right place to ask, if it's not, please tell me where I should post it.

I'm making a pomodoro timer browser extension using WXT and Svelte.js with Typescript. My extension works quite as it should, the problem is that certain things only really update when I close and reopen the extension. It might be that I'm misusing background.ts a bit. I tried lots of different stuff, placing some events on different places, tried normal functions and async on some places but it all didn't help... Could someone give me a few tips how I make this extension dynamic and make it work properly? I'm already stuck for quite some time now...

Here's my code for Start.svelte (element for start/pause button), TimerType.svelte (to change timers, the timer count does not update dynamically) and background.ts.

<!-- Start.svelte -->
<script lang="ts">
  import {
    POMODORO as pomodoro,
    SHORT_BREAK as shortBreak,
    LONG_BREAK as longBreak,
  } from "@/utils/constants";
  import pauseIcon from "~/assets/icon/pause.png";
  import playIcon from "~/assets/icon/play.png";
  import timeUp from "~/assets/sound/time-up.wav";
  import toDoubleDigit from "@/utils/toDoubleDigit";
  import { onMount } from "svelte";

  const timeUpSound = new Audio(timeUp);

  export let buttonState: "START" | "PAUSE" = "START";
  export let timer: HTMLElement | null = document.getElementById("timer");
  export let timerType: "POMODORO" | "SHORT_BREAK" | "LONG_BREAK" = "POMODORO";
  export let completedSessions = {
    completedPomodoros: 0,
    completedShortBreaks: 0,
    completedLongBreaks: 0,
  };

  let timeBetween: number = 0;
  let shouldUpdateTimer = true;

  onMount(async () => {
    const state = await browser.runtime.sendMessage({ type: "GET_STATE" });
    if (state) {
      timerType = state.timerType;
      completedSessions = statepletedSessions;
    }
    initializeTimer(timerType);
  });

  const initializeTimer = (timerType: "POMODORO" | "SHORT_BREAK" | "LONG_BREAK") => {
    switch (timerType) {
      case "POMODORO":
        timeBetween = Number(pomodoro);
        break;
      case "SHORT_BREAK":
        timeBetween = Number(shortBreak);
        break;
      case "LONG_BREAK":
        timeBetween = Number(longBreak);
        break;
    }

    if (timer) {
      const { minutes, seconds } = getMinutesSeconds(timeBetween);
      timer.innerHTML = `${minutes}:${seconds}`;
    }

    console.log(`debug> initializeTimer called with timerType: ${timerType}, timeBetween: ${timeBetween}`);
  };

  const resetTimer = () => {
    switch (timerType) {
      case "POMODORO":
        timeBetween = Number(pomodoro);
        buttonState = "START";
        break;
      case "SHORT_BREAK":
        timeBetween = Number(shortBreak);
        buttonState = "START";
        break;
      case "LONG_BREAK":
        timeBetween = Number(longBreak);
        buttonState = "START";
        break;
    }
    if (timer) {
      const { minutes, seconds } = getMinutesSeconds(timeBetween);
      timer.innerHTML = `${minutes}:${seconds}`;
    }
  };

  initializeTimer(timerType);

  console.log(`debug> timeBetween: ${timeBetween}`);

  $: {
    browser.runtime.onMessage.addListener((message, sender, onResponse) => {
      console.log(`debug> received message inside Start.svelte: ${message.type}`);
      if (message.type === "RESET_TIMER") {
        timeUpSound.play();
        completedSessions = messagepletedSessions;
        resetTimer();
      } else if (message.type === "INIT_TIMER") {
        initializeTimer(message.timerType);
      } else if (message.type === "UPDATE_TIMER") {
        if (timer) {
          timer.innerText = message.time;
          timeBetween = message.time
            .split(":")
            .reduce((acc: number, time: number, index: number) => {
              if (index === 0) {
                return acc + Number(time) * 60000;
              } else {
                return acc + Number(time) * 1000;
              }
            }, 0);
        }
      }
    });
  }

  const getMinutesSeconds = (time: number) => ({
    minutes: toDoubleDigit(Math.floor((time / 60000) % 60)),
    seconds: toDoubleDigit(Math.floor((time / 1000) % 60)),
  });

  $: ({ minutes, seconds } = getMinutesSeconds(timeBetween));

  const changeButtonState = () => {
    buttonState = buttonState === "START" ? "PAUSE" : "START";
  };

  $: {
    if (shouldUpdateTimer && timer) {
      timer.innerHTML = `${minutes}:${seconds}`;
      shouldUpdateTimer = false;
    }
  }

  const handleClick = async () => {
    if (buttonState === "START") {
      await browser.runtime.sendMessage({
        type: "START_TIMER",
        time: timeBetween,
      });
    } else {
      const response = await browser.runtime.sendMessage({
        type: "PAUSE_TIMER",
        time: timeBetween,
      });
      if (timer && response) {
        const time = Number(response.time);
        if (!isNaN(time)) {
          const { minutes, seconds } = getMinutesSeconds(time);
          timer.innerText = `${minutes}:${seconds}`;
        }
      }
    }

    changeButtonState();
  };
</script>

<button id="timerButton" on:click={handleClick}> // by the way, these buttons work correctly when you click on them, just usually don't show up correctly in the extension
  {#if buttonState === "START"}
    <img src={playIcon} width="12" alt="Play" />
  {:else}
    <img src={pauseIcon} width="12" alt="Pause" />
  {/if}
</button>
<!-- TimerType.svelte -->
<script lang="ts">
    import './timertype.css';
    import './Start.svelte';

    export let timerType: "POMODORO" | "SHORT_BREAK" | "LONG_BREAK" = "POMODORO";
    export let buttonState: "START" | "PAUSE" = "START";
    export let completedSessions = {
      completedPomodoros: 0,
      completedShortBreaks: 0,
      completedLongBreaks: 0
    };

    const handleClick = () => {
        const pomodoro = document.getElementById('pomodoro') as HTMLInputElement;
        const shortBreak = document.getElementById('shortBreak') as HTMLInputElement;
        const longBreak = document.getElementById('longBreak') as HTMLInputElement;

        if (pomodoro.checked) {
            timerType = "POMODORO";
        } else if (shortBreak.checked) {
            timerType = "SHORT_BREAK";
        } else if (longBreak.checked) {
            timerType = "LONG_BREAK";
        }

        browser.runtime.sendMessage({ type: "INIT_TIMER", timerType });

        console.log(`debug> completedSessions: ${JSON.stringify(completedSessions)}`);
        console.log(`debug> timer changed to: ${timerType}`);
    }
</script>

<div class="timer-type" data-is="multiswitch">
    <form>
        <label>
            <input type="radio" id="pomodoro" data-id="1" name="timerType" checked on:click={handleClick} disabled={buttonState === "PAUSE"}>
            <span>Pomodoro {#if completedSessionspletedPomodoros > 0}<strong style="font-family: 'Manrope'; line-height: 1;">({completedSessionspletedPomodoros})</strong>{/if}</span>
        </label>
        <label>
            <input type="radio" id="shortBreak" data-id="2" name="timerType" on:click={handleClick} disabled={buttonState === "PAUSE"}>
            <span>Short Break {#if completedSessionspletedShortBreaks > 0}<strong style="font-family: 'Manrope'; line-height: 1;">({completedSessionspletedShortBreaks})</strong>{/if}</span>
        </label>
        <label>
            <input type="radio" id="longBreak" name="timerType" data-id="3" on:click={handleClick} disabled={buttonState === "PAUSE"}>
            <span>Long Break {#if completedSessionspletedLongBreaks > 0}<strong style="font-family: 'Manrope'; line-height: 1;">({completedSessionspletedLongBreaks})</strong>{/if}</span>
        </label>
        <div id="indicator"></div>
    </form>
</div>
// background.ts
import { countdown } from "@/utils/countdown";
import toDoubleDigit from "@/utils/toDoubleDigit";

export let timerType: "POMODORO" | "SHORT_BREAK" | "LONG_BREAK" = "POMODORO";
export let completedSessions = {
  completedPomodoros: 0,
  completedShortBreaks: 0,
  completedLongBreaks: 0,
};

let interval: NodeJS.Timeout | null = null;
let timeBetween: number;

export default defineBackground(() => {
  console.log("info> started StudyMate", { id: browser.runtime.id });

  const getMinutesSeconds = (time: number) => {
    const minutes = toDoubleDigit(Math.floor(time / 60000) % 60);
    const seconds = toDoubleDigit(Math.floor(time / 1000) % 60);
    return { minutes, seconds };
  };

  const updateTimer = (time: number) => {
    let { minutes, seconds } = getMinutesSeconds(time);
    return `${minutes}:${seconds}`;
  };

  const playTimer = (time: number) => {
    interval = countdown(
      time,
      (remainingTime) => {
        timeBetween = remainingTime;
        browser.runtime.sendMessage({
          type: "UPDATE_TIMER",
          time: updateTimer(timeBetween),
        });
      },
      () => {
        if (timerType === "POMODORO") {
          completedSessionspletedPomodoros += 1;
        } else if (timerType === "SHORT_BREAK") {
          completedSessionspletedShortBreaks += 1;
        } else if (timerType === "LONG_BREAK") {
          completedSessionspletedLongBreaks += 1;
        }

        browser.runtime.sendMessage({ type: "RESET_TIMER" });
      }
    );
  };

  const pauseTimer = () => {
    if (interval !== null) clearInterval(interval);
  };

  browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.type === "GET_STATE") {
      sendResponse({ timerType, completedSessions });
    } else if (message.type === "START_TIMER") {
      playTimer(message.time);
      sendResponse({ status: "timerStarted", time: updateTimer(message.time) });
    } else if (message.type === "PAUSE_TIMER") {
      pauseTimer();
      console.log(
        `debug> background.ts sent response w/ ${updateTimer(message.time)}`
      );
      sendResponse({ status: "timerPaused", time: updateTimer(message.time) });
    } else if (message.type === "INIT_TIMER") {
      browser.runtime.sendMessage({ type: "INIT_TIMER", timerType: message.timerType });
    }

    console.log(
      `debug> received message inside background.ts: ${
        message.type
      } with additional data: ${JSON.stringify(message)}`
    );
    return true;
  });
});

If you need more code to reproduce, it's on GitHub: .

Also to reproduce, a quick example:

发布评论

评论列表(0)

  1. 暂无评论