summaryrefslogtreecommitdiffhomepage
path: root/src/adapters
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-09-07 19:16:08 +0900
committerAdam Malczewski <[email protected]>2026-09-07 19:16:08 +0900
commit9c201c60a727106f1e3bf8c2af5f86aa7427098f (patch)
tree37c0fa07913154386785126bd75093866f05c975 /src/adapters
parent61733924352285df6c44f66edf69ea6f738fae3e (diff)
downloaddispatch-web-9c201c60a727106f1e3bf8c2af5f86aa7427098f.tar.gz
dispatch-web-9c201c60a727106f1e3bf8c2af5f86aa7427098f.zip
feat(workspaces): sync the home tile layout per device orientation
The Win8-Start home grid's arrangement now syncs from the backend's home-tile-layout plugin: ONE shared document per device orientation (wide = landscape/desktop, tall = portrait/mobile) — every landscape device renders the same layout, every portrait device the same other one. localStorage persistence is retired (no migration — devices start from the shared document; the first arrangement creates it). - adapter/tile-layout-http.ts: the injected HTTP edge (GET/PUT/DELETE /home/tile-layout/:mode), the features/layout adapter's pattern - tile-layout-store: server-backed, keyed by geometry mode — boot + orientation-change probes (stale in-flight responses never land), locally-applied edits + debounced full-document PUT with rollback to the last server document, WS push filtered to the active orientation, quiet ephemeral degrade when the backend is unreachable (no error UI), sync gated while a probe is in flight - WorkspacesHome locks the store's orientation to the resolved grid geometry (the landscape/portrait split IS the device-type split) - app store: the tile store lives beside the layout store; the WS dispatch feeds 'tile-layout' broadcasts into it; 'tiles' prop from App.svelte (the page entry owns the httpBase) - ws adapter: the 'tile-layout' server message (parse guard) + socket handler; wire conformance case
Diffstat (limited to 'src/adapters')
-rw-r--r--src/adapters/ws/index.ts9
-rw-r--r--src/adapters/ws/logic.ts19
2 files changed, 28 insertions, 0 deletions
diff --git a/src/adapters/ws/index.ts b/src/adapters/ws/index.ts
index 3713c8c..4dc0104 100644
--- a/src/adapters/ws/index.ts
+++ b/src/adapters/ws/index.ts
@@ -6,6 +6,7 @@ import type {
ConversationStatusChangedMessage,
ExtensionsChangedMessage,
LayoutChangedMessage,
+ TileLayoutChangedMessage,
WsClientMessage,
} from "@dispatch/transport-contract";
import type { SurfaceServerMessage } from "@dispatch/ui-contract";
@@ -40,6 +41,12 @@ export interface SurfaceSocketOptions {
* tree for one workspace. The consumer filters by its active workspace.
*/
onLayout?: (msg: LayoutChangedMessage) => void;
+ /**
+ * Broadcast after every successful home tile-layout write: the FULL
+ * document for one device orientation (wide/tall). The consumer filters by
+ * its active geometry mode.
+ */
+ onTileLayout?: (msg: TileLayoutChangedMessage) => void;
onReopen?: () => void;
socketFactory?: (url: string) => WebSocketLike;
}
@@ -92,6 +99,8 @@ export function createSurfaceSocket(opts: SurfaceSocketOptions): SurfaceSocketHa
opts.onExtensionsChanged?.(msg as ExtensionsChangedMessage);
} else if (msg.type === "layout") {
opts.onLayout?.(msg as LayoutChangedMessage);
+ } else if (msg.type === "tile-layout") {
+ opts.onTileLayout?.(msg as TileLayoutChangedMessage);
} else {
opts.onMessage(msg as SurfaceServerMessage);
}
diff --git a/src/adapters/ws/logic.ts b/src/adapters/ws/logic.ts
index 6cbc559..4dbd173 100644
--- a/src/adapters/ws/logic.ts
+++ b/src/adapters/ws/logic.ts
@@ -6,6 +6,7 @@ import type {
ConversationStatusChangedMessage,
ExtensionsChangedMessage,
LayoutChangedMessage,
+ TileLayoutChangedMessage,
WsClientMessage,
WsServerMessage,
} from "@dispatch/transport-contract";
@@ -28,6 +29,7 @@ const VALID_SERVER_TYPES = new Set([
"conversation.compacted",
"extensions-changed",
"layout",
+ "tile-layout",
]);
/** Serialize a client message to a JSON string for the wire. */
@@ -206,6 +208,23 @@ export function parseServerMessage(data: string): WsServerMessage | null {
};
return msg;
}
+ case "tile-layout": {
+ // Home tile-layout broadcast (the synced home grid): the FULL document
+ // for ONE device orientation. Light shape checks here (the document's
+ // structural validation is the backend's write-time job); the store
+ // filters by its active geometry mode.
+ if (parsed.mode !== "wide" && parsed.mode !== "tall") return null;
+ const data = parsed.data;
+ if (data === null || !isRecord(data) || data.version !== 2 || !Array.isArray(data.tiles)) {
+ return null;
+ }
+ const msg: TileLayoutChangedMessage = {
+ type: "tile-layout",
+ mode: parsed.mode,
+ data: data as unknown as TileLayoutChangedMessage["data"],
+ };
+ return msg;
+ }
default:
return null;
}