From 196afc21d40e41b61656704976a979feac35b594 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Mon, 20 Apr 2026 22:40:37 +0100 Subject: [PATCH] feat(base): add BaseViewDraft type and view-draft atom family --- .../features/base/atoms/view-draft-atom.ts | 22 +++++++++++++++++++ .../src/features/base/types/base.types.ts | 11 ++++++++++ 2 files changed, 33 insertions(+) create mode 100644 apps/client/src/features/base/atoms/view-draft-atom.ts diff --git a/apps/client/src/features/base/atoms/view-draft-atom.ts b/apps/client/src/features/base/atoms/view-draft-atom.ts new file mode 100644 index 00000000..c99133da --- /dev/null +++ b/apps/client/src/features/base/atoms/view-draft-atom.ts @@ -0,0 +1,22 @@ +import { atomFamily, atomWithStorage } from "jotai/utils"; +import { BaseViewDraft } from "@/features/base/types/base.types"; + +export type ViewDraftKey = { + userId: string; + baseId: string; + viewId: string; +}; + +export const viewDraftStorageKey = (k: ViewDraftKey) => + `docmost:base-view-draft:v1:${k.userId}:${k.baseId}:${k.viewId}`; + +// `atomWithStorage` handles JSON serialization, cross-tab sync via the +// `storage` event, and lazy first-read out of the box. `atomFamily`'s +// comparator ensures the same triple resolves to the same atom instance +// across renders, so identity-equality cache hits in Jotai still work. +export const viewDraftAtomFamily = atomFamily( + (k: ViewDraftKey) => + atomWithStorage(viewDraftStorageKey(k), null), + (a, b) => + a.userId === b.userId && a.baseId === b.baseId && a.viewId === b.viewId, +); diff --git a/apps/client/src/features/base/types/base.types.ts b/apps/client/src/features/base/types/base.types.ts index 563883bf..04647dc2 100644 --- a/apps/client/src/features/base/types/base.types.ts +++ b/apps/client/src/features/base/types/base.types.ts @@ -299,3 +299,14 @@ export type UpdatePropertyResult = { // when the job finished migrating cells. jobId: string | null; }; + +// Local-first draft of filter / sort tweaks for a single view, stored in +// localStorage scoped to (userId, baseId, viewId). An absent `filter` or +// `sorts` field means "inherit the baseline for that axis". See +// `.claude/superpowers/specs/2026-04-20-base-view-draft-design.md`. +export type BaseViewDraft = { + filter?: FilterGroup; + sorts?: ViewSortConfig[]; + // ISO timestamp written on each put; diagnostic only, not read by logic. + updatedAt: string; +};