feat(idaa): add PersistedState store for idaa_loc (not yet wired in)

Creates ae_idaa_stores__idaa_loc.svelte.ts — the Svelte 5 PersistedState
replacement for the idaa_loc persisted() store in ae_idaa_stores.ts.

Mirrors the exact shape of idaa_local_data_struct with full TypeScript
interfaces (IdaaArchivesLoc, IdaaBbLoc, IdaaRecoveryMeetingsLoc,
IdaaLocState). Drops __version, name, and title (not runtime state).

Same localStorage key (ae_idaa_loc) — existing data loads cleanly.
Not wired into consumers yet; pending review before migration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-11 17:22:54 -04:00
parent 582b43da34
commit 7fc073053b

View File

@@ -0,0 +1,149 @@
import { PersistedState } from 'runed';
// ---- Types -------------------------------------------------------------------
interface IdaaArchivesLoc {
enabled: string; // 'all' | 'disabled' | 'enabled'
hidden: string; // 'all' | 'hidden' | 'not_hidden'
limit: number;
offset: number;
edit_kv: Record<string, any>; // Tracks which archive objects are being edited.
edit__archive_obj: any;
edit__archive_content_obj: any;
}
interface IdaaBbLoc {
enabled: string; // 'all' | 'disabled' | 'enabled'
hidden: string; // 'all' | 'hidden' | 'not_hidden'
limit: number;
offset: number;
edit_kv: Record<string, any>; // Tracks which post objects are being edited.
edit__post_obj: any;
edit__post_comment_obj: any;
show_list__post_obj_li: boolean;
qry__enabled: string;
qry__hidden: string;
qry__limit: number;
qry__offset: number;
qry__order_by: string; // For the IDB index query
qry__order_by_li: Record<string, string>; // For the SQL query
}
interface IdaaRecoveryMeetingsLoc {
edit_kv: Record<string, any>; // Tracks which meeting objects are being edited.
edit__event_obj: any;
qry__enabled: string;
qry__hidden: string;
qry__limit: number;
qry__order_by: string; // For the IDB index query; name, updated_on/created_on
qry__order_by_li: Record<string, string>; // For the SQL query
qry__offset: number;
qry__fulltext_str: string | null;
qry__physical: string | null;
qry__type: string | null;
qry__virtual: string | null;
// When true, only show meetings the member has starred. Favorites are stored
// server-side in event.mod_meetings_json.favorite (array of Novi UUIDs), so
// they persist across browsers without requiring a Novi API write capability.
qry__favorites_only: boolean;
// Collapse the "Meeting Info" data store panel. Persisted so preference survives reloads.
ds_info_collapsed: boolean;
}
export interface IdaaLocState {
novi_uuid: string | null;
novi_email: string | null;
novi_full_name: string | null;
// True after a successful Novi API verification (UUID confirmed to be a real Novi member).
// False on load, on verification failure, or for non-Novi sign-in paths.
novi_verified: boolean;
// Timestamp (ms since epoch) when the last successful verification occurred.
// Used to cache verification results and avoid repeated Novi API calls.
novi_verified_ts: number | null;
// Populated from $ae_loc.site_cfg_json at IDAA layout mount — not managed here.
// See routes/idaa/(idaa)/+layout.svelte for the override logic.
novi_admin_li: string[];
novi_trusted_li: string[];
novi_jitsi_mod_li: string[];
archives: IdaaArchivesLoc;
bb: IdaaBbLoc;
recovery_meetings: IdaaRecoveryMeetingsLoc;
}
// ---- Defaults ----------------------------------------------------------------
export const idaa_loc_defaults: IdaaLocState = {
novi_uuid: null,
novi_email: null,
novi_full_name: null,
novi_verified: false,
novi_verified_ts: null,
novi_admin_li: [],
novi_trusted_li: [],
novi_jitsi_mod_li: [],
archives: {
enabled: 'enabled',
hidden: 'not_hidden',
limit: 150,
offset: 0,
edit_kv: {},
edit__archive_obj: null,
edit__archive_content_obj: null
},
bb: {
enabled: 'enabled',
hidden: 'not_hidden',
limit: 50,
offset: 0,
edit_kv: {},
edit__post_obj: null,
edit__post_comment_obj: null,
show_list__post_obj_li: true,
qry__enabled: 'enabled',
qry__hidden: 'not_hidden',
qry__limit: 25,
qry__offset: 0,
qry__order_by: 'updated_on',
qry__order_by_li: { updated_on: 'DESC', created_on: 'DESC' }
},
recovery_meetings: {
edit_kv: {},
edit__event_obj: null,
qry__enabled: 'enabled',
qry__hidden: 'not_hidden',
qry__limit: 100,
qry__order_by: 'updated_on',
qry__order_by_li: {
priority: 'DESC',
sort: 'DESC',
updated_on: 'DESC',
created_on: 'DESC',
name: 'ASC'
},
qry__offset: 0,
qry__fulltext_str: null,
qry__physical: null,
qry__type: null,
qry__virtual: null,
qry__favorites_only: false,
ds_info_collapsed: false
}
};
// ---- Store -------------------------------------------------------------------
// Note: the top-level spread in deserialize ensures new top-level fields get
// their defaults after a browser upgrade. Nested objects (archives, bb,
// recovery_meetings) are replaced wholesale from localStorage — if a new field
// is added to a nested object, it will not auto-populate until the user clears
// their storage. This is the same trade-off as the events sub-stores.
export const idaa_loc = new PersistedState('ae_idaa_loc', idaa_loc_defaults, {
serializer: {
serialize: JSON.stringify,
deserialize: (raw: string) => ({ ...idaa_loc_defaults, ...JSON.parse(raw) })
}
});