Improved initial loading. Uses on device cach first
This commit is contained in:
+37
-12
@@ -1,9 +1,9 @@
|
|||||||
// ... (imports)
|
import { createStore, reconcile } from "solid-js/store";
|
||||||
import { createStore } from "solid-js/store";
|
import { createSignal, createEffect, createRoot } from "solid-js";
|
||||||
import { createSignal } from "solid-js";
|
|
||||||
import { pb, TASGRID_COLLECTION } from "@/lib/pocketbase";
|
import { pb, TASGRID_COLLECTION } from "@/lib/pocketbase";
|
||||||
import { toast } from "solid-sonner";
|
import { toast } from "solid-sonner";
|
||||||
|
|
||||||
|
const STORAGE_KEY = "tasgrid_data_v1";
|
||||||
|
|
||||||
export const [now, setNow] = createSignal(Date.now());
|
export const [now, setNow] = createSignal(Date.now());
|
||||||
export const [activeTaskId, setActiveTaskId] = createSignal<string | null>(null);
|
export const [activeTaskId, setActiveTaskId] = createSignal<string | null>(null);
|
||||||
@@ -30,12 +30,31 @@ interface TaskStore {
|
|||||||
prefId?: string; // ID of the user_preferences record
|
prefId?: string; // ID of the user_preferences record
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial state
|
// Synchronous hydration from localStorage for "instant" first paint
|
||||||
export const [store, setStore] = createStore<TaskStore>({
|
const getInitialState = (): TaskStore => {
|
||||||
tasks: [],
|
const saved = localStorage.getItem(STORAGE_KEY);
|
||||||
pWeight: 1.0,
|
if (saved) {
|
||||||
uWeight: 1.0,
|
try {
|
||||||
matrixScaleDays: 30, // Default requested by user
|
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<TaskStore>(getInitialState());
|
||||||
|
|
||||||
|
// Auto-persist changes to localStorage (wrapped in root to avoid console warning)
|
||||||
|
createRoot(() => {
|
||||||
|
createEffect(() => {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(store));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// -- Calibration --
|
// -- Calibration --
|
||||||
@@ -126,11 +145,17 @@ export const initStore = async () => {
|
|||||||
deletedAt: r.deletedAt ? new Date(r.deletedAt).getTime() : undefined
|
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) {
|
} catch (err) {
|
||||||
console.error("Failed to load data:", err);
|
// If we fail but have cached data, don't toast an error unless it's a hard failure
|
||||||
toast.error("Failed to sync with server.");
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+23
-2
@@ -20,12 +20,33 @@ export default defineConfig({
|
|||||||
{
|
{
|
||||||
src: 'pwa-192x192.png',
|
src: 'pwa-192x192.png',
|
||||||
sizes: '192x192',
|
sizes: '192x192',
|
||||||
type: 'image/png'
|
type: 'image/png',
|
||||||
|
purpose: 'any maskable'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: 'pwa-512x512.png',
|
src: 'pwa-512x512.png',
|
||||||
sizes: '512x512',
|
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]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user