@automerge/automerge-repo-react-hooks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/useDocument.d.ts +4 -0
- package/dist/useDocument.d.ts.map +1 -0
- package/dist/useDocument.js +24 -0
- package/dist/useHandle.d.ts +3 -0
- package/dist/useHandle.d.ts.map +1 -0
- package/dist/useHandle.js +7 -0
- package/dist/useRepo.d.ts +5 -0
- package/dist/useRepo.d.ts.map +1 -0
- package/dist/useRepo.js +8 -0
- package/package.json +30 -0
- package/src/index.ts +3 -0
- package/src/useDocument.ts +32 -0
- package/src/useHandle.ts +9 -0
- package/src/useRepo.ts +10 -0
- package/tsconfig.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# React Hooks for Automerge Repo
|
|
2
|
+
|
|
3
|
+
## Example usage
|
|
4
|
+
|
|
5
|
+
### App Setup
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import React, { StrictMode } from "react"
|
|
9
|
+
import ReactDOM from "react-dom/client"
|
|
10
|
+
import localforage from "localforage"
|
|
11
|
+
|
|
12
|
+
import { Repo, DocCollection } from "@automerge/automerge-repo"
|
|
13
|
+
|
|
14
|
+
import { BroadcastChannelNetworkAdapter } from "@automerge/automerge-repo-network-broadcastchannel"
|
|
15
|
+
import { LocalFirstRelayNetworkAdapter } from "@automerge/automerge-repo-network-localfirstrelay"
|
|
16
|
+
|
|
17
|
+
import App, { RootDocument } from "./App.js"
|
|
18
|
+
import { RepoContext } from "@automerge/automerge-repo-react-hooks"
|
|
19
|
+
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
21
|
+
const sharedWorker = new SharedWorker(
|
|
22
|
+
new URL("./shared-worker.js", import.meta.url),
|
|
23
|
+
{ type: "module", name: "@automerge/automerge-repo-shared-worker" }
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
async function getRepo(url: string): Promise<DocCollection> {
|
|
27
|
+
return await Repo({
|
|
28
|
+
network: [
|
|
29
|
+
new BroadcastChannelNetworkAdapter(),
|
|
30
|
+
new LocalFirstRelayNetworkAdapter("wss://local-first-relay.glitch.me/"),
|
|
31
|
+
],
|
|
32
|
+
sharePolicy: peerId => peerId.includes("shared-worker"),
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function getRootDocument(repo: DocCollection, initFunction: any) {
|
|
37
|
+
let docId: string | null = window.location.hash.replace(/^#/, "")
|
|
38
|
+
if (!docId) {
|
|
39
|
+
docId = await localforage.getItem("root")
|
|
40
|
+
}
|
|
41
|
+
let rootHandle
|
|
42
|
+
|
|
43
|
+
if (!docId) {
|
|
44
|
+
rootHandle = repo.create()
|
|
45
|
+
rootHandle.change(initFunction)
|
|
46
|
+
await localforage.setItem("root", rootHandle.documentId)
|
|
47
|
+
} else {
|
|
48
|
+
rootHandle = await repo.find(docId)
|
|
49
|
+
window.location.hash = docId
|
|
50
|
+
}
|
|
51
|
+
return rootHandle
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const initFunction = (d: RootDocument) => {
|
|
55
|
+
d.items = []
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const queryString = window.location.search // Returns:'?q=123'
|
|
59
|
+
|
|
60
|
+
// Further parsing:
|
|
61
|
+
const params = new URLSearchParams(queryString)
|
|
62
|
+
const hostname = params.get("host") || "automerge-storage-demo.glitch.me"
|
|
63
|
+
|
|
64
|
+
getRepo(`wss://${hostname}`).then(repo => {
|
|
65
|
+
getRootDocument(repo, initFunction).then(rootDoc => {
|
|
66
|
+
const rootElem = document.getElementById("root")
|
|
67
|
+
if (!rootElem) {
|
|
68
|
+
throw new Error("The 'root' element wasn't found in the host HTML doc.")
|
|
69
|
+
}
|
|
70
|
+
const root = ReactDOM.createRoot(rootElem)
|
|
71
|
+
root.render(
|
|
72
|
+
<StrictMode>
|
|
73
|
+
<RepoContext.Provider value={repo}>
|
|
74
|
+
<App rootDocumentId={rootDoc.documentId} />
|
|
75
|
+
</RepoContext.Provider>
|
|
76
|
+
</StrictMode>
|
|
77
|
+
)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDocument.d.ts","sourceRoot":"","sources":["../src/useDocument.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAyB,MAAM,2BAA2B,CAAA;AAI7E,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,UAAU,uBAyBL,SAAS,CAAC,CAAC,KAAK,IAAI,EACpE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useRepo } from "./useRepo";
|
|
3
|
+
export function useDocument(documentId) {
|
|
4
|
+
const [doc, setDoc] = useState();
|
|
5
|
+
const repo = useRepo();
|
|
6
|
+
const handle = documentId ? repo.find(documentId) : null;
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!handle)
|
|
9
|
+
return;
|
|
10
|
+
handle.value().then(v => setDoc(v));
|
|
11
|
+
const onPatch = (h) => setDoc(h.after);
|
|
12
|
+
handle.on("patch", onPatch);
|
|
13
|
+
const cleanup = () => {
|
|
14
|
+
handle.removeListener("patch", onPatch);
|
|
15
|
+
};
|
|
16
|
+
return cleanup;
|
|
17
|
+
}, [handle]);
|
|
18
|
+
const changeDoc = (changeFn) => {
|
|
19
|
+
if (!handle)
|
|
20
|
+
return;
|
|
21
|
+
handle.change(changeFn);
|
|
22
|
+
};
|
|
23
|
+
return [doc, changeDoc];
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHandle.d.ts","sourceRoot":"","sources":["../src/useHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAIjE,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,CAIjE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRepo.d.ts","sourceRoot":"","sources":["../src/useRepo.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAA;AAGhD,eAAO,MAAM,WAAW,+BAAmC,CAAA;AAE3D,wBAAgB,OAAO,IAAI,IAAI,CAI9B"}
|
package/dist/useRepo.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@automerge/automerge-repo-react-hooks",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Hooks to access an Automerge Repo from your react app.",
|
|
5
|
+
"repository": "https://github.com/pvh/automerge-repo",
|
|
6
|
+
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"private": false,
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"watch": "npm-watch"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@automerge/automerge-repo": "^0.0.1"
|
|
17
|
+
},
|
|
18
|
+
"watch": {
|
|
19
|
+
"build": {
|
|
20
|
+
"patterns": "./src/**/*",
|
|
21
|
+
"extensions": [
|
|
22
|
+
".ts"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"gitHead": "e572f26ae416140b025c3ba557d9f781abbdada1"
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Doc, ChangeFn } from "@automerge/automerge"
|
|
2
|
+
import { DocumentId, DocHandlePatchPayload } from "@automerge/automerge-repo"
|
|
3
|
+
import { useEffect, useState } from "react"
|
|
4
|
+
import { useRepo } from "./useRepo"
|
|
5
|
+
|
|
6
|
+
export function useDocument<T>(documentId?: DocumentId) {
|
|
7
|
+
const [doc, setDoc] = useState<Doc<T>>()
|
|
8
|
+
const repo = useRepo()
|
|
9
|
+
|
|
10
|
+
const handle = documentId ? repo.find<T>(documentId) : null
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!handle) return
|
|
14
|
+
|
|
15
|
+
handle.value().then(v => setDoc(v))
|
|
16
|
+
|
|
17
|
+
const onPatch = (h: DocHandlePatchPayload<T>) => setDoc(h.after)
|
|
18
|
+
handle.on("patch", onPatch)
|
|
19
|
+
const cleanup = () => {
|
|
20
|
+
handle.removeListener("patch", onPatch)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return cleanup
|
|
24
|
+
}, [handle])
|
|
25
|
+
|
|
26
|
+
const changeDoc = (changeFn: ChangeFn<T>) => {
|
|
27
|
+
if (!handle) return
|
|
28
|
+
handle.change(changeFn)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return [doc, changeDoc] as [Doc<T>, (changeFn: ChangeFn<T>) => void]
|
|
32
|
+
}
|
package/src/useHandle.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DocHandle, DocumentId } from "@automerge/automerge-repo"
|
|
2
|
+
import { useState } from "react"
|
|
3
|
+
import { useRepo } from "./useRepo.js"
|
|
4
|
+
|
|
5
|
+
export function useHandle<T>(documentId: DocumentId): DocHandle<T> {
|
|
6
|
+
const repo = useRepo()
|
|
7
|
+
const [handle] = useState<DocHandle<T>>(repo.find(documentId))
|
|
8
|
+
return handle
|
|
9
|
+
}
|
package/src/useRepo.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Repo } from "@automerge/automerge-repo"
|
|
2
|
+
import { createContext, useContext } from "react"
|
|
3
|
+
|
|
4
|
+
export const RepoContext = createContext<Repo | null>(null)
|
|
5
|
+
|
|
6
|
+
export function useRepo(): Repo {
|
|
7
|
+
const repo = useContext(RepoContext)
|
|
8
|
+
if (!repo) throw new Error("Repo was not found on RepoContext.")
|
|
9
|
+
return repo
|
|
10
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"jsx": "react",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"strict": false,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*.ts"]
|
|
16
|
+
}
|