Q184: the page, its sections, filters and selections survive page switches and restarts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2790d88186
commit
72cf57ce3a
27 files changed
+235
-38
No files matched your search
@@ -15,6 +15,7 @@ export const IPC_CONTRACTS: { [K in InvokedMethod]: z.ZodType<InputOf<K>> } = {
|
||||
loadSnapshot: noInput,
|
||||
relaunchApp: noInput,
|
||||
reportUnsaved: z.strictObject({ unsaved: z.boolean() }),
|
||||
saveViewState: z.strictObject({ key: z.string().min(1).max(200), value: z.json().refine((value) => JSON.stringify(value).length <= 4_000, "View state value is too large.") }),
|
||||
runningSessionCount: noInput,
|
||||
pickDirectory: noInput,
|
||||
listConnectorCatalog: noInput,
|
||||
|
||||
@@ -43,6 +43,7 @@ import { deleteWorkspace, setPrimaryWorkspace, updateWorkspace } from "../servic
|
||||
import { setProviderModel } from "../services/provider-default-model";
|
||||
import { completeSetup, saveOnboardingDraft } from "../services/onboarding-progress";
|
||||
import { setUnsavedInput } from "./unsaved-guard";
|
||||
import { saveViewState } from "../services/view-state";
|
||||
import { providerDisconnectImpact } from "../services/provider-disconnect-impact";
|
||||
|
||||
export interface IpcDeps {
|
||||
@@ -152,6 +153,14 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
}
|
||||
});
|
||||
handle("completeSetup", (input) => mutate(() => completeSetup(deps.connection.db, input.orgId)));
|
||||
handle("saveViewState", (input) => {
|
||||
try {
|
||||
saveViewState(deps.connection.db, input.key, input.value);
|
||||
return ok(null);
|
||||
} catch (error) {
|
||||
return fail("view_state_failed", error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
});
|
||||
handle("reportUnsaved", (input) => {
|
||||
setUnsavedInput(input.unsaved);
|
||||
return ok(null);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Where the operator was (page, section, filters, selected records), so switching away or restarting returns there.
|
||||
export const viewStateSchema = `
|
||||
alter table app_state add column view_state_json text;
|
||||
|
||||
insert into schema_migrations(version, name, applied_at)
|
||||
values (106, 'view_state', datetime('now'));
|
||||
`;
|
||||
@@ -104,6 +104,7 @@ import { goalDueDateSchema } from "./migrations/102-goal-due-date";
|
||||
import { workVerdictsSchema } from "./migrations/103-work-verdicts";
|
||||
import { workLanguageSchema } from "./migrations/104-work-language";
|
||||
import { onboardingProgressSchema } from "./migrations/105-onboarding-progress";
|
||||
import { viewStateSchema } from "./migrations/106-view-state";
|
||||
|
||||
interface Migration {
|
||||
version: number;
|
||||
@@ -217,6 +218,7 @@ const MIGRATIONS: Migration[] = [
|
||||
{ version: 103, name: "work_verdicts", sql: workVerdictsSchema },
|
||||
{ version: 104, name: "work_language", sql: workLanguageSchema },
|
||||
{ version: 105, name: "onboarding_progress", sql: onboardingProgressSchema },
|
||||
{ version: 106, name: "view_state", sql: viewStateSchema },
|
||||
];
|
||||
|
||||
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { loadViewState } from "./view-state";
|
||||
import { loadOnboardingDraft } from "./onboarding-progress";
|
||||
import type { DatabaseSync, StatementSync } from "node:sqlite";
|
||||
import type { AppSnapshot } from "../../shared/domain";
|
||||
@@ -88,6 +89,7 @@ export class SnapshotService {
|
||||
activeOrgId,
|
||||
uiPreferences: this.resolveUiPreferences(),
|
||||
onboardingDraft: loadOnboardingDraft(this.db),
|
||||
viewState: loadViewState(this.db),
|
||||
autonomousPaused: this.resolveAutonomousPaused(),
|
||||
integrationKeys: new IntegrationKeyStore(this.db).statuses(),
|
||||
organizations,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
|
||||
export function loadViewState(db: DatabaseSync): Record<string, unknown> {
|
||||
const row = db.prepare("select view_state_json from app_state where id = 1").get() as { view_state_json: string | null } | undefined;
|
||||
return row?.view_state_json ? JSON.parse(row.view_state_json) as Record<string, unknown> : {};
|
||||
}
|
||||
|
||||
/** Remembers one piece of where the operator was; null forgets it. */
|
||||
export function saveViewState(db: DatabaseSync, key: string, value: unknown): void {
|
||||
const path = `$.${JSON.stringify(key)}`;
|
||||
db.prepare(`
|
||||
insert into app_state(id, view_state_json, updated_at) values (1, json_object(), ?)
|
||||
on conflict(id) do nothing
|
||||
`).run(new Date().toISOString());
|
||||
if (value === null) {
|
||||
db.prepare("update app_state set view_state_json = json_remove(coalesce(view_state_json, json_object()), ?) where id = 1").run(path);
|
||||
return;
|
||||
}
|
||||
db.prepare("update app_state set view_state_json = json_set(coalesce(view_state_json, json_object()), ?, json(?)) where id = 1").run(path, JSON.stringify(value));
|
||||
}
|
||||
Reference in new issue
Block a user