From 46f9292c057df1a07be0cfaa3bd2e14f7390c196 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Fri, 24 Apr 2026 00:29:23 +0100 Subject: [PATCH] feat(base): subscribe to formula WS events on client --- .../base/atoms/formula-recompute-atom.ts | 3 ++ .../features/base/hooks/use-base-socket.ts | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 apps/client/src/features/base/atoms/formula-recompute-atom.ts diff --git a/apps/client/src/features/base/atoms/formula-recompute-atom.ts b/apps/client/src/features/base/atoms/formula-recompute-atom.ts new file mode 100644 index 000000000..320c89beb --- /dev/null +++ b/apps/client/src/features/base/atoms/formula-recompute-atom.ts @@ -0,0 +1,3 @@ +import { atom } from "jotai"; + +export const formulaRecomputeAtom = atom>({}); diff --git a/apps/client/src/features/base/hooks/use-base-socket.ts b/apps/client/src/features/base/hooks/use-base-socket.ts index 020e2c70c..c0c4c65c9 100644 --- a/apps/client/src/features/base/hooks/use-base-socket.ts +++ b/apps/client/src/features/base/hooks/use-base-socket.ts @@ -8,6 +8,7 @@ import { IBaseView, } from "@/features/base/types/base.types"; import { selectedRowIdsAtom } from "@/features/base/atoms/base-atoms"; +import { formulaRecomputeAtom } from "@/features/base/atoms/formula-recompute-atom"; import { IPagination } from "@/lib/types"; type BaseRowCreated = { @@ -69,12 +70,39 @@ type BaseViewEvent = { viewId?: string; }; +type BaseRowsUpdated = { + operation: "base:rows:updated"; + baseId: string; + rowIds: string[]; + propertyIds: string[]; + requestId?: string | null; +}; + +type BaseFormulaRecomputeStarted = { + operation: "base:formula:recompute:started"; + baseId: string; + propertyIds: string[]; + jobId: string; +}; + +type BaseFormulaRecomputeCompleted = { + operation: "base:formula:recompute:completed"; + baseId: string; + propertyIds: string[]; + jobId: string; + processed: number; + errored: number; +}; + type BaseInboundEvent = | BaseRowCreated | BaseRowUpdated | BaseRowDeleted | BaseRowsDeleted | BaseRowReordered + | BaseRowsUpdated + | BaseFormulaRecomputeStarted + | BaseFormulaRecomputeCompleted | BasePropertyEvent | BaseViewEvent | { operation: string; baseId: string }; @@ -240,6 +268,32 @@ export function useBaseSocket(baseId: string | undefined): void { ); break; } + case "base:rows:updated": { + // Formula recompute wrote new cell values on the server. The event + // only carries row IDs, so invalidate the rows query to refetch. + queryClient.invalidateQueries({ queryKey: ["base-rows", baseId] }); + break; + } + case "base:formula:recompute:started": { + const e = event as BaseFormulaRecomputeStarted; + const store = getDefaultStore(); + store.set(formulaRecomputeAtom, { + ...store.get(formulaRecomputeAtom), + [e.jobId]: e.propertyIds, + }); + break; + } + case "base:formula:recompute:completed": { + const e = event as BaseFormulaRecomputeCompleted; + const store = getDefaultStore(); + const current = store.get(formulaRecomputeAtom); + if (e.jobId in current) { + const next = { ...current }; + delete next[e.jobId]; + store.set(formulaRecomputeAtom, next); + } + break; + } case "base:property:created": case "base:property:updated": case "base:property:deleted":