mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 22:41:26 +02:00
refactor(scope): replace _scopeCursor bridge with reactive useScopedLiveQuery hook
The previous fix wired Dexie's `_scopeCursor` infra-table as a side-
channel between Svelte $state (active-space + current-user) and Dexie
liveQuery: every scoped query touched the table on read so liveQuery
subscribed to it, every setActiveSpace bumped the table so liveQuery
re-ran. Worked, but smelled — hidden side-effect inside `scopedTable`,
scope state pretending to be a Dexie row, +1 roundtrip per query, and
`current-user.ts` had to dynamic-import Dexie just to pump the bridge.
Replacement: a Svelte 5 `$effect`-based hook that owns scope-tracking
explicitly. The dependency now lives in the reactive layer (which is
where it belongs), not as a side-effect in the data layer.
What changes:
- New `data/scope/use-scoped-live-query.svelte.ts`. The hook reads a
module-level `scopeTick` `$state` counter inside its `$effect`.
Both `onActiveSpaceChanged` (existing) and `onCurrentUserChanged`
(new, added to `current-user.ts`) bump the tick on real changes.
Effect re-fires → previous Dexie subscription unsubscribes → fresh
one created with up-to-date `getInScopeSpaceIds()`. Same return
shape as `useLiveQueryWithDefault` for drop-in migration.
- `current-user.ts` gains an `onCurrentUserChanged` event bus,
symmetric to `active-space.svelte.ts#onActiveSpaceChanged`. Stays
a plain `.ts` (no runes) so it remains a leaf and works in the
test runner without the Svelte preprocessor — the rename to
`.svelte.ts` was tried earlier and reverted because of test
fallout (commit `01e6b9f04`).
- 53 module `queries.ts` files migrated from
`useLiveQueryWithDefault` → `useScopedLiveQuery`. The choice of
hook now documents at the call-site whether the query is
scope-aware. Pure mechanical find-replace — no logic changes.
What goes away:
- `data/scope/cursor.ts` deleted.
- `touchScopeCursor()` calls removed from `scopedTable` /
`scopedAnd` / `scopedGet`. Functions are pure data-layer again,
no implicit reactive subscriptions.
- `bumpScopeCursor()` calls removed from `setActiveSpace` and both
`loadActiveSpace` branches. Setter is pure state-update, no
Dexie write side-effect.
- `current-user.ts` no longer dynamic-imports `scope/cursor` —
`setCurrentUserId` is a clean three-line setter again.
- Dexie v46 drops the `_scopeCursor` table (`stores: { _scopeCursor:
null }`). v45 stays declared so existing browsers' version chain
remains contiguous; the v46 deletion runs once on next open. No
user data lost — the table only ever held a transient bumpedAt
row.
Existing 14 scope regression tests still pass. Type-check + theme-
token validators are clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
167d616cf7
commit
636138b2d4
57 changed files with 358 additions and 378 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<A
|
|||
active = member;
|
||||
status = 'ready';
|
||||
writeActiveSpaceHint(member.id);
|
||||
if (member.id !== prevId) {
|
||||
notifyHandlers(member);
|
||||
bumpScopeCursor();
|
||||
}
|
||||
if (member.id !== prevId) notifyHandlers(member);
|
||||
return member;
|
||||
}
|
||||
|
||||
|
|
@ -197,10 +186,7 @@ export async function loadActiveSpace(opts: { force?: boolean } = {}): Promise<A
|
|||
active = { ...chosen, role: hinted ? hinted.role : 'owner' };
|
||||
status = 'ready';
|
||||
writeActiveSpaceHint(chosen.id);
|
||||
if (active.id !== prevId) {
|
||||
notifyHandlers(active);
|
||||
bumpScopeCursor();
|
||||
}
|
||||
if (active.id !== prevId) notifyHandlers(active);
|
||||
return active;
|
||||
} catch (err) {
|
||||
lastError = err instanceof Error ? err.message : String(err);
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* Dexie bridge for the scope-change signal.
|
||||
*
|
||||
* Problem this solves: `getInScopeSpaceIds()` reads from plain Svelte
|
||||
* `$state` (`active-space.svelte.ts#active` + `current-user.ts#currentUserId`).
|
||||
* Dexie's `liveQuery` only re-runs when a Dexie table it read changes;
|
||||
* a `$state` assignment is invisible to it. The bootstrap sequence —
|
||||
* user opens `/wardrobe`, first querier runs before the active-space
|
||||
* is resolved from Better Auth, returns empty, setActiveSpace fires,
|
||||
* but the liveQuery never re-evaluates — left modules stuck on an
|
||||
* empty first result until an unrelated write woke them up.
|
||||
*
|
||||
* This module is the Dexie proxy. A single-row `_scopeCursor` table
|
||||
* (schema: `_scopeCursor: 'id'`, registered in Dexie v45) acts as the
|
||||
* change beacon. `bumpScopeCursor()` writes a new `{id:'active',
|
||||
* bumpedAt}` row every time scope state changes; `touchScopeCursor()`
|
||||
* reads the row on every scoped query so liveQuery subscribes to the
|
||||
* table. Net effect: a scope change triggers one infra write, every
|
||||
* dependent query re-runs with the fresh `getInScopeSpaceIds()`.
|
||||
*
|
||||
* Kept intentionally minimal (no encryption, no pending-change
|
||||
* tracking, not in SYNC_APP_MAP) — it's a liveness signal, not user
|
||||
* data.
|
||||
*/
|
||||
|
||||
import { db } from '../database';
|
||||
|
||||
const SCOPE_CURSOR_ID = 'active';
|
||||
|
||||
/**
|
||||
* Write a new `_scopeCursor` row so any in-flight liveQuery that
|
||||
* touched the table re-runs its querier. Swallows Dexie errors —
|
||||
* this is a best-effort signal, a missing bump only delays the
|
||||
* re-evaluation until the next Dexie write to a tracked table.
|
||||
* Safe to call before Dexie finishes opening; the write queues.
|
||||
*/
|
||||
export function bumpScopeCursor(): void {
|
||||
void db
|
||||
.table('_scopeCursor')
|
||||
.put({ id: SCOPE_CURSOR_ID, bumpedAt: Date.now() })
|
||||
.catch((err) => {
|
||||
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);
|
||||
}
|
||||
|
|
@ -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<T, PK>(tableName: string): Collection<T, PK> {
|
||||
// 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<T, PK>;
|
||||
const ids = getInScopeSpaceIds();
|
||||
const check = (record: unknown) => {
|
||||
|
|
@ -133,7 +127,6 @@ export function scopedForModule<T, PK>(
|
|||
* compound queries with `.or()`, `.and()`, `.reverse()` first.
|
||||
*/
|
||||
export function scopedAnd<T, PK>(collection: Collection<T, PK>): Collection<T, PK> {
|
||||
touchScopeCursor();
|
||||
const ids = getInScopeSpaceIds();
|
||||
return collection.and((record) => {
|
||||
const r = record as { spaceId?: unknown };
|
||||
|
|
@ -151,10 +144,6 @@ export function scopedAnd<T, PK>(collection: Collection<T, PK>): Collection<T, P
|
|||
* is a single field comparison on the one row returned.
|
||||
*/
|
||||
export async function scopedGet<T>(tableName: string, id: string | number): Promise<T | undefined> {
|
||||
// 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 };
|
||||
|
|
|
|||
|
|
@ -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<T>(
|
||||
querier: () => T | Promise<T>,
|
||||
|
|
@ -42,14 +59,15 @@ export function useScopedLiveQuery<T>(
|
|||
let error = $state<unknown>(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<T>(
|
|||
});
|
||||
|
||||
// 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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<LocalArticle, string>('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<string, string[]>()
|
||||
);
|
||||
|
|
@ -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<LocalArticle, string>('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<LocalArticle, string>('articles', 'articles').toArray(),
|
||||
scopedForModule<LocalHighlight, string>('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
|
||||
|
|
|
|||
|
|
@ -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<LocalBodyExercise, string>(
|
||||
'body',
|
||||
'bodyExercises'
|
||||
|
|
@ -143,7 +143,7 @@ export function useAllBodyExercises() {
|
|||
}
|
||||
|
||||
export function useAllBodyRoutines() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalBodyRoutine, string>('body', 'bodyRoutines').sortBy(
|
||||
'order'
|
||||
);
|
||||
|
|
@ -154,7 +154,7 @@ export function useAllBodyRoutines() {
|
|||
}
|
||||
|
||||
export function useAllBodyWorkouts() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalBodyWorkout, string>(
|
||||
'body',
|
||||
'bodyWorkouts'
|
||||
|
|
@ -166,7 +166,7 @@ export function useAllBodyWorkouts() {
|
|||
}
|
||||
|
||||
export function useAllBodySets() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalBodySet, string>('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<LocalBodySet>('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<LocalBodyMeasurement, string>(
|
||||
'body',
|
||||
'bodyMeasurements'
|
||||
|
|
@ -200,7 +200,7 @@ export function useAllBodyMeasurements() {
|
|||
}
|
||||
|
||||
export function useAllBodyChecks() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalBodyCheck, string>('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<LocalBodyPhase, string>('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<LocalMeal>('meals').where('date').aboveOrEqual(since).toArray();
|
||||
const visible = locals.filter((m) => !m.deletedAt);
|
||||
// Encrypted fields (description / portionSize / foods) get unwrapped
|
||||
|
|
|
|||
|
|
@ -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<LocalCampaign, string>(
|
||||
'broadcast',
|
||||
'broadcastCampaigns'
|
||||
|
|
@ -89,7 +89,7 @@ export function useAllCampaigns() {
|
|||
}
|
||||
|
||||
export function useAllTemplates() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const rows = await scopedForModule<LocalBroadcastTemplate, string>(
|
||||
'broadcast',
|
||||
'broadcastTemplates'
|
||||
|
|
|
|||
|
|
@ -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<LocalCalculation, string>(
|
||||
'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<LocalSavedFormula, string>(
|
||||
'calc',
|
||||
'savedFormulas'
|
||||
|
|
|
|||
|
|
@ -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<LocalCalendar, string>('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
|
||||
|
|
|
|||
|
|
@ -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<LocalConversation, string>('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<LocalConversation, string>('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<LocalTemplate, string>('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<LocalMessage>('messages')
|
||||
|
|
|
|||
|
|
@ -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<LocalCity, string>('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<LocalLocation, string>(
|
||||
'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<LocalFavorite, string>(
|
||||
'citycorners',
|
||||
'ccFavorites'
|
||||
|
|
|
|||
|
|
@ -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<ComicStory[]>(async () => {
|
||||
return useScopedLiveQuery<ComicStory[]>(async () => {
|
||||
const locals = await scopedForModule<LocalComicStory, string>(
|
||||
'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<ComicStory[]>(async () => {
|
||||
return useScopedLiveQuery<ComicStory[]>(async () => {
|
||||
const locals = await scopedForModule<LocalComicStory, string>('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<Image | null>(async () => {
|
||||
return useScopedLiveQuery<Image | null>(async () => {
|
||||
if (!imageId) return null;
|
||||
const locals = await scopedForModule<LocalImage, string>('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<ComicStory | null>(async () => {
|
||||
return useScopedLiveQuery<ComicStory | null>(async () => {
|
||||
if (!id) return null;
|
||||
const locals = await scopedForModule<LocalComicStory, string>('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<Image[]>(async () => {
|
||||
return useScopedLiveQuery<Image[]>(async () => {
|
||||
if (!storyId) return [];
|
||||
const locals = await scopedForModule<LocalImage, string>('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<ComicStory[]>(async () => {
|
||||
return useScopedLiveQuery<ComicStory[]>(async () => {
|
||||
if (!module || !entryId) return [];
|
||||
const locals = await scopedForModule<LocalComicStory, string>(
|
||||
'comic',
|
||||
|
|
|
|||
|
|
@ -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<LocalConversation[]>(async () => {
|
||||
return useScopedLiveQuery<LocalConversation[]>(async () => {
|
||||
try {
|
||||
const all = await scopedForModule<LocalConversation, string>(
|
||||
'companion',
|
||||
|
|
@ -22,7 +22,7 @@ export function useConversations() {
|
|||
}
|
||||
|
||||
export function useMessages(conversationId: string) {
|
||||
return useLiveQueryWithDefault<LocalMessage[]>(async () => {
|
||||
return useScopedLiveQuery<LocalMessage[]>(async () => {
|
||||
if (!conversationId) return [];
|
||||
try {
|
||||
return await db
|
||||
|
|
|
|||
|
|
@ -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<LocalContact, string>('contacts', 'contacts').toArray();
|
||||
const visible = applyVisibility(raw).filter((c) => !c.deletedAt);
|
||||
const decrypted = await decryptRecords('contacts', visible);
|
||||
|
|
|
|||
|
|
@ -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<LocalContextSpace, string>(
|
||||
'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<LocalDocument, string>('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<LocalDocument>('documents')
|
||||
.where('contextSpaceId')
|
||||
|
|
|
|||
|
|
@ -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<LocalDream, string>('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<LocalDreamSymbol, string>('dreams', 'dreamSymbols').toArray()
|
||||
).filter((s) => !s.deletedAt);
|
||||
|
|
|
|||
|
|
@ -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<LocalDrinkEntry>('drinkEntries')
|
||||
.orderBy('date')
|
||||
|
|
@ -58,7 +58,7 @@ export function useAllDrinkEntries() {
|
|||
}
|
||||
|
||||
export function useAllDrinkPresets() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalDrinkPreset, string>('drink', 'drinkPresets').sortBy(
|
||||
'order'
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<LocalSocialEvent, string>(
|
||||
'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<LocalSocialEvent, string>(
|
||||
'events',
|
||||
'socialEvents'
|
||||
|
|
@ -119,7 +119,7 @@ export function useUpcomingEvents() {
|
|||
|
||||
/** Past events. */
|
||||
export function usePastEvents() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalSocialEvent, string>(
|
||||
'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<LocalEventGuest, string>('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
|
||||
|
|
|
|||
|
|
@ -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<LocalTransaction, string>('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<LocalFinanceCategory, string>(
|
||||
'finance',
|
||||
'financeCategories'
|
||||
|
|
|
|||
|
|
@ -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<LocalFirst, string>('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<LocalFirst, string>('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<LocalFirst, string>('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<LocalFirst, string>('firsts', 'firsts').toArray()
|
||||
).filter((f) => !f.deletedAt && !f.isArchived && f.personIds?.includes(personId));
|
||||
|
|
|
|||
|
|
@ -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<LocalMeal, string>('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<MealWithNutrition | null> {
|
||||
|
|
@ -65,7 +65,7 @@ export async function loadMealById(id: string): Promise<MealWithNutrition | null
|
|||
|
||||
/** All goals, auto-updates on any change. */
|
||||
export function useAllGoals() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalGoal, string>('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<LocalFavorite, string>('food', 'foodFavorites').toArray();
|
||||
return locals.filter((f) => !f.deletedAt);
|
||||
}, [] as LocalFavorite[]);
|
||||
|
|
|
|||
|
|
@ -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<LocalGuide, string>('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<LocalSection>('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<LocalStep>('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<LocalRun, string>('guides', 'runs').toArray();
|
||||
const visible = all.filter((r) => !r.deletedAt);
|
||||
const map = new Map<string, Run>();
|
||||
|
|
|
|||
|
|
@ -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<LocalHabit, string>('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<LocalHabitLog, string>('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<LocalHabitLog>('habitLogs')
|
||||
.where('habitId')
|
||||
|
|
|
|||
|
|
@ -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<LocalCollection, string>(
|
||||
'inventory',
|
||||
'invCollections'
|
||||
|
|
@ -171,7 +171,7 @@ export function useAllCollections() {
|
|||
}
|
||||
|
||||
export function useAllItems() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const visible = (
|
||||
await scopedForModule<LocalItem, string>('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<LocalLocation, string>(
|
||||
'inventory',
|
||||
'invLocations'
|
||||
|
|
@ -191,7 +191,7 @@ export function useAllLocations() {
|
|||
}
|
||||
|
||||
export function useAllCategories() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalCategory, string>(
|
||||
'inventory',
|
||||
'invCategories'
|
||||
|
|
|
|||
|
|
@ -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<LocalInvoice, string>('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<LocalInvoiceClient, string>(
|
||||
'invoices',
|
||||
'invoiceClients'
|
||||
|
|
|
|||
|
|
@ -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<LocalJournalEntry, string>('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<LocalJournalEntry>('journalEntries').get(id);
|
||||
if (!local || local.deletedAt) return null;
|
||||
|
|
|
|||
|
|
@ -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<LocalMailDraft, string>('mail', 'mailDrafts').toArray();
|
||||
const visible = locals.filter((d) => !d.deletedAt);
|
||||
const decrypted = await decryptRecords('mailDrafts', visible);
|
||||
|
|
|
|||
|
|
@ -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<LocalMeditatePreset>('meditatePresets')
|
||||
.orderBy('order')
|
||||
|
|
@ -79,7 +79,7 @@ export function useAllPresets() {
|
|||
}
|
||||
|
||||
export function useAllSessions() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await db
|
||||
.table<LocalMeditateSession>('meditateSessions')
|
||||
.orderBy('startedAt')
|
||||
|
|
@ -92,7 +92,7 @@ export function useAllSessions() {
|
|||
}
|
||||
|
||||
export function useSettings() {
|
||||
return useLiveQueryWithDefault(
|
||||
return useScopedLiveQuery(
|
||||
async () => {
|
||||
const locals = await scopedForModule<LocalMeditateSettings, string>(
|
||||
'meditate',
|
||||
|
|
|
|||
|
|
@ -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<LocalMemo, string>('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<LocalMemo, string>('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<LocalMemory>('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<LocalMemoTag, string>('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<LocalSpace, string>('memoro', 'memoroSpaces').toArray();
|
||||
return locals.filter((s) => !s.deletedAt).map(toSpace);
|
||||
}, [] as Space[]);
|
||||
|
|
|
|||
|
|
@ -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<LocalMoodEntry, string>('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<LocalMoodSettings, string>(
|
||||
'mood',
|
||||
|
|
|
|||
|
|
@ -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<LocalMood, string>('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<LocalSequence, string>('moodlit', 'sequences').toArray();
|
||||
return locals.filter((s) => !s.deletedAt);
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -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<LocalSong, string>('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<LocalPlaylist, string>(
|
||||
'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<LocalPlaylistSong, string>(
|
||||
'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<LocalProject, string>('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<LocalMarker>('markers').where('beatId').equals(beatId).toArray();
|
||||
return locals.filter((m) => !m.deletedAt).sort((a, b) => a.startTime - b.startTime);
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<LocalPeriod, string>('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<LocalPeriod, string>('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<LocalPeriodDayLog, string>('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<LocalPeriodDayLog>('periodDayLogs')
|
||||
|
|
@ -116,7 +116,7 @@ export function useDayLog(date: string) {
|
|||
}
|
||||
|
||||
export function useAllSymptoms() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalPeriodSymptom, string>(
|
||||
'period',
|
||||
'periodSymptoms'
|
||||
|
|
|
|||
|
|
@ -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<LocalAlbum, string>('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<LocalAlbumItem, string>('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<LocalFavorite, string>(
|
||||
'photos',
|
||||
'photoFavorites'
|
||||
|
|
|
|||
|
|
@ -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<LocalImage, string>('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<LocalImage, string>('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<LocalBoard, string>('picture', 'boards').toArray();
|
||||
const allItems = await scopedForModule<LocalBoardItem, string>(
|
||||
'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<LocalImageTag, string>('picture', 'imageTags').toArray();
|
||||
}, [] as LocalImageTag[]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<LocalPlant, string>('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<LocalPlantPhoto, string>(
|
||||
'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<LocalWateringSchedule, string>(
|
||||
'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<LocalWateringLog, string>(
|
||||
'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<LocalPlantTag, string>('plants', 'plantTags').toArray();
|
||||
return locals.filter((t) => !t.deletedAt);
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -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<LocalPlaygroundSnippet>('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<LocalPlaygroundConversation, string>(
|
||||
'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
|
||||
|
|
|
|||
|
|
@ -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<LocalDeck, string>('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<LocalSlide>('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<LocalDeck>('presiDecks').get(id);
|
||||
if (!local || local.deletedAt) return null;
|
||||
|
|
|
|||
|
|
@ -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<UserContext | null>(async () => {
|
||||
return useScopedLiveQuery<UserContext | null>(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<MeImage[]>(async () => {
|
||||
return useScopedLiveQuery<MeImage[]>(async () => {
|
||||
const locals = await scopedForModule<LocalMeImage, string>('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<MeImage[]>(async () => {
|
||||
return useScopedLiveQuery<MeImage[]>(async () => {
|
||||
const locals = await scopedForModule<LocalMeImage, string>('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<MeImage[]>(async () => {
|
||||
return useScopedLiveQuery<MeImage[]>(async () => {
|
||||
const locals = await scopedForModule<LocalMeImage, string>('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<MeImage | null>(async () => {
|
||||
return useScopedLiveQuery<MeImage | null>(async () => {
|
||||
const locals = await scopedForModule<LocalMeImage, string>('profile', 'meImages')
|
||||
.and((row) => row.primaryFor === slot)
|
||||
.toArray();
|
||||
|
|
|
|||
|
|
@ -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<LocalCollection, string>(
|
||||
'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<LocalQuestion, string>('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<LocalAnswer, string>('questions', 'answers').toArray();
|
||||
const visible = locals.filter((a) => !a.deletedAt && a.questionId === questionId);
|
||||
const decrypted = await decryptRecords('answers', visible);
|
||||
|
|
|
|||
|
|
@ -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<LocalQuiz, string>('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<LocalQuiz>('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<LocalQuizQuestion>('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<LocalQuizAttempt>('quizAttempts').where('quizId').equals(quizId).toArray()
|
||||
).filter((a) => !a.deletedAt);
|
||||
|
|
|
|||
|
|
@ -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<LocalRecipe, string>('recipes', 'recipes').toArray();
|
||||
const visible = locals.filter((r) => !r.deletedAt);
|
||||
const decrypted = await decryptRecords('recipes', visible);
|
||||
|
|
|
|||
|
|
@ -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<LocalSkill, string>('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<LocalActivity, string>(
|
||||
'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<LocalAchievement, string>(
|
||||
'skilltree',
|
||||
'achievements'
|
||||
|
|
|
|||
|
|
@ -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<LocalSleepEntry, string>(
|
||||
'sleep',
|
||||
'sleepEntries'
|
||||
|
|
@ -96,7 +96,7 @@ export function useAllSleepEntries() {
|
|||
}
|
||||
|
||||
export function useAllSleepHygieneLogs() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalSleepHygieneLog, string>(
|
||||
'sleep',
|
||||
'sleepHygieneLogs'
|
||||
|
|
@ -107,7 +107,7 @@ export function useAllSleepHygieneLogs() {
|
|||
}
|
||||
|
||||
export function useAllSleepHygieneChecks() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalSleepHygieneCheck, string>(
|
||||
'sleep',
|
||||
'sleepHygieneChecks'
|
||||
|
|
@ -119,7 +119,7 @@ export function useAllSleepHygieneChecks() {
|
|||
}
|
||||
|
||||
export function useSleepSettings() {
|
||||
return useLiveQueryWithDefault(
|
||||
return useScopedLiveQuery(
|
||||
async () => {
|
||||
const locals = await scopedForModule<LocalSleepSettings, string>(
|
||||
'sleep',
|
||||
|
|
|
|||
|
|
@ -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<LocalFile, string>('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<LocalFolder, string>(
|
||||
'storage',
|
||||
'storageFolders'
|
||||
|
|
|
|||
|
|
@ -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<LocalStretchExercise, string>(
|
||||
'stretch',
|
||||
'stretchExercises'
|
||||
|
|
@ -121,7 +121,7 @@ export function useAllStretchExercises() {
|
|||
}
|
||||
|
||||
export function useAllStretchRoutines() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalStretchRoutine, string>(
|
||||
'stretch',
|
||||
'stretchRoutines'
|
||||
|
|
@ -133,7 +133,7 @@ export function useAllStretchRoutines() {
|
|||
}
|
||||
|
||||
export function useAllStretchSessions() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalStretchSession, string>(
|
||||
'stretch',
|
||||
'stretchSessions'
|
||||
|
|
@ -145,7 +145,7 @@ export function useAllStretchSessions() {
|
|||
}
|
||||
|
||||
export function useAllStretchAssessments() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalStretchAssessment, string>(
|
||||
'stretch',
|
||||
'stretchAssessments'
|
||||
|
|
@ -159,7 +159,7 @@ export function useAllStretchAssessments() {
|
|||
}
|
||||
|
||||
export function useAllStretchReminders() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalStretchReminder, string>(
|
||||
'stretch',
|
||||
'stretchReminders'
|
||||
|
|
|
|||
|
|
@ -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<LocalAlarm, string>('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<LocalCountdownTimer, string>(
|
||||
'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<LocalWorldClock>('timeWorldClocks')
|
||||
.orderBy('sortOrder')
|
||||
|
|
|
|||
|
|
@ -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<LocalBoardView, string>('todo', 'boardViews').sortBy(
|
||||
'order'
|
||||
);
|
||||
|
|
@ -81,14 +81,14 @@ export function useAllBoardViews() {
|
|||
}
|
||||
|
||||
export function useAllReminders() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalReminder, string>('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<LocalTodoProject, string>('todo', 'todoProjects').sortBy(
|
||||
'order'
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<LocalLink, string>('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<LocalTag, string>('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<LocalFolder, string>('uload', 'uloadFolders').sortBy(
|
||||
'order'
|
||||
);
|
||||
|
|
@ -190,14 +190,14 @@ export function useAllFolders() {
|
|||
}
|
||||
|
||||
export function useAllLinkTags() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalLinkTag, string>('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<LocalLink>('links').get(id);
|
||||
|
|
|
|||
|
|
@ -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<Garment[]>(async () => {
|
||||
return useScopedLiveQuery<Garment[]>(async () => {
|
||||
const locals = await scopedForModule<LocalWardrobeGarment, string>(
|
||||
'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<Garment[]>(async () => {
|
||||
return useScopedLiveQuery<Garment[]>(async () => {
|
||||
const locals = await scopedForModule<LocalWardrobeGarment, string>(
|
||||
'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<Garment | null>(async () => {
|
||||
return useScopedLiveQuery<Garment | null>(async () => {
|
||||
if (!id) return null;
|
||||
const locals = await scopedForModule<LocalWardrobeGarment, string>(
|
||||
'wardrobe',
|
||||
|
|
@ -78,7 +78,7 @@ export function useGarment(id: string | null) {
|
|||
|
||||
/** All non-archived outfits in the active space. */
|
||||
export function useAllOutfits() {
|
||||
return useLiveQueryWithDefault<Outfit[]>(async () => {
|
||||
return useScopedLiveQuery<Outfit[]>(async () => {
|
||||
const locals = await scopedForModule<LocalWardrobeOutfit, string>(
|
||||
'wardrobe',
|
||||
'wardrobeOutfits'
|
||||
|
|
@ -92,7 +92,7 @@ export function useAllOutfits() {
|
|||
}
|
||||
|
||||
export function useOutfitsByOccasion(occasion: OutfitOccasion) {
|
||||
return useLiveQueryWithDefault<Outfit[]>(async () => {
|
||||
return useScopedLiveQuery<Outfit[]>(async () => {
|
||||
const locals = await scopedForModule<LocalWardrobeOutfit, string>('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<Outfit | null>(async () => {
|
||||
return useScopedLiveQuery<Outfit | null>(async () => {
|
||||
if (!id) return null;
|
||||
const locals = await scopedForModule<LocalWardrobeOutfit, string>('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<Image[]>(async () => {
|
||||
return useScopedLiveQuery<Image[]>(async () => {
|
||||
if (!outfitId) return [];
|
||||
const locals = await scopedForModule<LocalImage, string>('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<Image[]>(async () => {
|
||||
return useScopedLiveQuery<Image[]>(async () => {
|
||||
if (!garmentId) return [];
|
||||
const locals = await scopedForModule<LocalImage, string>('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<Outfit[]>(async () => {
|
||||
return useScopedLiveQuery<Outfit[]>(async () => {
|
||||
if (!garmentId) return [];
|
||||
const locals = await scopedForModule<LocalWardrobeOutfit, string>(
|
||||
'wardrobe',
|
||||
|
|
|
|||
|
|
@ -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<LocalWebsite, string>('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<LocalWebsitePage>('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<LocalWebsiteBlock>('websiteBlocks').toArray();
|
||||
const visible = locals.filter((b) => !b.deletedAt);
|
||||
return visible.map(toWebsiteBlock).sort((a, b) => a.order - b.order);
|
||||
|
|
|
|||
|
|
@ -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<WeatherLocation, string>('wetter', 'wetterLocations').sortBy('order'),
|
||||
[] as WeatherLocation[]
|
||||
);
|
||||
}
|
||||
|
||||
export function useDefaultLocation() {
|
||||
return useLiveQueryWithDefault(
|
||||
return useScopedLiveQuery(
|
||||
() => db.table<WeatherLocation>('wetterLocations').where('isDefault').equals(1).first(),
|
||||
undefined as WeatherLocation | undefined
|
||||
);
|
||||
}
|
||||
|
||||
export function useSettings() {
|
||||
return useLiveQueryWithDefault(
|
||||
return useScopedLiveQuery(
|
||||
async () => {
|
||||
const settings = await db.table<WeatherSettings>('wetterSettings').get('default');
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -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<LocalWish, string>('wishes', 'wishesItems').sortBy(
|
||||
'order'
|
||||
);
|
||||
|
|
@ -79,7 +79,7 @@ export function useAllWishes() {
|
|||
}
|
||||
|
||||
export function useAllLists() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const locals = await scopedForModule<LocalWishList, string>('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<LocalPriceCheck>('wishesPriceChecks')
|
||||
.where('wishId')
|
||||
|
|
|
|||
|
|
@ -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<LocalDraft, string>('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<LocalDraft>('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<LocalDraftVersion>('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<LocalDraftVersion>('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<LocalDraft>('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<LocalGeneration>('writingGenerations')
|
||||
|
|
@ -188,7 +188,7 @@ export function useGenerationsForDraft(draftId: string) {
|
|||
}
|
||||
|
||||
export function useAllStyles() {
|
||||
return useLiveQueryWithDefault(async () => {
|
||||
return useScopedLiveQuery(async () => {
|
||||
const rows = await scopedForModule<LocalWritingStyle, string>(
|
||||
'writing',
|
||||
'writingStyles'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue