/ src / makeDocumentProjection.ts
makeDocumentProjection.ts
 1  import {onCleanup} from "solid-js"
 2  import {Doc, DocHandle, DocHandleChangePayload} from "@automerge/automerge-repo"
 3  import autoproduce from "./autoproduce.js"
 4  import {createStore, produce, reconcile, type Store} from "solid-js/store"
 5  
 6  const cache = new WeakMap<
 7  	DocHandle<unknown>,
 8  	{
 9  		refs: number
10  		store: Store<Doc<unknown>>
11  		cleanup(): void
12  	}
13  >()
14  
15  /**
16   * make a fine-grained live view of a document from its handle.
17   * @param handle an Automerge
18   * [DocHandle](https://automerge.org/automerge-repo/classes/_automerge_automerge_repo.DocHandle.html)
19   */
20  export default function makeDocumentProjection<T>(handle: DocHandle<T>) {
21  	onCleanup(() => {
22  		const item = cache.get(handle)!
23  		if (!item) return
24  		if (!item.refs--) {
25  			item.cleanup()
26  		}
27  	})
28  
29  	if (cache.has(handle)) {
30  		const item = cache.get(handle)!
31  		item.refs++
32  		return item.store as Doc<T>
33  	}
34  
35  	const [doc, set] = createStore<Doc<T>>(handle.doc())
36  
37  	cache.set(handle, {
38  		refs: 0,
39  		store: doc,
40  		cleanup() {
41  			handle.off("change", patch)
42  			handle.off("delete", ondelete)
43  		},
44  	})
45  
46  	function patch(payload: DocHandleChangePayload<T>) {
47  		set(produce(autoproduce(payload.patches)))
48  	}
49  
50  	function ondelete() {
51  		set(reconcile({} as Doc<T>))
52  	}
53  
54  	handle.on("change", patch)
55  	handle.on("delete", ondelete)
56  
57  	handle.whenReady().then(() => {
58  		set(handle.doc())
59  	})
60  
61  	return doc
62  }