From cffbfa7de46d5db60aad90404cc8960e831f2a14 Mon Sep 17 00:00:00 2001 From: Zeu Capua Date: Fri, 10 Apr 2026 02:39:19 -0700 Subject: [PATCH] update stores with PersistedState, useInterval timers --- bun.lock | 12 +++++ package.json | 2 + src/lib/TaskItem.svelte | 53 +++++++++++++++++++++ src/lib/stores.svelte.ts | 48 ++++--------------- src/routes/+layout.svelte | 17 +++---- src/routes/[id]/+page.svelte | 90 ++++++------------------------------ svelte.config.js | 10 +--- 7 files changed, 101 insertions(+), 131 deletions(-) create mode 100644 src/lib/TaskItem.svelte diff --git a/bun.lock b/bun.lock index 36cc900..e2eb613 100644 --- a/bun.lock +++ b/bun.lock @@ -7,10 +7,12 @@ "dependencies": { "@tailwindcss/vite": "^4.1.13", "oslo": "^1.2.1", + "runed": "^0.37.1", "svelte-french-toast": "^1.2.0", "tailwindcss": "^4.1.13", }, "devDependencies": { + "@sveltejs/adapter-netlify": "^6.0.4", "@sveltejs/kit": "^2.43.5", "@sveltejs/vite-plugin-svelte": "^6.2.1", "svelte": "^5.39.6", @@ -78,6 +80,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], + "@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="], + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], @@ -200,6 +204,8 @@ "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="], + "@sveltejs/adapter-netlify": ["@sveltejs/adapter-netlify@6.0.4", "", { "dependencies": { "@iarna/toml": "^2.2.5", "esbuild": "^0.25.4" }, "peerDependencies": { "@sveltejs/kit": "^2.31.0" } }, "sha512-Ln/vgZc1v/2JFXhl5QiVKJ61a75jKe2rdxMnDO8PPDpQN8Tq/0w4JnBGAMBqENtD+kDACyY00ZTyzP9uTP77wg=="], + "@sveltejs/kit": ["@sveltejs/kit@2.43.5", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-44Mm5csR4mesKx2Eyhtk8UVrLJ4c04BT2wMTfYGKJMOkUqpHP5KLL2DPV0hXUA4t4+T3ZYe0aBygd42lVYv2cA=="], "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], @@ -260,6 +266,8 @@ "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "detect-libc": ["detect-libc@2.1.1", "", {}, "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="], "devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="], @@ -310,6 +318,8 @@ "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], "memfs": ["memfs@3.5.3", "", { "dependencies": { "fs-monkey": "^1.0.4" } }, "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw=="], @@ -340,6 +350,8 @@ "rollup": ["rollup@4.52.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.3", "@rollup/rollup-android-arm64": "4.52.3", "@rollup/rollup-darwin-arm64": "4.52.3", "@rollup/rollup-darwin-x64": "4.52.3", "@rollup/rollup-freebsd-arm64": "4.52.3", "@rollup/rollup-freebsd-x64": "4.52.3", "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", "@rollup/rollup-linux-arm-musleabihf": "4.52.3", "@rollup/rollup-linux-arm64-gnu": "4.52.3", "@rollup/rollup-linux-arm64-musl": "4.52.3", "@rollup/rollup-linux-loong64-gnu": "4.52.3", "@rollup/rollup-linux-ppc64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-musl": "4.52.3", "@rollup/rollup-linux-s390x-gnu": "4.52.3", "@rollup/rollup-linux-x64-gnu": "4.52.3", "@rollup/rollup-linux-x64-musl": "4.52.3", "@rollup/rollup-openharmony-arm64": "4.52.3", "@rollup/rollup-win32-arm64-msvc": "4.52.3", "@rollup/rollup-win32-ia32-msvc": "4.52.3", "@rollup/rollup-win32-x64-gnu": "4.52.3", "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A=="], + "runed": ["runed@0.37.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0", "zod": "^4.1.0" }, "optionalPeers": ["@sveltejs/kit", "zod"] }, "sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA=="], + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], diff --git a/package.json b/package.json index fe9f93f..46b56e3 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" }, "devDependencies": { + "@sveltejs/adapter-netlify": "^6.0.4", "@sveltejs/kit": "^2.43.5", "@sveltejs/vite-plugin-svelte": "^6.2.1", "svelte": "^5.39.6", @@ -22,6 +23,7 @@ "dependencies": { "@tailwindcss/vite": "^4.1.13", "oslo": "^1.2.1", + "runed": "^0.37.1", "svelte-french-toast": "^1.2.0", "tailwindcss": "^4.1.13" } diff --git a/src/lib/TaskItem.svelte b/src/lib/TaskItem.svelte new file mode 100644 index 0000000..59dd83f --- /dev/null +++ b/src/lib/TaskItem.svelte @@ -0,0 +1,53 @@ + + +
  • +
    + + +
    + +
    + + +
    +
  • diff --git a/src/lib/stores.svelte.ts b/src/lib/stores.svelte.ts index 32c3a8c..d634e01 100644 --- a/src/lib/stores.svelte.ts +++ b/src/lib/stores.svelte.ts @@ -1,43 +1,11 @@ import { generateId } from "./utils"; - -// Browser + Local Storage -const browser_exists = (typeof window !== "undefined") && (typeof (document) !== "undefined"); -const storage = browser_exists ? localStorage : null; - -// Generalized Local Storage -export function persisted(key: string, default_value: T) { - let value : T | undefined = $state(); - - const initial_local = storage?.getItem(key); - if (initial_local) { - value = JSON.parse(initial_local).value as T; - if (!value) { update(); } - } - else { - value = default_value; - update(); - } - - function update() { - if (browser_exists) { - storage?.setItem(key, JSON.stringify({ value: value })); - } - } - - return { - get value() { return value; }, - set value(new_value) { value = new_value; update(); }, - update - } -} +import { PersistedState } from "runed"; export type Task = { id: string; description: string; - is_completed: boolean; - // optional - duration?: number; - stopwatchInterval?: number; + completed: boolean; + duration: number; } export type List = { @@ -46,15 +14,15 @@ export type List = { tasks: Task[]; } -export const local_lists = persisted("local_lists", [ +export const local_lists = new PersistedState("local_lists", [ { id: generateId(), title: "Take a Break", tasks: [ - { id: generateId(), description: "Drink water", is_completed: false }, - { id: generateId(), description: "Stand up and stretch", is_completed: false }, - { id: generateId(), description: "Go outside for 10 seconds", is_completed: false }, ] + { id: generateId(), description: "Drink water", completed: false, duration: 0 }, + { id: generateId(), description: "Stand up and stretch", completed: false, duration: 0 }, + { id: generateId(), description: "Go outside for 10 seconds", completed: false, duration: 0 }, ] } ]); -export const pinned_list = persisted("pinned_list", local_lists.value![0].id); +export const pinned_list = new PersistedState("pinned_list", local_lists.current[0].id); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 5d89a1c..b0917c0 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -5,7 +5,8 @@ import { goto } from "$app/navigation"; import { fade } from "svelte/transition"; import toast, { Toaster } from "svelte-french-toast"; - import { persisted, pinned_list } from "$lib/stores.svelte"; + import { pinned_list } from "$lib/stores.svelte"; + import { PersistedState } from "runed"; interface Props { children: Snippet @@ -13,9 +14,9 @@ let { children }: Props = $props(); - let theme = persisted("theme", "dark"); + let theme = new PersistedState("theme", "dark"); let is_menu_open = $state(false); - let theme_style = $derived(theme.value === "dark" + let theme_style = $derived(theme.current === "dark" ? "text-white absolute top-0 z-[-2] h-screen w-screen bg-[#000000] bg-[radial-gradient(#ffffff33_1px,#00091d_1px)] bg-size-[20px_20px]" : "text-black absolute inset-0 -z-10 h-full w-full bg-white bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] bg-size-[16px_16px]" ); @@ -26,7 +27,7 @@ onMount(() => { if (page.url.pathname === "/") { - goto(`/${pinned_list.value}`); + goto(`/${pinned_list.current}`); } }); @@ -41,7 +42,7 @@ {#if is_menu_open} {/if} -