Skip to content

Commit

Permalink
Merge pull request #28 from Saba-Sabato/chrome-backed-stores
Browse files Browse the repository at this point in the history
Chrome backed stores
  • Loading branch information
NekitCorp authored Sep 17, 2024
2 parents 9bb13b5 + 52dbb66 commit bd3c79d
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 73 deletions.
5 changes: 3 additions & 2 deletions src/background/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
39 changes: 9 additions & 30 deletions src/components/Options.svelte
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
<script lang="ts">
import { storage } from "../storage";
import { onMount } from "svelte";
import { type Writable } from "svelte/store";
export let count: number;
let successMessage: string | null = null;
export let count: Writable<number>;
function increment() {
count += 1;
}
function decrement() {
count -= 1;
}
function save() {
storage.set({ count }).then(() => {
successMessage = "Options saved!";
setTimeout(() => {
successMessage = null;
}, 1500);
});
}
onMount(() => {
console.log(`Options onMount count=${$count}`);
});
</script>

<div class="container">
<p>Current count: <b>{count}</b></p>
<p>Current count: <b>{$count}</b></p>
<div>
<button on:click={decrement}>-</button>
<button on:click={increment}>+</button>
<button on:click={save}>Save</button>
{#if successMessage}<span class="success">{successMessage}</span>{/if}
<button on:click={() => ($count -= 1)}>-</button>
<button on:click={() => ($count += 1)}>+</button>
</div>
</div>

Expand All @@ -52,9 +36,4 @@
button:focus {
background-color: #27ae60;
}
.success {
color: #2ecc71;
font-weight: bold;
}
</style>
9 changes: 1 addition & 8 deletions src/components/Overlay.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
<script lang="ts">
import { onMount } from "svelte";
import { storage } from "../storage";
import { count } from "../storage";
import Options from "./Options.svelte";
let count = 0;
onMount(() => {
storage.get().then((storage) => (count = storage.count));
});
</script>

<div class="overlay">
Expand Down
5 changes: 3 additions & 2 deletions src/content/index.ts
Original file line number Diff line number Diff line change
@@ -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/
Expand All @@ -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 });
10 changes: 4 additions & 6 deletions src/options/index.ts
Original file line number Diff line number Diff line change
@@ -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/
Expand All @@ -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 },
});
}
}
Expand Down
10 changes: 4 additions & 6 deletions src/popup/index.ts
Original file line number Diff line number Diff line change
@@ -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/
Expand All @@ -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 },
});
}
}
Expand Down
10 changes: 4 additions & 6 deletions src/sidepanel/index.ts
Original file line number Diff line number Diff line change
@@ -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/
Expand All @@ -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 },
});
}
}
Expand Down
71 changes: 58 additions & 13 deletions src/storage.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,58 @@
type IStorage = {
count: number;
};

const defaultStorage: IStorage = {
count: 0,
};

export const storage = {
get: (): Promise<IStorage> =>
chrome.storage.sync.get(defaultStorage) as Promise<IStorage>,
set: (value: IStorage): Promise<void> => 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<T>(key: string, initialValue: T): Writable<T> {
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);

0 comments on commit bd3c79d

Please sign in to comment.