diff --git a/src/background/index.ts b/src/background/index.ts index f6e8416..41a5349 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -1,10 +1,11 @@ -import { storage } from "../storage"; +import { get } from "svelte/store"; +import { count } from "../storage"; // Background service workers // https://developer.chrome.com/docs/extensions/mv3/service_workers/ chrome.runtime.onInstalled.addListener(() => { - storage.get().then(console.log); + console.log(get(count)); }); // NOTE: If you want to toggle the side panel from the extension's action button, diff --git a/src/components/Options.svelte b/src/components/Options.svelte index de2b5d6..47e2c17 100644 --- a/src/components/Options.svelte +++ b/src/components/Options.svelte @@ -1,35 +1,19 @@
-

Current count: {count}

+

Current count: {$count}

- - - - {#if successMessage}{successMessage}{/if} + +
@@ -52,9 +36,4 @@ button:focus { background-color: #27ae60; } - - .success { - color: #2ecc71; - font-weight: bold; - } diff --git a/src/components/Overlay.svelte b/src/components/Overlay.svelte index 3b0f287..b2dbbb8 100644 --- a/src/components/Overlay.svelte +++ b/src/components/Overlay.svelte @@ -1,13 +1,6 @@
diff --git a/src/content/index.ts b/src/content/index.ts index b3e194d..5dfc43d 100644 --- a/src/content/index.ts +++ b/src/content/index.ts @@ -1,5 +1,6 @@ +import { get } from "svelte/store"; import Overlay from "../components/Overlay.svelte"; -import { storage } from "../storage"; +import { count } from "../storage"; // Content scripts // https://developer.chrome.com/docs/extensions/mv3/content_scripts/ @@ -8,7 +9,7 @@ import { storage } from "../storage"; import "./styles.css"; // Some JS on the page -storage.get().then(console.log); +console.log(`CONTENT: ${get(count)}`); // Some svelte component on the page new Overlay({ target: document.body }); diff --git a/src/options/index.ts b/src/options/index.ts index 2d2a7c1..de5fb02 100644 --- a/src/options/index.ts +++ b/src/options/index.ts @@ -1,5 +1,5 @@ import Options from "../components/Options.svelte"; -import { storage } from "../storage"; +import { count } from "../storage"; // Options // https://developer.chrome.com/docs/extensions/mv3/options/ @@ -8,11 +8,9 @@ function render() { const target = document.getElementById("app"); if (target) { - storage.get().then(({ count }) => { - new Options({ - target, - props: { count }, - }); + new Options({ + target, + props: { count }, }); } } diff --git a/src/popup/index.ts b/src/popup/index.ts index 4a00ba0..d9d125a 100644 --- a/src/popup/index.ts +++ b/src/popup/index.ts @@ -1,5 +1,5 @@ import Options from "../components/Options.svelte"; -import { storage } from "../storage"; +import { count } from "../storage"; // Action popup // https://developer.chrome.com/docs/extensions/reference/action/ @@ -8,11 +8,9 @@ function render() { const target = document.getElementById("app"); if (target) { - storage.get().then(({ count }) => { - new Options({ - target, - props: { count }, - }); + new Options({ + target, + props: { count }, }); } } diff --git a/src/sidepanel/index.ts b/src/sidepanel/index.ts index 1fffe4e..f8df213 100644 --- a/src/sidepanel/index.ts +++ b/src/sidepanel/index.ts @@ -1,5 +1,5 @@ import Options from "../components/Options.svelte"; -import { storage } from "../storage"; +import { count } from "../storage"; // Side panel // https://developer.chrome.com/docs/extensions/reference/sidePanel/ @@ -8,11 +8,9 @@ function render() { const target = document.getElementById("app"); if (target) { - storage.get().then(({ count }) => { - new Options({ - target, - props: { count }, - }); + new Options({ + target, + props: { count }, }); } } diff --git a/src/storage.ts b/src/storage.ts index 015c59d..5265f4c 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1,13 +1,58 @@ -type IStorage = { - count: number; -}; - -const defaultStorage: IStorage = { - count: 0, -}; - -export const storage = { - get: (): Promise => - chrome.storage.sync.get(defaultStorage) as Promise, - set: (value: IStorage): Promise => chrome.storage.sync.set(value), -}; +import { writable, type Writable } from 'svelte/store'; + +/** + * Creates a persistent Svelte store backed by Chrome's sync storage. + * @template T The type of the store's value + * @param key The key to use in Chrome's storage + * @param initialValue The initial value of the store + * @returns A writable Svelte store + */ +export function persistentStore(key: string, initialValue: T): Writable { + const store = writable(initialValue); + // Ensure each value is updated exactly once in store and in chrome storage + let storeValueQueue: T[] = []; + let chromeValueQueue: T[] = []; + + function watchStore() { + store.subscribe((value) => { + if (chromeValueQueue.length > 0 && value === chromeValueQueue[0]) { + chromeValueQueue.shift(); + return; + } + + storeValueQueue.push(value); + chrome.storage.sync.set({ [key]: value }); + }); + } + + function watchChrome() { + chrome.storage.sync.onChanged.addListener((changes) => { + if (!(Object.hasOwn(changes, key))) return; + + const value = changes[key].newValue as T; + if (storeValueQueue.length > 0 && value === storeValueQueue[0]) { + storeValueQueue.shift(); + return; + } + + chromeValueQueue.push(value); + store.set(value); + }); + } + + // Initialize the store with the value from Chrome storage + chrome.storage.sync.get(key).then((result) => { + let value = Object.hasOwn(result, key) ? result[key] : initialValue; + if (!Object.hasOwn(result, key)) { + console.log(`Persistent store: couldn't find key [${key}] in chrome storage. Default to initial value [${initialValue}]`) + } + chromeValueQueue.push(value); + store.set(value); + watchStore(); + watchChrome(); + }); + + return store; +} + +export const count = persistentStore("count", 10); \ No newline at end of file