diff --git a/apps/mana/apps/web/src/lib/data/current-user.ts b/apps/mana/apps/web/src/lib/data/current-user.ts index e018d1249..796a54436 100644 --- a/apps/mana/apps/web/src/lib/data/current-user.ts +++ b/apps/mana/apps/web/src/lib/data/current-user.ts @@ -2,38 +2,69 @@ * Single source of truth for the current authenticated user id. * * Why a separate module? - * The data layer (database.ts hooks) needs to know who is writing each - * record so it can stamp `userId` automatically. Importing the auth store - * directly would couple the data layer to UI state and create a circular - * dependency. Instead, the root layout pushes the current user id here on - * every auth state change. + * The data layer (database.ts hooks) needs to know who is writing + * each record so it can stamp `userId` automatically. Importing the + * auth store directly would couple the data layer to UI state and + * create a circular dependency. Instead, the root layout pushes the + * current user id here on every auth state change. + * + * Reactive consumers: + * This module stays a plain `.ts` (no runes) so it remains a leaf in + * the dependency graph and works inside the test runner without the + * Svelte preprocessor. To still notify reactive consumers of changes, + * we expose `onCurrentUserChanged(handler)` — same pattern as + * `onActiveSpaceChanged` in scope/active-space.svelte.ts. The + * `useScopedLiveQuery` hook subscribes to both buses and re-runs + * liveQueries whenever either side flips. * * Guest mode: when no user is signed in, records are stamped with the - * `GUEST_USER_ID` sentinel. The mana-sync backend treats these as anonymous - * and rejects them at the RLS layer once auth is required. + * `GUEST_USER_ID` sentinel. The mana-sync backend treats these as + * anonymous and rejects them at the RLS layer once auth is required. */ export const GUEST_USER_ID = 'guest'; let currentUserId: string | null = null; +type CurrentUserChangedHandler = (userId: string | null) => void; +const handlers: CurrentUserChangedHandler[] = []; + /** - * Updates the active user. Pass `null` for sign-out / guest. - * - * After updating the in-memory value, bumps the Dexie `_scopeCursor` - * (lazy import to keep this module leaf-level) so every liveQuery - * subscribed via `touchScopeCursor` re-evaluates with the new - * `getInScopeSpaceIds()`. Fire-and-forget — a missing bump only - * delays re-evaluation until the next Dexie write elsewhere. + * Subscribe to user-id changes. Returns an unsubscribe function. The + * handler is replayed once with the current value so consumers don't + * need a separate "read initial" call. + */ +export function onCurrentUserChanged(handler: CurrentUserChangedHandler): () => void { + handlers.push(handler); + try { + handler(currentUserId); + } catch (err) { + console.error('[current-user] handler replay failed:', err); + } + return () => { + const i = handlers.indexOf(handler); + if (i >= 0) handlers.splice(i, 1); + }; +} + +function notifyHandlers(id: string | null): void { + for (const h of handlers) { + try { + h(id); + } catch (err) { + console.error('[current-user] handler failed:', err); + } + } +} + +/** + * Updates the active user. Pass `null` for sign-out / guest. Notifies + * every `onCurrentUserChanged` subscriber when the value flips. */ export function setCurrentUserId(id: string | null): void { const prev = currentUserId; currentUserId = id; - if (id !== prev) { - void import('./scope/cursor').then(({ bumpScopeCursor }) => { - bumpScopeCursor(); - }); - } + if (id !== prev) notifyHandlers(id); } /** Returns the active user id, or `null` if unauthenticated. */ @@ -42,8 +73,9 @@ export function getCurrentUserId(): string | null { } /** - * Returns the user id to stamp on local records: real user when signed in, - * `GUEST_USER_ID` otherwise. Always non-null so it can be used as a key. + * Returns the user id to stamp on local records: real user when signed + * in, `GUEST_USER_ID` otherwise. Always non-null so it can be used as + * a key. */ export function getEffectiveUserId(): string { return currentUserId ?? GUEST_USER_ID; diff --git a/apps/mana/apps/web/src/lib/data/database.ts b/apps/mana/apps/web/src/lib/data/database.ts index a5c267105..ff8b6259b 100644 --- a/apps/mana/apps/web/src/lib/data/database.ts +++ b/apps/mana/apps/web/src/lib/data/database.ts @@ -1052,16 +1052,32 @@ db.version(44).stores({ comicStories: 'id, createdAt, style, isFavorite, isArchived', }); -// v45 — Infra table `_scopeCursor` (see data/scope/cursor.ts for the -// full rationale). Single-row beacon that every scoped query touches -// so Dexie's liveQuery subscribes to it; bumped on every -// setActiveSpace. Without this, scope changes were invisible to -// liveQueries and modules rendered empty on first mount until an -// unrelated write re-triggered the querier. NOT in SYNC_APP_MAP — -// it's a client-side liveness signal, not user data. +// v45 → v46: scope-cursor bridge superseded by `useScopedLiveQuery`. +// +// v45 introduced a `_scopeCursor` infra table as a Dexie-side beacon +// for active-space / current-user changes — every scoped query touched +// it so Dexie's liveQuery subscribed to it, every setActiveSpace +// bumped it. Worked, but smelled: hidden side-effect inside scopedTable, +// scope state pretending to be a Dexie row, +1 roundtrip per query. +// +// The clean replacement: `data/scope/use-scoped-live-query.svelte.ts` +// wraps liveQuery in a Svelte 5 `$effect` that reads the scope state +// directly. Runes auto-track those reads, so a scope change tears +// down the previous Dexie subscription and creates a fresh one — no +// bridge table needed. Both `active-space.svelte.ts#active` and +// `current-user.svelte.ts#currentUserId` are `$state` so the tracking +// just works. +// +// v45 stays declared so existing Dexie databases keep their version +// chain intact; v46 drops the table by passing `null` (Dexie idiom +// for "delete this store"). Safe because `_scopeCursor` only ever +// held a transient `{id, bumpedAt}` row — no user data lost. db.version(45).stores({ _scopeCursor: 'id', }); +db.version(46).stores({ + _scopeCursor: null, +}); // ─── Sync Routing ────────────────────────────────────────── // SYNC_APP_MAP, TABLE_TO_SYNC_NAME, TABLE_TO_APP, SYNC_NAME_TO_TABLE, diff --git a/apps/mana/apps/web/src/lib/data/scope/active-space.svelte.ts b/apps/mana/apps/web/src/lib/data/scope/active-space.svelte.ts index 2448fa9df..ee14c03a2 100644 --- a/apps/mana/apps/web/src/lib/data/scope/active-space.svelte.ts +++ b/apps/mana/apps/web/src/lib/data/scope/active-space.svelte.ts @@ -12,7 +12,6 @@ import type { SpaceType, SpaceTier } from '@mana/shared-types'; import { isSpaceType, isSpaceTier } from '@mana/shared-types'; import { authFetch } from './auth-fetch'; -import { bumpScopeCursor } from './cursor'; export interface ActiveSpace { id: string; @@ -92,14 +91,7 @@ export function setActiveSpace(space: ActiveSpace | null): void { active = space; status = space ? 'ready' : 'idle'; lastError = null; - if (space?.id !== prevId) { - notifyHandlers(space); - // Dexie-bridge: bump the _scopeCursor so every liveQuery that - // touchScopeCursor'd re-runs with the new getInScopeSpaceIds(). - // Without this, modules mounted before the bootstrap resolved - // the active space sit on an empty first result forever. - bumpScopeCursor(); - } + if (space?.id !== prevId) notifyHandlers(space); } /** @@ -172,10 +164,7 @@ export async function loadActiveSpace(opts: { force?: boolean } = {}): Promise { - console.warn('[scope/cursor] bump failed', err); - }); -} - -/** - * Register a liveQuery-time subscription on `_scopeCursor`. - * Fire-and-forget: the Dexie read registers the subscription - * synchronously during the querier's execution, even though the - * returned Promise is not awaited. The async resolution itself is - * irrelevant — liveQuery only cares that the table was read. - * - * Called from `scopedTable` / `scopedGet` so every scoped query - * automatically subscribes without the caller needing to know. - */ -export function touchScopeCursor(): void { - void db - .table('_scopeCursor') - .get(SCOPE_CURSOR_ID) - .catch(() => undefined); -} diff --git a/apps/mana/apps/web/src/lib/data/scope/scoped-db.ts b/apps/mana/apps/web/src/lib/data/scope/scoped-db.ts index f7b0dbc4b..5ced28151 100644 --- a/apps/mana/apps/web/src/lib/data/scope/scoped-db.ts +++ b/apps/mana/apps/web/src/lib/data/scope/scoped-db.ts @@ -24,7 +24,6 @@ import { getActiveSpaceId } from './active-space.svelte'; import { personalSpaceSentinel } from './bootstrap'; import { isModuleAllowedInSpace, type SpaceModuleId, type SpaceType } from '@mana/shared-types'; import { getActiveSpace } from './active-space.svelte'; -import { touchScopeCursor } from './cursor'; export class ScopeNotReadyError extends Error { constructor() { @@ -76,11 +75,6 @@ export function getInScopeSpaceIds(): string[] { * first and the spaceId filter runs on the narrowed set. */ export function scopedTable(tableName: string): Collection { - // Register a liveQuery-time subscription on `_scopeCursor` so that - // setActiveSpace / setCurrentUserId changes trigger a re-run of this - // query. See data/scope/cursor.ts for the full rationale on the Dexie - // bridge for scope state. - touchScopeCursor(); const table = db.table(tableName) as Table; const ids = getInScopeSpaceIds(); const check = (record: unknown) => { @@ -133,7 +127,6 @@ export function scopedForModule( * compound queries with `.or()`, `.and()`, `.reverse()` first. */ export function scopedAnd(collection: Collection): Collection { - touchScopeCursor(); const ids = getInScopeSpaceIds(); return collection.and((record) => { const r = record as { spaceId?: unknown }; @@ -151,10 +144,6 @@ export function scopedAnd(collection: Collection): Collection(tableName: string, id: string | number): Promise { - // Register the liveQuery-time subscription same as scopedTable — a - // scopedGet inside a liveQuery (e.g. useGarment(id)) needs to re-run - // too when scope changes. - touchScopeCursor(); const record = (await db.table(tableName).get(id)) as T | undefined; if (!record) return undefined; const rec = record as { spaceId?: unknown }; diff --git a/apps/mana/apps/web/src/lib/data/scope/use-scoped-live-query.svelte.ts b/apps/mana/apps/web/src/lib/data/scope/use-scoped-live-query.svelte.ts index ce275914c..bfbb527fb 100644 --- a/apps/mana/apps/web/src/lib/data/scope/use-scoped-live-query.svelte.ts +++ b/apps/mana/apps/web/src/lib/data/scope/use-scoped-live-query.svelte.ts @@ -9,29 +9,46 @@ * Why this exists: * Dexie's `liveQuery` only re-runs when a Dexie table it read during * evaluation is written to. The scope state — `active` in - * `active-space.svelte.ts` and `currentUserId` in - * `current-user.svelte.ts` — lives in Svelte 5 `$state` runes, - * which Dexie cannot observe. Without a bridge, a module mounted - * before the active-space bootstrap finished would render an empty - * first result and stay empty until some unrelated Dexie write + * `active-space.svelte.ts` and `currentUserId` in `current-user.ts` — + * lives outside Dexie, so a scope change is invisible to the + * subscription. Without a bridge, a module mounted before the + * active-space bootstrap finished would render an empty first + * result and stay empty until some unrelated Dexie write * accidentally re-triggered the querier. * - * The clean fix: wrap the Dexie liveQuery subscription in an - * `$effect` that reads `getActiveSpaceId()` + `getEffectiveUserId()` - * at the top. Svelte 5 auto-tracks those reads as effect - * dependencies, and re-runs the effect — tearing down the previous - * subscription and creating a fresh one — whenever scope changes. - * Every call to the querier inside the new subscription evaluates - * `getInScopeSpaceIds()` fresh, so the visible result matches the - * active scope. No infra-tables, no hidden side-effects. + * The clean fix: wrap the Dexie `liveQuery` subscription in a + * Svelte 5 `$effect` whose dependency is a `scopeTick` `$state` + * counter. `setActiveSpace` (via `onActiveSpaceChanged`) and + * `setCurrentUserId` (via `onCurrentUserChanged`) bump the counter, + * which re-runs the effect — tearing down the previous Dexie + * subscription and creating a fresh one. Each new subscription + * evaluates the querier with the up-to-date `getInScopeSpaceIds()`, + * so the visible result matches the active scope. No infra-tables, + * no hidden side-effects in `scopedTable`. * * Same return shape as `useLiveQueryWithDefault` for drop-in * migration. */ import { liveQuery } from 'dexie'; -import { getActiveSpaceId } from './active-space.svelte'; -import { getEffectiveUserId } from '../current-user'; +import { onActiveSpaceChanged } from './active-space.svelte'; +import { onCurrentUserChanged } from '../current-user'; + +// Module-level scope-tick counter. A single counter is enough because +// every consumer just needs *some* reactive value to track — the +// liveQuery's querier itself reads the actual scope ids. +let scopeTick = $state(0); + +// Wire both event buses to bump the tick once per real change. The +// handlers replay the current value on subscribe (so the tick fires +// once at module-init), then again on every flip. Subscriptions are +// permanent — there's only one of each per app session. +onActiveSpaceChanged(() => { + scopeTick++; +}); +onCurrentUserChanged(() => { + scopeTick++; +}); export function useScopedLiveQuery( querier: () => T | Promise, @@ -42,14 +59,15 @@ export function useScopedLiveQuery( let error = $state(undefined); $effect(() => { - // Tracking reads — these are the dependencies that trigger - // re-subscription. Reading the values (even discarding them) - // is enough; Svelte 5's $effect auto-registers the dep. - void getActiveSpaceId(); - void getEffectiveUserId(); + // Tracking read — Svelte 5 auto-registers this as a dependency. + // Whenever the tick changes (active space or user changed), the + // effect re-fires: the previous teardown unsubscribes the old + // Dexie observable, and a fresh one is created below with the + // up-to-date scope. + void scopeTick; - // Reset the loading flag on every re-subscribe so consumers - // can show a transient skeleton while the new querier runs. + // Reset the loading flag on every re-subscribe so consumers can + // show a transient skeleton while the new querier runs. loading = true; const subscription = liveQuery(querier).subscribe({ @@ -65,7 +83,7 @@ export function useScopedLiveQuery( }); // Tear-down: Svelte runs this when the effect re-fires (scope - // changed) or the component unmounts. Replaces the old onDestroy + // changed) or the component unmounts. Replaces the onDestroy // pattern from `useLiveQueryWithDefault`. return () => subscription.unsubscribe(); }); diff --git a/apps/mana/apps/web/src/lib/modules/articles/queries.ts b/apps/mana/apps/web/src/lib/modules/articles/queries.ts index 267ebf773..586de1b5a 100644 --- a/apps/mana/apps/web/src/lib/modules/articles/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/articles/queries.ts @@ -6,7 +6,7 @@ * to know which space it's in. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { scopedForModule, scopedGet } from '$lib/data/scope'; import { articleTagOps } from './stores/tags.svelte'; @@ -61,7 +61,7 @@ export function toHighlight(local: LocalHighlight): Highlight { // ─── Live Queries ───────────────────────────────────────── export function useAllArticles() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('articles', 'articles').toArray(); const visible = locals.filter((a) => !a.deletedAt); const decrypted = await decryptRecords('articles', visible); @@ -72,7 +72,7 @@ export function useAllArticles() { } export function useArticle(id: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { // scopedGet returns undefined if the article belongs to another // space — protects against URL-manipulated deep links. @@ -91,7 +91,7 @@ export function useArticle(id: string) { * table, so the DetailView's TagField stays in sync with both sides. */ export function useArticleTagIds(articleId: string) { - return useLiveQueryWithDefault(async () => articleTagOps.getTagIds(articleId), [] as string[]); + return useScopedLiveQuery(async () => articleTagOps.getTagIds(articleId), [] as string[]); } /** @@ -100,7 +100,7 @@ export function useArticleTagIds(articleId: string) { * Dexie query regardless of how many articles are shown. */ export function useArticleTagMap(articleIds: string[]) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => articleTagOps.getTagIdsForMany(articleIds), new Map() ); @@ -132,7 +132,7 @@ const WEEK_MS = 7 * 24 * 60 * 60 * 1000; * articles (needed for title-based top-sites grouping). */ export function useStats() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const [articleRows, highlightRows] = await Promise.all([ scopedForModule('articles', 'articles').toArray(), @@ -214,7 +214,7 @@ export interface HighlightWithArticle { } export function useAllHighlights() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const [articleRows, highlightRows] = await Promise.all([ scopedForModule('articles', 'articles').toArray(), scopedForModule('articles', 'articleHighlights').toArray(), @@ -250,7 +250,7 @@ export function useAllHighlights() { } export function useArticleHighlights(articleId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { // scopedForModule returns the scope-filtered Collection; we narrow // to this article in a post-filter (O(highlights per space), tiny). // Using scopedForModule instead of a direct indexed where() keeps the diff --git a/apps/mana/apps/web/src/lib/modules/body/queries.ts b/apps/mana/apps/web/src/lib/modules/body/queries.ts index b85cc1ed6..217ffebd3 100644 --- a/apps/mana/apps/web/src/lib/modules/body/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/body/queries.ts @@ -4,7 +4,7 @@ * Read-side only — mutations live in stores/body.svelte.ts. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -131,7 +131,7 @@ export function toBodyPhase(local: LocalBodyPhase): BodyPhase { // ─── Live Queries ─────────────────────────────────────────── export function useAllBodyExercises() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'body', 'bodyExercises' @@ -143,7 +143,7 @@ export function useAllBodyExercises() { } export function useAllBodyRoutines() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('body', 'bodyRoutines').sortBy( 'order' ); @@ -154,7 +154,7 @@ export function useAllBodyRoutines() { } export function useAllBodyWorkouts() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'body', 'bodyWorkouts' @@ -166,7 +166,7 @@ export function useAllBodyWorkouts() { } export function useAllBodySets() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('body', 'bodySets').toArray(); const visible = locals.filter((s) => !s.deletedAt); const decrypted = await decryptRecords('bodySets', visible); @@ -175,7 +175,7 @@ export function useAllBodySets() { } export function useSetsForWorkout(workoutId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('bodySets') .where('workoutId') @@ -188,7 +188,7 @@ export function useSetsForWorkout(workoutId: string) { } export function useAllBodyMeasurements() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'body', 'bodyMeasurements' @@ -200,7 +200,7 @@ export function useAllBodyMeasurements() { } export function useAllBodyChecks() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('body', 'bodyChecks').toArray(); const visible = locals.filter((c) => !c.deletedAt); const decrypted = await decryptRecords('bodyChecks', visible); @@ -209,7 +209,7 @@ export function useAllBodyChecks() { } export function useAllBodyPhases() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('body', 'bodyPhases').toArray(); const visible = locals.filter((p) => !p.deletedAt); const decrypted = await decryptRecords('bodyPhases', visible); @@ -231,7 +231,7 @@ export function useAllBodyPhases() { * the helper is permissive so a future "year view" can extend it. */ export function useFoodMealsSince(since: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db.table('meals').where('date').aboveOrEqual(since).toArray(); const visible = locals.filter((m) => !m.deletedAt); // Encrypted fields (description / portionSize / foods) get unwrapped diff --git a/apps/mana/apps/web/src/lib/modules/broadcast/queries.ts b/apps/mana/apps/web/src/lib/modules/broadcast/queries.ts index 03ce59bb7..c2130278e 100644 --- a/apps/mana/apps/web/src/lib/modules/broadcast/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/broadcast/queries.ts @@ -6,7 +6,7 @@ * matches the post-Spaces convention so lists respect the active space. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { scopedForModule } from '$lib/data/scope'; import { campaignTable, templateTable, settingsTable } from './collections'; @@ -77,7 +77,7 @@ export function toSettings(local: LocalBroadcastSettings): BroadcastSettings { /** All campaigns in the active space, newest first by updatedAt. */ export function useAllCampaigns() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const rows = await scopedForModule( 'broadcast', 'broadcastCampaigns' @@ -89,7 +89,7 @@ export function useAllCampaigns() { } export function useAllTemplates() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const rows = await scopedForModule( 'broadcast', 'broadcastTemplates' diff --git a/apps/mana/apps/web/src/lib/modules/calc/queries.ts b/apps/mana/apps/web/src/lib/modules/calc/queries.ts index 039012cc2..500b9f12a 100644 --- a/apps/mana/apps/web/src/lib/modules/calc/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/calc/queries.ts @@ -2,7 +2,7 @@ * Reactive queries for Calc — uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { LocalCalculation, LocalSavedFormula } from './types'; @@ -39,7 +39,7 @@ export function toSavedFormula(local: LocalSavedFormula): SavedFormula { /** All calculations (history), newest first. */ export function useAllCalculations() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'calc', 'calculations' @@ -53,7 +53,7 @@ export function useAllCalculations() { /** All saved formulas. */ export function useAllSavedFormulas() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'calc', 'savedFormulas' diff --git a/apps/mana/apps/web/src/lib/modules/calendar/queries.ts b/apps/mana/apps/web/src/lib/modules/calendar/queries.ts index 2223259d2..74ee996c2 100644 --- a/apps/mana/apps/web/src/lib/modules/calendar/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/calendar/queries.ts @@ -10,7 +10,7 @@ * at init time; no manual fetch/refresh needed. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule, applyVisibility } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -41,7 +41,7 @@ export function toCalendar(local: LocalCalendar): Calendar { /** All calendars, auto-updates on any change. */ export function useAllCalendars() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('calendar', 'calendars').toArray(); const visible = applyVisibility(locals).filter((c) => !c.deletedAt); return visible.map(toCalendar); @@ -53,7 +53,7 @@ export function useAllCalendars() { * with LocalEvent for native calendar events. Auto-updates on change. */ export function useAllCalendarItems() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { // Fetch all non-deleted timeBlocks (filter on plaintext deletedAt // before paying the per-row decrypt cost). Scope filter narrows to // the active space + visibility filter drops records the user isn't diff --git a/apps/mana/apps/web/src/lib/modules/chat/queries.ts b/apps/mana/apps/web/src/lib/modules/chat/queries.ts index 499c65fbf..c2954018d 100644 --- a/apps/mana/apps/web/src/lib/modules/chat/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/chat/queries.ts @@ -7,7 +7,7 @@ * to the public types so consumers see plaintext. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -69,7 +69,7 @@ export function toMessage(local: LocalMessage): Message { /** All non-archived conversations, sorted by pinned first then updatedAt desc. */ export function useAllConversations() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('chat', 'conversations').toArray() ).filter((c) => !c.deletedAt && !c.isArchived); @@ -80,7 +80,7 @@ export function useAllConversations() { /** All archived conversations, sorted by updatedAt desc. */ export function useArchivedConversations() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('chat', 'conversations').toArray() ).filter((c) => !c.deletedAt && c.isArchived); @@ -93,7 +93,7 @@ export function useArchivedConversations() { /** All templates, sorted by name. */ export function useAllTemplates() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('chat', 'chatTemplates').toArray() ).filter((t) => !t.deletedAt); @@ -104,7 +104,7 @@ export function useAllTemplates() { /** Messages for a specific conversation, sorted by createdAt asc. */ export function useConversationMessages(conversationId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await db .table('messages') diff --git a/apps/mana/apps/web/src/lib/modules/citycorners/queries.ts b/apps/mana/apps/web/src/lib/modules/citycorners/queries.ts index 04bdab9e6..4543ce0a2 100644 --- a/apps/mana/apps/web/src/lib/modules/citycorners/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/citycorners/queries.ts @@ -5,7 +5,7 @@ * at init time; no manual fetch/refresh needed. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { LocalCity, LocalLocation, LocalFavorite } from './types'; @@ -19,7 +19,7 @@ import type { LocalCity, LocalLocation, LocalFavorite } from './types'; /** All cities, sorted by name. Auto-updates on any change. */ export function useAllCities() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const all = await scopedForModule('citycorners', 'cities').toArray(); return all.filter((c) => !c.deletedAt).sort((a, b) => a.name.localeCompare(b.name)); }, [] as LocalCity[]); @@ -27,7 +27,7 @@ export function useAllCities() { /** All locations, sorted by name. Auto-updates on any change. */ export function useAllLocations() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const all = await scopedForModule( 'citycorners', 'ccLocations' @@ -38,7 +38,7 @@ export function useAllLocations() { /** All favorites. Auto-updates on any change. */ export function useAllFavorites() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const all = await scopedForModule( 'citycorners', 'ccFavorites' diff --git a/apps/mana/apps/web/src/lib/modules/comic/queries.ts b/apps/mana/apps/web/src/lib/modules/comic/queries.ts index 2b215f53b..a6c9fadd9 100644 --- a/apps/mana/apps/web/src/lib/modules/comic/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/comic/queries.ts @@ -8,7 +8,7 @@ * in this module, panels are picture rows). */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; import type { LocalImage, Image } from '$lib/modules/picture/types'; @@ -17,7 +17,7 @@ import { toStory, type ComicStory, type ComicStyle, type LocalComicStory } from /** All non-archived, non-deleted stories in the active space, newest first. */ export function useAllStories() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'comic', 'comicStories' @@ -32,7 +32,7 @@ export function useAllStories() { /** Stories filtered by style — used by the style-tabs view in M5 list tool. */ export function useStoriesByStyle(style: ComicStyle) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('comic', 'comicStories') .and((row) => row.style === style) .toArray(); @@ -52,7 +52,7 @@ export function useStoriesByStyle(style: ComicStyle) { * single-image hook today. */ export function usePanelImage(imageId: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!imageId) return null; const locals = await scopedForModule('picture', 'images') .and((row) => row.id === imageId) @@ -66,7 +66,7 @@ export function usePanelImage(imageId: string | null) { /** A single story by id, live-updating. Null while loading / missing. */ export function useStory(id: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!id) return null; const locals = await scopedForModule('comic', 'comicStories') .and((row) => row.id === id) @@ -87,7 +87,7 @@ export function useStory(id: string | null) { * ordered list but not deleted. */ export function useStoryPanels(storyId: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!storyId) return []; const locals = await scopedForModule('picture', 'images') .and((row) => row.comicStoryId === storyId) @@ -111,7 +111,7 @@ export function useStoriesByInput( module: 'journal' | 'notes' | 'library' | 'writing' | 'calendar' | null, entryId: string | null ) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!module || !entryId) return []; const locals = await scopedForModule( 'comic', diff --git a/apps/mana/apps/web/src/lib/modules/companion/queries.ts b/apps/mana/apps/web/src/lib/modules/companion/queries.ts index e2213b00d..cc1f2e07e 100644 --- a/apps/mana/apps/web/src/lib/modules/companion/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/companion/queries.ts @@ -2,13 +2,13 @@ * Companion Queries — Reactive reads for conversations and messages. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { LocalConversation, LocalMessage } from './types'; export function useConversations() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { try { const all = await scopedForModule( 'companion', @@ -22,7 +22,7 @@ export function useConversations() { } export function useMessages(conversationId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!conversationId) return []; try { return await db diff --git a/apps/mana/apps/web/src/lib/modules/contacts/queries.ts b/apps/mana/apps/web/src/lib/modules/contacts/queries.ts index 7cc942692..7e73bc231 100644 --- a/apps/mana/apps/web/src/lib/modules/contacts/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/contacts/queries.ts @@ -2,7 +2,7 @@ * Reactive queries & pure helpers for Contacts — uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule, applyVisibility } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -53,7 +53,7 @@ export function toContact(local: LocalContact): Contact { // ─── Live Queries ────────────────────────────────────────── export function useAllContacts() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const raw = await scopedForModule('contacts', 'contacts').toArray(); const visible = applyVisibility(raw).filter((c) => !c.deletedAt); const decrypted = await decryptRecords('contacts', visible); diff --git a/apps/mana/apps/web/src/lib/modules/context/queries.ts b/apps/mana/apps/web/src/lib/modules/context/queries.ts index 68cc67418..00faf1595 100644 --- a/apps/mana/apps/web/src/lib/modules/context/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/context/queries.ts @@ -6,7 +6,7 @@ * at init time; no manual fetch/refresh needed. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -49,7 +49,7 @@ export function toDocument(local: LocalDocument): Document { /** All spaces, sorted by name. Auto-updates on any change. */ export function useAllSpaces() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'context', 'contextSpaces' @@ -63,7 +63,7 @@ export function useAllSpaces() { /** All documents, sorted by updated_at desc. Auto-updates on any change. */ export function useAllDocuments() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('context', 'documents').toArray(); const visible = locals.filter((d) => !d.deletedAt); const decrypted = await decryptRecords('documents', visible); @@ -75,7 +75,7 @@ export function useAllDocuments() { /** Documents for a specific context-space. Auto-updates on any change. */ export function useSpaceDocuments(contextSpaceId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('documents') .where('contextSpaceId') diff --git a/apps/mana/apps/web/src/lib/modules/dreams/queries.ts b/apps/mana/apps/web/src/lib/modules/dreams/queries.ts index 9843e2da6..612aed89d 100644 --- a/apps/mana/apps/web/src/lib/modules/dreams/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/dreams/queries.ts @@ -7,7 +7,7 @@ import { formatDate } from '$lib/i18n/format'; * then decryptRecords the visible set before mapping to public types. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -63,7 +63,7 @@ export function toDreamSymbol(local: LocalDreamSymbol): DreamSymbol { // ─── Live Queries ────────────────────────────────────────── export function useAllDreams() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('dreams', 'dreams').toArray() ).filter((d) => !d.deletedAt && !d.isArchived); @@ -76,7 +76,7 @@ export function useAllDreams() { } export function useAllDreamSymbols() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('dreams', 'dreamSymbols').toArray() ).filter((s) => !s.deletedAt); diff --git a/apps/mana/apps/web/src/lib/modules/drink/queries.ts b/apps/mana/apps/web/src/lib/modules/drink/queries.ts index e70853014..9be167827 100644 --- a/apps/mana/apps/web/src/lib/modules/drink/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/drink/queries.ts @@ -2,7 +2,7 @@ * Reactive Queries & Pure Helpers for Drink module. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -45,7 +45,7 @@ export function toDrinkPreset(local: LocalDrinkPreset): DrinkPreset { // ─── Live Queries ───────────────────────────────────────── export function useAllDrinkEntries() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('drinkEntries') .orderBy('date') @@ -58,7 +58,7 @@ export function useAllDrinkEntries() { } export function useAllDrinkPresets() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('drink', 'drinkPresets').sortBy( 'order' ); diff --git a/apps/mana/apps/web/src/lib/modules/events/queries.ts b/apps/mana/apps/web/src/lib/modules/events/queries.ts index fb941725b..07e11a2a7 100644 --- a/apps/mana/apps/web/src/lib/modules/events/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/events/queries.ts @@ -4,7 +4,7 @@ * Joins LocalSocialEvent with its TimeBlock to produce the UI-facing SocialEvent. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -87,7 +87,7 @@ export function toEventGuest(local: LocalEventGuest): EventGuest { /** All non-deleted events, joined with their TimeBlock for time fields. */ export function useAllEvents() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'events', 'socialEvents' @@ -101,7 +101,7 @@ export function useAllEvents() { /** Upcoming events (startTime >= now), sorted ascending. */ export function useUpcomingEvents() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'events', 'socialEvents' @@ -119,7 +119,7 @@ export function useUpcomingEvents() { /** Past events. */ export function usePastEvents() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'events', 'socialEvents' @@ -137,7 +137,7 @@ export function usePastEvents() { /** Single event by ID. */ export function useEvent(eventId: () => string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const id = eventId(); if (!id) return null; @@ -153,7 +153,7 @@ export function useEvent(eventId: () => string) { /** All guests across all events, grouped by eventId. Useful for list views. */ export function useGuestsByEvent() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const all = await scopedForModule('events', 'eventGuests').toArray(); const visible = all.filter((g) => !g.deletedAt); @@ -173,7 +173,7 @@ export function useGuestsByEvent() { /** Guests for a single event. */ export function useEventGuests(eventId: () => string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const id = eventId(); if (!id) return []; const guests = await db @@ -189,7 +189,7 @@ export function useEventGuests(eventId: () => string) { /** Bring-list items for a single event, sorted by order. */ export function useEventItems(eventId: () => string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const id = eventId(); if (!id) return []; const items = await db diff --git a/apps/mana/apps/web/src/lib/modules/finance/queries.ts b/apps/mana/apps/web/src/lib/modules/finance/queries.ts index 52a239b6d..899a119dc 100644 --- a/apps/mana/apps/web/src/lib/modules/finance/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/finance/queries.ts @@ -2,7 +2,7 @@ * Reactive Queries & Pure Helpers for Finance module. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -45,7 +45,7 @@ export function toCategory(local: LocalFinanceCategory): FinanceCategory { // ─── Live Queries ────────────────────────────────────────── export function useAllTransactions() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('finance', 'transactions').toArray() ).filter((t) => !t.deletedAt); @@ -57,7 +57,7 @@ export function useAllTransactions() { } export function useAllCategories() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'finance', 'financeCategories' diff --git a/apps/mana/apps/web/src/lib/modules/firsts/queries.ts b/apps/mana/apps/web/src/lib/modules/firsts/queries.ts index 4b934b73b..af491e431 100644 --- a/apps/mana/apps/web/src/lib/modules/firsts/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/firsts/queries.ts @@ -1,4 +1,4 @@ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -35,7 +35,7 @@ export function toFirst(local: LocalFirst): First { // ─── Live Queries ────────────────────────────────────────── export function useAllFirsts() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('firsts', 'firsts').toArray() ).filter((f) => !f.deletedAt && !f.isArchived); @@ -58,7 +58,7 @@ export function useAllFirsts() { } export function useDreams() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('firsts', 'firsts').toArray() ).filter((f) => !f.deletedAt && !f.isArchived && f.status === 'dream'); @@ -72,7 +72,7 @@ export function useDreams() { } export function useLivedFirsts() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('firsts', 'firsts').toArray() ).filter((f) => !f.deletedAt && !f.isArchived && f.status === 'lived'); @@ -84,7 +84,7 @@ export function useLivedFirsts() { } export function useFirstsByPerson(personId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('firsts', 'firsts').toArray() ).filter((f) => !f.deletedAt && !f.isArchived && f.personIds?.includes(personId)); diff --git a/apps/mana/apps/web/src/lib/modules/food/queries.ts b/apps/mana/apps/web/src/lib/modules/food/queries.ts index 6c11c23ea..8e46178d9 100644 --- a/apps/mana/apps/web/src/lib/modules/food/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/food/queries.ts @@ -4,7 +4,7 @@ * Uses table names: meals, goals, foodFavorites. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -43,7 +43,7 @@ export function toMealWithNutrition(local: LocalMeal): MealWithNutrition { /** All meals, auto-updates on any change. */ export function useAllMeals() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('food', 'meals').toArray(); const visible = locals.filter((m) => !m.deletedAt); const decrypted = await decryptRecords('meals', visible); @@ -53,7 +53,7 @@ export function useAllMeals() { /** * Look up a single meal by id and decrypt it. Used by the detail page, - * which inlines its own useLiveQueryWithDefault wrapper so the querier + * which inlines its own useScopedLiveQuery wrapper so the querier * can capture the route param directly (matches plants DetailView pattern). */ export async function loadMealById(id: string): Promise { @@ -65,7 +65,7 @@ export async function loadMealById(id: string): Promise { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('food', 'goals').toArray(); return locals.filter((g) => !g.deletedAt); }, [] as LocalGoal[]); @@ -73,7 +73,7 @@ export function useAllGoals() { /** All favorites, auto-updates on any change. */ export function useAllFavorites() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('food', 'foodFavorites').toArray(); return locals.filter((f) => !f.deletedAt); }, [] as LocalFavorite[]); diff --git a/apps/mana/apps/web/src/lib/modules/guides/queries.ts b/apps/mana/apps/web/src/lib/modules/guides/queries.ts index cadda66cc..46fb28afe 100644 --- a/apps/mana/apps/web/src/lib/modules/guides/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/guides/queries.ts @@ -5,7 +5,7 @@ * content fields on the fly before handing to the UI. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -78,7 +78,7 @@ export function toRun(local: LocalRun): Run { // ─── Live Queries ────────────────────────────────────────── export function useAllGuides() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const all = await scopedForModule('guides', 'guides').toArray(); const visible = all.filter((g) => !g.deletedAt); const decrypted = await decryptRecords('guides', visible); @@ -87,7 +87,7 @@ export function useAllGuides() { } export function useGuide(id: () => string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const guideId = id(); if (!guideId) return null; @@ -101,7 +101,7 @@ export function useGuide(id: () => string) { } export function useSections(guideId: () => string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const gid = guideId(); if (!gid) return []; const all = await db.table('sections').where('guideId').equals(gid).toArray(); @@ -112,7 +112,7 @@ export function useSections(guideId: () => string) { } export function useSteps(guideId: () => string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const gid = guideId(); if (!gid) return []; const all = await db.table('steps').where('guideId').equals(gid).toArray(); @@ -123,7 +123,7 @@ export function useSteps(guideId: () => string) { } export function useLatestRun(guideId: () => string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const gid = guideId(); if (!gid) return null; @@ -138,7 +138,7 @@ export function useLatestRun(guideId: () => string) { } export function useRunsByGuide() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const all = await scopedForModule('guides', 'runs').toArray(); const visible = all.filter((r) => !r.deletedAt); const map = new Map(); diff --git a/apps/mana/apps/web/src/lib/modules/habits/queries.ts b/apps/mana/apps/web/src/lib/modules/habits/queries.ts index 5b06a65a1..0b6e772a8 100644 --- a/apps/mana/apps/web/src/lib/modules/habits/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/habits/queries.ts @@ -1,10 +1,10 @@ /** * Reactive Queries & Pure Helpers for Habits module. * - * Uses useLiveQueryWithDefault on the unified database. + * Uses useScopedLiveQuery on the unified database. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { LocalHabit, LocalHabitLog, Habit, HabitLog } from './types'; @@ -44,14 +44,14 @@ export function toHabitLog(local: LocalHabitLog, block?: LocalTimeBlock | null): // ─── Live Queries ────────────────────────────────────────── export function useAllHabits() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('habits', 'habits').sortBy('order'); return locals.filter((h) => !h.deletedAt).map(toHabit); }, [] as Habit[]); } export function useAllHabitLogs() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('habits', 'habitLogs').toArray(); const active = locals.filter((l) => !l.deletedAt); @@ -68,7 +68,7 @@ export function useAllHabitLogs() { } export function useHabitLogsForHabit(habitId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('habitLogs') .where('habitId') diff --git a/apps/mana/apps/web/src/lib/modules/inventory/queries.ts b/apps/mana/apps/web/src/lib/modules/inventory/queries.ts index 6c56fd975..7728f313f 100644 --- a/apps/mana/apps/web/src/lib/modules/inventory/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/inventory/queries.ts @@ -4,7 +4,7 @@ * Uses prefixed table names: invCollections, invItems, invLocations, invCategories. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -161,7 +161,7 @@ export function toCategory(local: LocalCategory): Category { // ─── Live Queries ────────────────────────────────────────── export function useAllCollections() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'inventory', 'invCollections' @@ -171,7 +171,7 @@ export function useAllCollections() { } export function useAllItems() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('inventory', 'invItems').toArray() ).filter((i) => !i.deletedAt); @@ -181,7 +181,7 @@ export function useAllItems() { } export function useAllLocations() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'inventory', 'invLocations' @@ -191,7 +191,7 @@ export function useAllLocations() { } export function useAllCategories() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'inventory', 'invCategories' diff --git a/apps/mana/apps/web/src/lib/modules/invoices/queries.ts b/apps/mana/apps/web/src/lib/modules/invoices/queries.ts index 5d6b45257..fe4ece2f8 100644 --- a/apps/mana/apps/web/src/lib/modules/invoices/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/invoices/queries.ts @@ -2,7 +2,7 @@ * Reactive queries and pure helpers for the Invoices module. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { scopedForModule } from '$lib/data/scope'; import type { @@ -77,7 +77,7 @@ export function toInvoiceClient(local: LocalInvoiceClient): InvoiceClient { * and persisting would require a cron to flip it, creating sync churn. */ export function useAllInvoices() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('invoices', 'invoices').toArray(); const visible = locals.filter((i) => !i.deletedAt); const decrypted = await decryptRecords('invoices', visible); @@ -95,7 +95,7 @@ export function useAllInvoices() { } export function useInvoiceClients() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'invoices', 'invoiceClients' diff --git a/apps/mana/apps/web/src/lib/modules/journal/queries.ts b/apps/mana/apps/web/src/lib/modules/journal/queries.ts index cab2fa9fd..9a0fc4cab 100644 --- a/apps/mana/apps/web/src/lib/modules/journal/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/journal/queries.ts @@ -7,7 +7,7 @@ import { formatDate } from '$lib/i18n/format'; * then decryptRecords the visible set before mapping to public types. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -36,7 +36,7 @@ export function toJournalEntry(local: LocalJournalEntry): JournalEntry { // ─── Live Queries ────────────────────────────────────────── export function useAllJournalEntries() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('journal', 'journalEntries').toArray() ).filter((e) => !e.deletedAt && !e.isArchived); @@ -49,7 +49,7 @@ export function useAllJournalEntries() { } export function useJournalEntry(id: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const local = await db.table('journalEntries').get(id); if (!local || local.deletedAt) return null; diff --git a/apps/mana/apps/web/src/lib/modules/mail/queries.ts b/apps/mana/apps/web/src/lib/modules/mail/queries.ts index 23860065e..339fdf5b0 100644 --- a/apps/mana/apps/web/src/lib/modules/mail/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/mail/queries.ts @@ -2,7 +2,7 @@ * Mail module queries — drafts (local-first) + API data helpers. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -29,7 +29,7 @@ export function toMailDraft(local: LocalMailDraft): MailDraft { // ─── Live Queries (local drafts) ──────────────────────────── export function useAllDrafts() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('mail', 'mailDrafts').toArray(); const visible = locals.filter((d) => !d.deletedAt); const decrypted = await decryptRecords('mailDrafts', visible); diff --git a/apps/mana/apps/web/src/lib/modules/meditate/queries.ts b/apps/mana/apps/web/src/lib/modules/meditate/queries.ts index b6c309944..cd2bfdfb4 100644 --- a/apps/mana/apps/web/src/lib/modules/meditate/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/meditate/queries.ts @@ -4,7 +4,7 @@ * Read-side only — mutations live in stores/meditate.svelte.ts. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -67,7 +67,7 @@ export function toMeditateSettings(local: LocalMeditateSettings): MeditateSettin // ─── Live Queries ─────────────────────────────────────────── export function useAllPresets() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('meditatePresets') .orderBy('order') @@ -79,7 +79,7 @@ export function useAllPresets() { } export function useAllSessions() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('meditateSessions') .orderBy('startedAt') @@ -92,7 +92,7 @@ export function useAllSessions() { } export function useSettings() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const locals = await scopedForModule( 'meditate', diff --git a/apps/mana/apps/web/src/lib/modules/memoro/queries.ts b/apps/mana/apps/web/src/lib/modules/memoro/queries.ts index 6fa1411fd..065ba8878 100644 --- a/apps/mana/apps/web/src/lib/modules/memoro/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/memoro/queries.ts @@ -2,7 +2,7 @@ * Reactive queries & pure helpers for Memoro — uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -68,7 +68,7 @@ export function toSpace(local: LocalSpace): Space { /** All non-archived memos, sorted by pinned first then createdAt desc. */ export function useAllMemos() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = (await scopedForModule('memoro', 'memos').toArray()).filter( (m) => !m.deletedAt && !m.isArchived ); @@ -79,7 +79,7 @@ export function useAllMemos() { /** All archived memos, sorted by updatedAt desc. */ export function useArchivedMemos() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = (await scopedForModule('memoro', 'memos').toArray()).filter( (m) => !m.deletedAt && m.isArchived ); @@ -92,7 +92,7 @@ export function useArchivedMemos() { /** Memories for a specific memo. */ export function useMemoriesByMemo(memoId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await db.table('memories').where('memoId').equals(memoId).toArray() ).filter((m) => !m.deletedAt); @@ -106,7 +106,7 @@ export { useAllTags } from '@mana/shared-stores'; /** All memo-tag associations. */ export function useAllMemoTags() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('memoro', 'memoTags').toArray(); return locals.filter((mt) => !mt.deletedAt); }, [] as LocalMemoTag[]); @@ -114,7 +114,7 @@ export function useAllMemoTags() { /** All spaces. */ export function useAllSpaces() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('memoro', 'memoroSpaces').toArray(); return locals.filter((s) => !s.deletedAt).map(toSpace); }, [] as Space[]); diff --git a/apps/mana/apps/web/src/lib/modules/mood/queries.ts b/apps/mana/apps/web/src/lib/modules/mood/queries.ts index 1238ac4f3..8e5333df6 100644 --- a/apps/mana/apps/web/src/lib/modules/mood/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/mood/queries.ts @@ -2,7 +2,7 @@ * Reactive Queries & Pure Helpers for the Mood module. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule, applyVisibility } from '$lib/data/scope'; @@ -46,7 +46,7 @@ export function toMoodSettings(local: LocalMoodSettings): MoodSettings { // ─── Live Queries ─────────────────────────────────────────── export function useAllMoodEntries() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('mood', 'moodEntries').toArray(); const visible = applyVisibility(locals).filter((e) => !e.deletedAt); const decrypted = await decryptRecords('moodEntries', visible); @@ -58,7 +58,7 @@ export function useAllMoodEntries() { } export function useMoodSettings() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const locals = await scopedForModule( 'mood', diff --git a/apps/mana/apps/web/src/lib/modules/moodlit/queries.ts b/apps/mana/apps/web/src/lib/modules/moodlit/queries.ts index 454f2cf25..e98ff9c46 100644 --- a/apps/mana/apps/web/src/lib/modules/moodlit/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/moodlit/queries.ts @@ -2,7 +2,7 @@ * Reactive queries for Moodlit — uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { LocalMood, LocalSequence, Mood } from './types'; @@ -26,7 +26,7 @@ export function getMoodById(moods: Mood[], id: string): Mood | undefined { /** All moods, sorted by name. */ export function useAllMoods() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('moodlit', 'moods').toArray(); return locals.filter((m) => !m.deletedAt); }, []); @@ -34,7 +34,7 @@ export function useAllMoods() { /** All sequences, sorted by name. */ export function useAllSequences() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('moodlit', 'sequences').toArray(); return locals.filter((s) => !s.deletedAt); }, []); diff --git a/apps/mana/apps/web/src/lib/modules/music/queries.ts b/apps/mana/apps/web/src/lib/modules/music/queries.ts index ece4f3cf7..389e82f54 100644 --- a/apps/mana/apps/web/src/lib/modules/music/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/music/queries.ts @@ -2,7 +2,7 @@ * Reactive queries & pure helpers for Music — uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -71,7 +71,7 @@ export function toProject(local: LocalProject): Project { /** All songs, sorted by title. */ export function useAllSongs() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('music', 'songs').toArray(); const visible = locals.filter((s) => !s.deletedAt); // title is encrypted on disk; sort needs the plaintext value. @@ -82,7 +82,7 @@ export function useAllSongs() { /** All playlists, sorted by name. */ export function useAllPlaylists() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'music', 'mukkePlaylists' @@ -95,7 +95,7 @@ export function useAllPlaylists() { /** All playlist-song associations. */ export function useAllPlaylistSongs() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'music', 'playlistSongs' @@ -106,7 +106,7 @@ export function useAllPlaylistSongs() { /** All projects, sorted by title. */ export function useAllProjects() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('music', 'mukkeProjects').toArray(); return locals .filter((p) => !p.deletedAt) @@ -117,7 +117,7 @@ export function useAllProjects() { /** All markers for a given beat ID. */ export function useMarkersByBeat(beatId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db.table('markers').where('beatId').equals(beatId).toArray(); return locals.filter((m) => !m.deletedAt).sort((a, b) => a.startTime - b.startTime); }, []); diff --git a/apps/mana/apps/web/src/lib/modules/notes/queries.ts b/apps/mana/apps/web/src/lib/modules/notes/queries.ts index 165362f79..92e9d372d 100644 --- a/apps/mana/apps/web/src/lib/modules/notes/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/notes/queries.ts @@ -15,7 +15,7 @@ import { formatDate } from '$lib/i18n/format'; * a future concern only if note volume per user grows past that. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule, scopedGet, applyVisibility } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -42,7 +42,7 @@ export function toNote(local: LocalNote): Note { // ─── Live Queries ────────────────────────────────────────── export function useAllNotes() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { // Filter on plaintext metadata first — none of these fields are // in the encryption registry, so they stay readable even with // the vault locked. Cuts the decrypt workload to only what the @@ -64,7 +64,7 @@ export function useAllNotes() { /** Single note by id, decrypted. Used by detail views. */ export function useNote(id: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { // scopedGet returns undefined if the note belongs to another // space — protects against URL-manipulated deep links. diff --git a/apps/mana/apps/web/src/lib/modules/period/queries.ts b/apps/mana/apps/web/src/lib/modules/period/queries.ts index 1bfd80b9f..33f8ff046 100644 --- a/apps/mana/apps/web/src/lib/modules/period/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/period/queries.ts @@ -2,7 +2,7 @@ * Reactive Queries & Pure Helpers for Periods module. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecord, decryptRecords } from '$lib/data/crypto'; @@ -65,7 +65,7 @@ export function toPeriodSymptom(local: LocalPeriodSymptom): PeriodSymptom { // ─── Live Queries ────────────────────────────────────────── export function useAllPeriods() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('period', 'periods').toArray() ).filter((c) => !c.deletedAt && !c.isArchived); @@ -75,7 +75,7 @@ export function useAllPeriods() { } export function useCurrentPeriod() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const locals = await scopedForModule('period', 'periods').toArray(); const real = locals.filter((c) => !c.deletedAt && !c.isArchived && !c.isPredicted); @@ -89,7 +89,7 @@ export function useCurrentPeriod() { } export function useAllDayLogs() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('period', 'periodDayLogs').toArray() ).filter((l) => !l.deletedAt); @@ -99,7 +99,7 @@ export function useAllDayLogs() { } export function useDayLog(date: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const locals = await db .table('periodDayLogs') @@ -116,7 +116,7 @@ export function useDayLog(date: string) { } export function useAllSymptoms() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'period', 'periodSymptoms' diff --git a/apps/mana/apps/web/src/lib/modules/photos/queries.ts b/apps/mana/apps/web/src/lib/modules/photos/queries.ts index f9fd8a7d9..cf65d1e98 100644 --- a/apps/mana/apps/web/src/lib/modules/photos/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/photos/queries.ts @@ -4,7 +4,7 @@ * Uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { LocalAlbum, LocalAlbumItem, LocalFavorite, Album, AlbumItem } from './types'; @@ -42,7 +42,7 @@ export function toAlbumItem(local: LocalAlbumItem): AlbumItem { /** All albums. Auto-updates on any change. */ export function useAllAlbums() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('photos', 'albums').toArray(); return locals.filter((a) => !a.deletedAt).map(toAlbum); }, []); @@ -50,7 +50,7 @@ export function useAllAlbums() { /** All album items. Auto-updates on any change. */ export function useAllAlbumItems() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('photos', 'albumItems').toArray(); return locals.filter((i) => !i.deletedAt).map(toAlbumItem); }, []); @@ -58,7 +58,7 @@ export function useAllAlbumItems() { /** All favorites. Auto-updates on any change. */ export function useAllFavorites() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'photos', 'photoFavorites' diff --git a/apps/mana/apps/web/src/lib/modules/picture/queries.ts b/apps/mana/apps/web/src/lib/modules/picture/queries.ts index 6e748b786..720a7a49b 100644 --- a/apps/mana/apps/web/src/lib/modules/picture/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/picture/queries.ts @@ -7,7 +7,7 @@ */ import { liveQuery } from 'dexie'; -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -77,7 +77,7 @@ export function toBoard(local: LocalBoard): Board { /** All non-archived images, sorted by createdAt desc. */ export function useAllImages() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('picture', 'images').toArray(); const visible = locals.filter((img) => !img.isArchived && !img.deletedAt); const decrypted = await decryptRecords('images', visible); @@ -89,7 +89,7 @@ export function useAllImages() { /** All archived images, sorted by createdAt desc. */ export function useArchivedImages() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('picture', 'images').toArray(); const visible = locals.filter((img) => !!img.isArchived && !img.deletedAt); const decrypted = await decryptRecords('images', visible); @@ -101,7 +101,7 @@ export function useArchivedImages() { /** All boards with item counts, sorted by updatedAt desc. */ export function useAllBoards() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('picture', 'boards').toArray(); const allItems = await scopedForModule( 'picture', @@ -139,7 +139,7 @@ export { useAllTags as useAllPictureTags } from '@mana/shared-stores'; /** All image-tag associations. */ export function useAllImageTags() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { return await scopedForModule('picture', 'imageTags').toArray(); }, [] as LocalImageTag[]); } diff --git a/apps/mana/apps/web/src/lib/modules/plants/queries.ts b/apps/mana/apps/web/src/lib/modules/plants/queries.ts index 52328e492..b377641e0 100644 --- a/apps/mana/apps/web/src/lib/modules/plants/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/queries.ts @@ -6,7 +6,7 @@ * at init time; no manual fetch/refresh needed. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -99,7 +99,7 @@ export function toWateringLog(local: LocalWateringLog): WateringLog { /** All plants. Auto-updates on any change. */ export function useAllPlants() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('plants', 'plants').toArray() ).filter((p) => !p.deletedAt); @@ -110,7 +110,7 @@ export function useAllPlants() { /** All plant photos. Auto-updates on any change. */ export function useAllPlantPhotos() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'plants', 'plantPhotos' @@ -121,7 +121,7 @@ export function useAllPlantPhotos() { /** All watering schedules. Auto-updates on any change. */ export function useAllWateringSchedules() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'plants', 'wateringSchedules' @@ -132,7 +132,7 @@ export function useAllWateringSchedules() { /** All watering logs. Auto-updates on any change. */ export function useAllWateringLogs() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'plants', 'wateringLogs' @@ -143,7 +143,7 @@ export function useAllWateringLogs() { /** All plant↔tag junctions (active only). */ export function useAllPlantTags() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('plants', 'plantTags').toArray(); return locals.filter((t) => !t.deletedAt); }, []); diff --git a/apps/mana/apps/web/src/lib/modules/playground/queries.ts b/apps/mana/apps/web/src/lib/modules/playground/queries.ts index 981afae4e..69a8b1448 100644 --- a/apps/mana/apps/web/src/lib/modules/playground/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/playground/queries.ts @@ -6,7 +6,7 @@ * pattern as notes / dreams / places. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -34,7 +34,7 @@ export function toSnippet(local: LocalPlaygroundSnippet): PlaygroundSnippet { } export function useAllSnippets() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('playgroundSnippets') .orderBy('order') @@ -85,7 +85,7 @@ export function toMessage(local: LocalPlaygroundMessage): PlaygroundConversation } export function useAllConversations() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'playground', 'playgroundConversations' @@ -100,7 +100,7 @@ export function useAllConversations() { } export function useConversationMessages(conversationId: () => string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const cid = conversationId(); if (!cid) return []; const locals = await db diff --git a/apps/mana/apps/web/src/lib/modules/presi/queries.ts b/apps/mana/apps/web/src/lib/modules/presi/queries.ts index 76088b587..9230c87f1 100644 --- a/apps/mana/apps/web/src/lib/modules/presi/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/presi/queries.ts @@ -4,7 +4,7 @@ * Uses prefixed table names: presiDecks, slides. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecord, decryptRecords } from '$lib/data/crypto'; @@ -40,7 +40,7 @@ export function toSlide(local: LocalSlide): Slide { /** All decks, sorted by updatedAt descending. Auto-updates on any change. */ export function useAllDecks() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await scopedForModule('presi', 'presiDecks').toArray() ).filter((d) => !d.deletedAt); @@ -53,7 +53,7 @@ export function useAllDecks() { /** Slides for a specific deck, sorted by order. Auto-updates on any change. */ export function useDeckSlides(deckId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await db.table('slides').where('deckId').equals(deckId).toArray() ).filter((s) => !s.deletedAt); @@ -64,7 +64,7 @@ export function useDeckSlides(deckId: string) { /** Single deck by ID. Auto-updates on any change. */ export function useDeck(id: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const local = await db.table('presiDecks').get(id); if (!local || local.deletedAt) return null; diff --git a/apps/mana/apps/web/src/lib/modules/profile/queries.ts b/apps/mana/apps/web/src/lib/modules/profile/queries.ts index 5d5336d93..f76c1a138 100644 --- a/apps/mana/apps/web/src/lib/modules/profile/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/profile/queries.ts @@ -7,7 +7,7 @@ * visible pool without any query re-write. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { scopedForModule } from '$lib/data/scope'; import { userContextTable } from './collections'; @@ -24,7 +24,7 @@ import { /** Reactive live-query for the user context singleton. */ export function useUserContext() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const local = await userContextTable.get(USER_CONTEXT_SINGLETON_ID); if (!local || local.deletedAt) return null; const [decrypted] = await decryptRecords('userContext', [local]); @@ -38,7 +38,7 @@ export function useUserContext() { * query re-runs automatically when the active space changes. */ export function useAllMeImages() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('profile', 'meImages').toArray(); const visible = locals .filter((row) => !row.deletedAt) @@ -52,7 +52,7 @@ export function useAllMeImages() { * Me-images in the active space filtered by `kind`. */ export function useMeImagesByKind(kind: MeImageKind) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('profile', 'meImages') .and((row) => row.kind === kind) .toArray(); @@ -71,7 +71,7 @@ export function useMeImagesByKind(kind: MeImageKind) { * isn't here, it must not be sent to OpenAI. */ export function useReferenceImages() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('profile', 'meImages').toArray(); const visible = locals .filter((row) => !row.deletedAt && row.usage?.aiReference === true) @@ -89,7 +89,7 @@ export function useReferenceImages() { * local to that space. */ export function useImageByPrimary(slot: MeImagePrimarySlot) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('profile', 'meImages') .and((row) => row.primaryFor === slot) .toArray(); diff --git a/apps/mana/apps/web/src/lib/modules/questions/queries.ts b/apps/mana/apps/web/src/lib/modules/questions/queries.ts index 00ee3ea9d..e37d4fa60 100644 --- a/apps/mana/apps/web/src/lib/modules/questions/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/questions/queries.ts @@ -4,7 +4,7 @@ * Uses table names: qCollections, questions, answers. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -103,7 +103,7 @@ export function toAnswer(local: LocalAnswer): Answer { /** All collections, sorted by sortOrder. Auto-updates on any change. */ export function useAllCollections() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'questions', 'qCollections' @@ -117,7 +117,7 @@ export function useAllCollections() { /** All questions. Auto-updates on any change. */ export function useAllQuestions() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('questions', 'questions').toArray(); const visible = locals.filter((q) => !q.deletedAt); const decrypted = await decryptRecords('questions', visible); @@ -127,7 +127,7 @@ export function useAllQuestions() { /** All answers for a given question. */ export function useAnswersByQuestion(questionId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('questions', 'answers').toArray(); const visible = locals.filter((a) => !a.deletedAt && a.questionId === questionId); const decrypted = await decryptRecords('answers', visible); diff --git a/apps/mana/apps/web/src/lib/modules/quiz/queries.ts b/apps/mana/apps/web/src/lib/modules/quiz/queries.ts index 64bb9f4d4..d3fdd332e 100644 --- a/apps/mana/apps/web/src/lib/modules/quiz/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/quiz/queries.ts @@ -7,7 +7,7 @@ * AttemptAnswer payloads, which hold no user-typed content). */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -69,7 +69,7 @@ export function toAttempt(local: LocalQuizAttempt): QuizAttempt { // ─── Live Queries ────────────────────────────────────────── export function useAllQuizzes() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = (await scopedForModule('quiz', 'quizzes').toArray()).filter( (q) => !q.deletedAt && !q.isArchived ); @@ -82,7 +82,7 @@ export function useAllQuizzes() { } export function useQuiz(id: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const local = await db.table('quizzes').get(id); if (!local || local.deletedAt) return null; @@ -94,7 +94,7 @@ export function useQuiz(id: string) { } export function useQuestions(quizId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await db.table('quizQuestions').where('quizId').equals(quizId).toArray() ).filter((q) => !q.deletedAt); @@ -104,7 +104,7 @@ export function useQuestions(quizId: string) { } export function useAttempts(quizId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const visible = ( await db.table('quizAttempts').where('quizId').equals(quizId).toArray() ).filter((a) => !a.deletedAt); diff --git a/apps/mana/apps/web/src/lib/modules/recipes/queries.ts b/apps/mana/apps/web/src/lib/modules/recipes/queries.ts index 00eb25d77..2d73a49f7 100644 --- a/apps/mana/apps/web/src/lib/modules/recipes/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/recipes/queries.ts @@ -2,7 +2,7 @@ * Reactive Queries & Pure Helpers for Recipes module. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -36,7 +36,7 @@ export function toRecipe(local: LocalRecipe): Recipe { // ─── Live Queries ───────────────────────────────────────── export function useAllRecipes() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('recipes', 'recipes').toArray(); const visible = locals.filter((r) => !r.deletedAt); const decrypted = await decryptRecords('recipes', visible); diff --git a/apps/mana/apps/web/src/lib/modules/skilltree/queries.ts b/apps/mana/apps/web/src/lib/modules/skilltree/queries.ts index 950d5d8c4..e6ec74ae1 100644 --- a/apps/mana/apps/web/src/lib/modules/skilltree/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/skilltree/queries.ts @@ -6,7 +6,7 @@ * at init time; no manual fetch/refresh needed. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { LocalSkill, LocalActivity, LocalAchievement } from './types'; @@ -47,7 +47,7 @@ export function toActivity(local: LocalActivity): Activity { /** All skills, auto-updates on any change. */ export function useAllSkills() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('skilltree', 'skills').toArray(); return locals.filter((s) => !s.deletedAt).map(toSkill); }, [] as Skill[]); @@ -55,7 +55,7 @@ export function useAllSkills() { /** All activities, auto-updates on any change. */ export function useAllActivities() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'skilltree', 'activities' @@ -66,7 +66,7 @@ export function useAllActivities() { /** All achievements (raw local records), auto-updates on any change. */ export function useAllAchievements() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'skilltree', 'achievements' diff --git a/apps/mana/apps/web/src/lib/modules/sleep/queries.ts b/apps/mana/apps/web/src/lib/modules/sleep/queries.ts index e0b3c8817..ff1199e47 100644 --- a/apps/mana/apps/web/src/lib/modules/sleep/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/sleep/queries.ts @@ -4,7 +4,7 @@ * Read-side only — mutations live in stores/sleep.svelte.ts. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -84,7 +84,7 @@ export function toSleepSettings(local: LocalSleepSettings): SleepSettings { // ─── Live Queries ─────────────────────────────────────────── export function useAllSleepEntries() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'sleep', 'sleepEntries' @@ -96,7 +96,7 @@ export function useAllSleepEntries() { } export function useAllSleepHygieneLogs() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'sleep', 'sleepHygieneLogs' @@ -107,7 +107,7 @@ export function useAllSleepHygieneLogs() { } export function useAllSleepHygieneChecks() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'sleep', 'sleepHygieneChecks' @@ -119,7 +119,7 @@ export function useAllSleepHygieneChecks() { } export function useSleepSettings() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const locals = await scopedForModule( 'sleep', diff --git a/apps/mana/apps/web/src/lib/modules/storage/queries.ts b/apps/mana/apps/web/src/lib/modules/storage/queries.ts index 985cdfd2b..7a94c0da8 100644 --- a/apps/mana/apps/web/src/lib/modules/storage/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/storage/queries.ts @@ -4,7 +4,7 @@ * Uses table names: files, storageFolders, storageTags, fileTags. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -107,7 +107,7 @@ export function toTag(local: { /** All non-deleted files, sorted by name. Auto-updates on any change. */ export function useAllFiles() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('storage', 'files').toArray(); const visible = locals.filter((f) => !f.isDeleted && !f.deletedAt); // name + originalName are encrypted on disk; sort needs plaintext. @@ -118,7 +118,7 @@ export function useAllFiles() { /** All non-deleted folders, sorted by name. Auto-updates on any change. */ export function useAllFolders() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'storage', 'storageFolders' diff --git a/apps/mana/apps/web/src/lib/modules/stretch/queries.ts b/apps/mana/apps/web/src/lib/modules/stretch/queries.ts index 47ba89a17..a5c072065 100644 --- a/apps/mana/apps/web/src/lib/modules/stretch/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/stretch/queries.ts @@ -4,7 +4,7 @@ * Read-side only — mutations live in stores/stretch.svelte.ts. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -109,7 +109,7 @@ export function toStretchReminder(local: LocalStretchReminder): StretchReminder // ─── Live Queries ─────────────────────────────────────────── export function useAllStretchExercises() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'stretch', 'stretchExercises' @@ -121,7 +121,7 @@ export function useAllStretchExercises() { } export function useAllStretchRoutines() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'stretch', 'stretchRoutines' @@ -133,7 +133,7 @@ export function useAllStretchRoutines() { } export function useAllStretchSessions() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'stretch', 'stretchSessions' @@ -145,7 +145,7 @@ export function useAllStretchSessions() { } export function useAllStretchAssessments() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'stretch', 'stretchAssessments' @@ -159,7 +159,7 @@ export function useAllStretchAssessments() { } export function useAllStretchReminders() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'stretch', 'stretchReminders' diff --git a/apps/mana/apps/web/src/lib/modules/times/queries.ts b/apps/mana/apps/web/src/lib/modules/times/queries.ts index 3e2ecd175..935dee394 100644 --- a/apps/mana/apps/web/src/lib/modules/times/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/times/queries.ts @@ -7,7 +7,7 @@ import { liveQuery } from 'dexie'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import type { LocalClient, LocalProject, @@ -255,7 +255,7 @@ export function allWorldClocks$() { /** All alarms, auto-updates on any change. Returns { value, loading, error }. */ export function useAllAlarms() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('times', 'timeAlarms').toArray(); return locals.filter((a) => !a.deletedAt).map(toAlarm); }, [] as Alarm[]); @@ -263,7 +263,7 @@ export function useAllAlarms() { /** All countdown timers, auto-updates on any change. Returns { value, loading, error }. */ export function useAllCountdownTimers() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'times', 'timeCountdownTimers' @@ -274,7 +274,7 @@ export function useAllCountdownTimers() { /** All world clocks, sorted by sortOrder. Returns { value, loading, error }. */ export function useAllWorldClocks() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('timeWorldClocks') .orderBy('sortOrder') diff --git a/apps/mana/apps/web/src/lib/modules/todo/queries.ts b/apps/mana/apps/web/src/lib/modules/todo/queries.ts index ebaaaf45d..416026226 100644 --- a/apps/mana/apps/web/src/lib/modules/todo/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/todo/queries.ts @@ -2,7 +2,7 @@ * Reactive queries & pure helpers for Todo — uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule, applyVisibility } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -45,7 +45,7 @@ export function toTask(local: LocalTask): Task { // ─── Live Queries ────────────────────────────────────────── export function useAllTasks() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { // Scope-first, then in-memory sort by `order`. sortBy is O(n) — fine // for a user's own task list; if it ever becomes hot, add a // [spaceId+order] compound index in a follow-up Dexie version. @@ -72,7 +72,7 @@ export function useAllTasks() { export { useAllTags as useAllLabels } from '@mana/shared-stores'; export function useAllBoardViews() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('todo', 'boardViews').sortBy( 'order' ); @@ -81,14 +81,14 @@ export function useAllBoardViews() { } export function useAllReminders() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('todo', 'reminders').toArray(); return applyVisibility(locals).filter((r) => !r.deletedAt); }, [] as LocalReminder[]); } export function useAllProjects() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('todo', 'todoProjects').sortBy( 'order' ); diff --git a/apps/mana/apps/web/src/lib/modules/uload/queries.ts b/apps/mana/apps/web/src/lib/modules/uload/queries.ts index e91e20db4..41e3ce360 100644 --- a/apps/mana/apps/web/src/lib/modules/uload/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/uload/queries.ts @@ -6,7 +6,7 @@ */ import { liveQuery } from 'dexie'; -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecord, decryptRecords } from '$lib/data/crypto'; @@ -165,7 +165,7 @@ export function allLinkTags$() { // ─── Svelte 5 Reactive Hooks ────────────────────────────── export function useAllLinks() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('uload', 'links').toArray(); const visible = locals.filter((l) => !l.deletedAt); const decrypted = await decryptRecords('links', visible); @@ -174,14 +174,14 @@ export function useAllLinks() { } export function useAllTags() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('uload', 'uloadTags').toArray(); return locals.filter((t) => !t.deletedAt).map(toTag); }, [] as Tag[]); } export function useAllFolders() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('uload', 'uloadFolders').sortBy( 'order' ); @@ -190,14 +190,14 @@ export function useAllFolders() { } export function useAllLinkTags() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('uload', 'linkTags').toArray(); return locals.filter((lt) => !lt.deletedAt).map(toLinkTag); }, [] as LinkTag[]); } export function useLinkById(id: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { if (!id) return null; const local = await db.table('links').get(id); diff --git a/apps/mana/apps/web/src/lib/modules/wardrobe/queries.ts b/apps/mana/apps/web/src/lib/modules/wardrobe/queries.ts index 3bd9ff66c..9c5bdf496 100644 --- a/apps/mana/apps/web/src/lib/modules/wardrobe/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/wardrobe/queries.ts @@ -7,7 +7,7 @@ * filtered by `wardrobeOutfitId` — see useOutfitTryOns below. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; import type { LocalImage, Image } from '$lib/modules/picture/types'; @@ -27,7 +27,7 @@ import { /** All non-archived, non-deleted garments in the active space. */ export function useAllGarments() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'wardrobe', 'wardrobeGarments' @@ -42,7 +42,7 @@ export function useAllGarments() { /** Garments filtered by category — used by the Category-Tabs view. */ export function useGarmentsByCategory(category: GarmentCategory) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'wardrobe', 'wardrobeGarments' @@ -59,7 +59,7 @@ export function useGarmentsByCategory(category: GarmentCategory) { /** A single garment by id, live-updating. Null while loading / missing. */ export function useGarment(id: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!id) return null; const locals = await scopedForModule( 'wardrobe', @@ -78,7 +78,7 @@ export function useGarment(id: string | null) { /** All non-archived outfits in the active space. */ export function useAllOutfits() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule( 'wardrobe', 'wardrobeOutfits' @@ -92,7 +92,7 @@ export function useAllOutfits() { } export function useOutfitsByOccasion(occasion: OutfitOccasion) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('wardrobe', 'wardrobeOutfits') .and((row) => row.occasion === occasion) .toArray(); @@ -105,7 +105,7 @@ export function useOutfitsByOccasion(occasion: OutfitOccasion) { } export function useOutfit(id: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!id) return null; const locals = await scopedForModule('wardrobe', 'wardrobeOutfits') .and((row) => row.id === id) @@ -125,7 +125,7 @@ export function useOutfit(id: string | null) { * strip under the current composition. */ export function useOutfitTryOns(outfitId: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!outfitId) return []; const locals = await scopedForModule('picture', 'images') .and((row) => row.wardrobeOutfitId === outfitId) @@ -146,7 +146,7 @@ export function useOutfitTryOns(outfitId: string | null) { * `useOutfitsContainingGarment` for the cross-outfit view. */ export function useGarmentSoloTryOns(garmentId: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!garmentId) return []; const locals = await scopedForModule('picture', 'images') .and((row) => row.wardrobeGarmentId === garmentId) @@ -167,7 +167,7 @@ export function useGarmentSoloTryOns(garmentId: string | null) { * snapshot provides the thumbnail without another image lookup. */ export function useOutfitsContainingGarment(garmentId: string | null) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!garmentId) return []; const locals = await scopedForModule( 'wardrobe', diff --git a/apps/mana/apps/web/src/lib/modules/website/queries.ts b/apps/mana/apps/web/src/lib/modules/website/queries.ts index 571789c50..05fa42b0f 100644 --- a/apps/mana/apps/web/src/lib/modules/website/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/website/queries.ts @@ -7,7 +7,7 @@ * parameters change, which is why we don't accept id params here. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { @@ -72,7 +72,7 @@ export function toWebsiteBlock(local: LocalWebsiteBlock): WebsiteBlock { // ─── Live Queries ───────────────────────────────────────── export function useAllSites() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('website', 'websites').toArray(); const visible = locals.filter((s) => !s.deletedAt); return visible.map(toWebsite).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)); @@ -80,7 +80,7 @@ export function useAllSites() { } export function useAllPages() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db.table('websitePages').toArray(); const visible = locals.filter((p) => !p.deletedAt); return visible.map(toWebsitePage).sort((a, b) => a.order - b.order); @@ -88,7 +88,7 @@ export function useAllPages() { } export function useAllBlocks() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db.table('websiteBlocks').toArray(); const visible = locals.filter((b) => !b.deletedAt); return visible.map(toWebsiteBlock).sort((a, b) => a.order - b.order); diff --git a/apps/mana/apps/web/src/lib/modules/wetter/queries.ts b/apps/mana/apps/web/src/lib/modules/wetter/queries.ts index ad861f65c..2eb057e31 100644 --- a/apps/mana/apps/web/src/lib/modules/wetter/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/wetter/queries.ts @@ -2,27 +2,27 @@ * Wetter module read-side — Dexie liveQuery hooks for locations. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import type { WeatherLocation, WeatherSettings } from './types'; export function useLocations() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( () => scopedForModule('wetter', 'wetterLocations').sortBy('order'), [] as WeatherLocation[] ); } export function useDefaultLocation() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( () => db.table('wetterLocations').where('isDefault').equals(1).first(), undefined as WeatherLocation | undefined ); } export function useSettings() { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { const settings = await db.table('wetterSettings').get('default'); return ( diff --git a/apps/mana/apps/web/src/lib/modules/wishes/queries.ts b/apps/mana/apps/web/src/lib/modules/wishes/queries.ts index a42c12f72..ddfd6e767 100644 --- a/apps/mana/apps/web/src/lib/modules/wishes/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/wishes/queries.ts @@ -2,7 +2,7 @@ * Reactive queries & pure helpers for Wishes — uses Dexie liveQuery on the unified DB. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; import { decryptRecords } from '$lib/data/crypto'; @@ -68,7 +68,7 @@ export function toPriceCheck(local: LocalPriceCheck): PriceCheck { // ─── Live Queries ────────────────────────────────────────── export function useAllWishes() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('wishes', 'wishesItems').sortBy( 'order' ); @@ -79,7 +79,7 @@ export function useAllWishes() { } export function useAllLists() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('wishes', 'wishesLists').sortBy( 'order' ); @@ -89,7 +89,7 @@ export function useAllLists() { } export function usePriceChecks(wishId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await db .table('wishesPriceChecks') .where('wishId') diff --git a/apps/mana/apps/web/src/lib/modules/writing/queries.ts b/apps/mana/apps/web/src/lib/modules/writing/queries.ts index d8ffcf771..895e6d46b 100644 --- a/apps/mana/apps/web/src/lib/modules/writing/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/writing/queries.ts @@ -2,7 +2,7 @@ * Reactive queries + pure helpers for the Writing module. */ -import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte'; import { decryptRecords } from '$lib/data/crypto'; import { db } from '$lib/data/database'; import { scopedForModule } from '$lib/data/scope'; @@ -102,7 +102,7 @@ export function toWritingStyle(local: LocalWritingStyle): WritingStyle { // ─── Live Queries ───────────────────────────────────────── export function useAllDrafts() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const locals = await scopedForModule('writing', 'writingDrafts').toArray(); const visible = locals.filter((d) => !d.deletedAt); const decrypted = await decryptRecords('writingDrafts', visible); @@ -111,7 +111,7 @@ export function useAllDrafts() { } export function useDraft(id: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { if (!id) return null; const row = await db.table('writingDrafts').get(id); @@ -124,7 +124,7 @@ export function useDraft(id: string) { } export function useVersionsForDraft(draftId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!draftId) return [] as DraftVersion[]; const rows = await db .table('writingDraftVersions') @@ -138,7 +138,7 @@ export function useVersionsForDraft(draftId: string) { } export function useVersion(versionId: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { if (!versionId) return null; const row = await db.table('writingDraftVersions').get(versionId); @@ -157,7 +157,7 @@ export function useVersion(versionId: string) { * automatically in the editor. */ export function useCurrentVersionForDraft(draftId: string) { - return useLiveQueryWithDefault( + return useScopedLiveQuery( async () => { if (!draftId) return null; const draftRow = await db.table('writingDrafts').get(draftId); @@ -174,7 +174,7 @@ export function useCurrentVersionForDraft(draftId: string) { } export function useGenerationsForDraft(draftId: string) { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { if (!draftId) return [] as Generation[]; const rows = await db .table('writingGenerations') @@ -188,7 +188,7 @@ export function useGenerationsForDraft(draftId: string) { } export function useAllStyles() { - return useLiveQueryWithDefault(async () => { + return useScopedLiveQuery(async () => { const rows = await scopedForModule( 'writing', 'writingStyles'