Local & Cloud Data
Architectural Overview
Section titled “Architectural Overview”| Feature | Cloud Studysets | Local Studysets |
|---|---|---|
| Storage | Remote Database (PostgreSQL) | Browser’s IndexedDB |
| Access Method | GraphQL API | idb-api-layer (Dexie.js wrapper) |
| Authentication | Required for creation/editing | No account needed |
| IDs | Strings (UUIDs/HashIDs) | Integers (Auto-incrementing) |
| Server-Side Rendering | Supported | Not Supported (Browser-only API) |
Loading Implementation
Section titled “Loading Implementation”Cloud Studysets (Initial Render)
Section titled “Cloud Studysets (Initial Render)”Cloud data is typically fetched on the server in +page.server.ts load functions. This allows the page to be pre-rendered (SSR), making the data available immediately upon the first render.
Example: src/routes/studysets/[id]/+page.server.ts
export async function load({ params, locals }) { const data = await locals.sdk.PublicStudyset({ id: params.id }); return { studyset: data.studyset };}Local Studysets (Post-Mount Loading)
Section titled “Local Studysets (Post-Mount Loading)”IndexedDB is a browser API and is not available during server-side rendering. Therefore, local data cannot be loaded in +page.server.ts.
Instead, the server load function only passes the ID from the URL, and the actual data is fetched on the client side within onMount. This results in a “loading state” or a slight delay where the data is populated after the initial page shell has loaded.
Example: src/routes/studyset/local/+page.server.ts
export async function load({ url, locals }) { let localId = parseInt(url.searchParams.get("id") ?? ""); return { localId: localId }; // Server only knows the ID}Example: src/routes/StudysetPage.svelte
let title = $state(data?.studyset?.title); // Initialized immediately for Cloudlet terms = $state(data?.studyset?.terms);
onMount(function () { if (data.local) { (async () => { // Fetched after page load for Local const localStudyset = await idbApiLayer.getStudysetById(data.localId); title = localStudyset?.title; terms = localStudyset?.terms ?? []; })(); }});Image Handling & Memory Management
Section titled “Image Handling & Memory Management”Images are handled fundamentally differently between the two storage types.
Cloud Images
Section titled “Cloud Images”Cloud images are served via standard URLs from the backend (e.g., https://usercontent.static.quizfreely.org/api/term-images/[id]/[side]). The browser handles caching and memory management for these URLs normally.
Local Images (Object URLs)
Section titled “Local Images (Object URLs)”Local images are stored as Blob objects in IndexedDB. To display them in an <img> tag, we must generate an Object URL.
- Generation: We use
URL.createObjectURL(blob)(wrapped inidbLayerImg.getImageObjectUrl). - Cleanup: Unlike standard URLs, Object URLs live in memory until the document is closed or they are manually revoked. To prevent memory leaks, you must call
URL.revokeObjectURL(url)when the component is destroyed.
Reference Implementation in src/routes/StudysetPage.svelte:
onMount(() => { let objectUrls: string[] = []; if (data.local) { (async () => { // ... fetch studyset ... terms.forEach((term: any) => { if (term.termImageUrl) objectUrls.push(term.termImageUrl); if (term.defImageUrl) objectUrls.push(term.defImageUrl); }); })(); }
return () => { // CLEANUP: Revoke all generated Object URLs objectUrls.forEach(objectUrl => URL.revokeObjectURL(objectUrl)); };});Saving & Updating
Section titled “Saving & Updating”The project uses a “dual-path” approach in shared components like Edit.svelte.
- Cloud: Uses the GraphQL SDK (e.g.,
sdk.UpdateStudysetAndTerms). - Local: Uses
idbApiLayer(e.g.,idbApiLayer.updateStudyset).
Reference: src/lib/components/Edit.svelte
function saveButton() { if (data.local) { updateLocalStudyset(); // Uses idbApiLayer } else { updateCloudStudyset(); // Uses GraphQL SDK }}