Something went wrong. Try again.
automerge-repo drop in replacement backed by Habitat "permissioned spaces" om your PDS
Something went wrong. Try again.
4.2 kB · 129 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130import { useSyncExternalStore, createContext, useContext, useCallback, useState, useEffect, type Context } from 'react'
import { DocHandle } from './doc-handle.js'import { Repo } from './repo.js'import type { DocumentId } from './document-id.js'import type { Doc } from '@automerge/automerge'
export const RepoContext: Context<Repo | null> = createContext<Repo | null>(null)
export function useRepo(): Repo { const repo = useContext(RepoContext) if (!repo) throw new Error('Repo was not found on RepoContext.') return repo}
interface UseDocHandleOptions { suspense?: boolean}
export function useDocHandle<T>(id: DocumentId | undefined, options?: UseDocHandleOptions): DocHandle<T> | undefined { if (options?.suspense) throw new Error('useDocHandle with suspense is not yet implemented in automerge-atproto') const repo = useRepo() const [handle, setHandle] = useState<DocHandle<T> | undefined>(undefined) useEffect(() => { if (!id) { setHandle(undefined) return } let cancelled = false setHandle(undefined) repo.find<T>(id).then((h) => { if (!cancelled) setHandle(h) }).catch((error) => { if (!cancelled) { console.error(`useDocHandle: failed to find document ${id}:`, error) setHandle(undefined) } }) return () => { cancelled = true } }, [repo, id])
return handle}
export type UseDocumentReturn<T> = [Doc<T>, (changeFn: (doc: T) => void, options?: { message?: string, time?: number }) => void]
const NOOP_CHANGE = () => {}
export function useDocument<T>(id: DocumentId | undefined): UseDocumentReturn<T> | [undefined, () => void] { const handle = useDocHandle<T>(id)
const subscribe = useCallback( (callback: () => void) => { if (!handle) return () => {} handle.on('change', callback) return () => handle.off('change', callback) }, [handle], )
const getSnapshot = useCallback(() => { if (!handle?.isReady()) return undefined return handle.doc() }, [handle])
const doc = useSyncExternalStore(subscribe, getSnapshot)
const change = useCallback( (changeFn: (doc: T) => void, options?: { message?: string, time?: number }) => { handle?.change(changeFn, options) }, [handle], )
if (!doc) return [undefined, NOOP_CHANGE] return [doc, change]}
export function useDocHandles(_ids: readonly DocumentId[]): DocHandle<unknown>[] { throw new Error('useDocHandles is not implemented in automerge-atproto')}
export function useDocuments(): { ids: DocumentId[]; create: () => DocumentId } { throw new Error('useDocuments is not implemented in automerge-atproto')}
export interface UseLocalAwarenessProps { handle?: DocHandle<unknown> userId: string initialState: unknown heartbeatTime?: number}
export function useLocalAwareness(_props: UseLocalAwarenessProps): unknown[] { throw new Error('useLocalAwareness is not implemented in automerge-atproto. Presence/awareness is separate from durable sync.')}
export type PeerStates = Record<string, unknown>export type Heartbeats = Record<string, number>
export interface UseRemoteAwarenessProps<T> { handle?: DocHandle<T> localUserId?: string offlineTimeout?: number getTime?: () => number}
export function useRemoteAwareness<T>(_props: UseRemoteAwarenessProps<T>): [PeerStates, Heartbeats] { throw new Error('useRemoteAwareness is not implemented in automerge-atproto. Presence/awareness is separate from durable sync.')}
export interface UsePresenceConfig<State = unknown> { handle: DocHandle<unknown> initialState?: State heartbeatMs?: number peerTtlMs?: number}
export interface UsePresenceResult<State = unknown> { peerStates: Record<string, unknown> localState: State | undefined update: <Channel extends keyof State>(channel: Channel, value: State[Channel]) => void start: (config?: Partial<UsePresenceConfig<State>>) => void stop: () => void}
export function usePresence<State = unknown>(_config: UsePresenceConfig<State>): UsePresenceResult<State> { throw new Error('usePresence is not implemented in automerge-atproto. Presence/awareness is separate from durable sync.')}