diff --git a/src/store/index.ts b/src/store/index.ts index 8e5b094..addf1e7 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,9 +1,9 @@ -// ... (imports) -import { createStore } from "solid-js/store"; -import { createSignal } from "solid-js"; +import { createStore, reconcile } from "solid-js/store"; +import { createSignal, createEffect, createRoot } from "solid-js"; import { pb, TASGRID_COLLECTION } from "@/lib/pocketbase"; import { toast } from "solid-sonner"; +const STORAGE_KEY = "tasgrid_data_v1"; export const [now, setNow] = createSignal(Date.now()); export const [activeTaskId, setActiveTaskId] = createSignal(null); @@ -30,12 +30,31 @@ interface TaskStore { prefId?: string; // ID of the user_preferences record } -// Initial state -export const [store, setStore] = createStore({ - tasks: [], - pWeight: 1.0, - uWeight: 1.0, - matrixScaleDays: 30, // Default requested by user +// Synchronous hydration from localStorage for "instant" first paint +const getInitialState = (): TaskStore => { + const saved = localStorage.getItem(STORAGE_KEY); + if (saved) { + try { + return JSON.parse(saved); + } catch (e) { + console.error("Failed to parse cached data", e); + } + } + return { + tasks: [], + pWeight: 1.0, + uWeight: 1.0, + matrixScaleDays: 30, + }; +}; + +export const [store, setStore] = createStore(getInitialState()); + +// Auto-persist changes to localStorage (wrapped in root to avoid console warning) +createRoot(() => { + createEffect(() => { + localStorage.setItem(STORAGE_KEY, JSON.stringify(store)); + }); }); // -- Calibration -- @@ -126,11 +145,17 @@ export const initStore = async () => { deletedAt: r.deletedAt ? new Date(r.deletedAt).getTime() : undefined })); - setStore("tasks", loadedTasks); + // Use reconcile for efficient store update (keeps observers happy) + setStore("tasks", reconcile(loadedTasks)); } catch (err) { - console.error("Failed to load data:", err); - toast.error("Failed to sync with server."); + // If we fail but have cached data, don't toast an error unless it's a hard failure + if (store.tasks.length === 0) { + console.error("Failed to load data:", err); + toast.error("Failed to sync with server."); + } else { + console.warn("Sync failed, using cached data", err); + } } }; diff --git a/vite.config.ts b/vite.config.ts index 65735c0..39aa456 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -20,12 +20,33 @@ export default defineConfig({ { src: 'pwa-192x192.png', sizes: '192x192', - type: 'image/png' + type: 'image/png', + purpose: 'any maskable' }, { src: 'pwa-512x512.png', sizes: '512x512', - type: 'image/png' + type: 'image/png', + purpose: 'any maskable' + } + ] + }, + workbox: { + globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'], + runtimeCaching: [ + { + urlPattern: /^https:\/\/pocketbase\.ccllc\.pro\/api\/.*/i, + handler: 'NetworkFirst', + options: { + cacheName: 'api-cache', + expiration: { + maxEntries: 100, + maxAgeSeconds: 60 * 60 * 24 * 7 // 1 week + }, + cacheableResponse: { + statuses: [0, 200] + } + } } ] }