Implements docs/offline-sync-plan.md milestones M0–M2. M0 (PWA shell): - Add vite-plugin-pwa (generateSW, registerType: 'prompt', manifest:false); NetworkOnly for /api,/collab,/socket.io, NetworkFirst for GET /api, navigateFallback to index.html. - Register SW via useRegisterSW with a Mantine update prompt; skip registration inside Capacitor native WebView (is-capacitor guard). M1 (harden CRDT body + title into Yjs): - Lift the per-page Y.Doc/Hocuspocus providers into a shared hook+context so body and title editors share one doc. - Move the page title into a dedicated 'title' Yjs fragment (CRDT, offline- tolerant); drop the REST title save. Server persists the title fragment to page.title and seeds it for legacy pages (empty-fragment guard); a collab rename emits a treeUpdate so other users' tree/breadcrumbs refresh. - Persist the rebuilt ydoc on the content->ydoc path to neutralize the Yjs duplication trap. Add a 3-state sync indicator. M2 (offline read/navigation): - Persist React Query to IndexedDB (idb-keyval persister, version buster, selected roots only). - "Make available offline" action warms page, space, tree (root+ancestors+ children) and comments under exact hook keys, plus the page ydoc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
22 lines
871 B
TypeScript
22 lines
871 B
TypeScript
import { createContext, useContext } from "react";
|
|
import type { HocuspocusProvider } from "@hocuspocus/provider";
|
|
import type * as Y from "yjs";
|
|
|
|
// Shared collaboration providers lifted above the title/body editors so that
|
|
// both siblings bind to the SAME Y.Doc and HocuspocusProvider. The title lives
|
|
// in a dedicated 'title' fragment of the same doc as the body.
|
|
export interface EditorProvidersContextValue {
|
|
ydoc: Y.Doc;
|
|
remote: HocuspocusProvider;
|
|
providersReady: boolean;
|
|
}
|
|
|
|
export const EditorProvidersContext =
|
|
createContext<EditorProvidersContextValue | null>(null);
|
|
|
|
// Returns the shared providers, or null when rendered outside of a provider.
|
|
// Consumers must be null-safe (the body editor falls back to a non-collab mode).
|
|
export function useEditorProviders(): EditorProvidersContextValue | null {
|
|
return useContext(EditorProvidersContext);
|
|
}
|