@automerge/automerge-repo-react-hooks 0.1.2 → 0.1.4-alpha.0
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/useBootstrap.d.ts +28 -0
- package/dist/useBootstrap.d.ts.map +1 -0
- package/dist/useBootstrap.js +66 -0
- package/dist/useDocument.js +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/useBootstrap.ts +106 -0
- package/src/useDocument.ts +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
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,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DocHandle, Repo } from "@automerge/automerge-repo";
|
|
2
|
+
export declare const setHash: (hash: string, pushState?: boolean) => void;
|
|
3
|
+
export declare const useHash: () => string;
|
|
4
|
+
/**
|
|
5
|
+
* This hook is used to set up a single document as the base of an app session.
|
|
6
|
+
* This is a common pattern for simple multiplayer apps with shareable URLs.
|
|
7
|
+
*
|
|
8
|
+
* It will first check for the document ID in the URL hash:
|
|
9
|
+
* //myapp/#documentId=[document ID]
|
|
10
|
+
* Failing that, it will check for a `documentId` key in localStorage.
|
|
11
|
+
* Failing that, it will call onNoDocument, expecting a handle to be returned.
|
|
12
|
+
*
|
|
13
|
+
* The URL and localStorage will then be updated.
|
|
14
|
+
* Finally, it will return the document ID.
|
|
15
|
+
*
|
|
16
|
+
* @param {string?} props.key Key to use for the URL hash and localStorage
|
|
17
|
+
* @param {function?} props.fallback Function returning a document handle called if lookup fails. Defaults to repo.create()
|
|
18
|
+
* @param {function?} props.onInvalidDocumentId Function to call if documentId is invalid; signature (error) => (repo, onCreate)
|
|
19
|
+
* @returns {DocHandle} The document handle
|
|
20
|
+
*/
|
|
21
|
+
interface UseBootstrapOptions<T> {
|
|
22
|
+
key?: string;
|
|
23
|
+
onNoDocument?: (repo: Repo) => DocHandle<T>;
|
|
24
|
+
onInvalidDocumentId?(repo: Repo, error: Error): DocHandle<T>;
|
|
25
|
+
}
|
|
26
|
+
export declare const useBootstrap: <T>({ key, onNoDocument, onInvalidDocumentId, }?: UseBootstrapOptions<T>) => DocHandle<T>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=useBootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBootstrap.d.ts","sourceRoot":"","sources":["../src/useBootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAc,IAAI,EAAE,MAAM,2BAA2B,CAAA;AAKvE,eAAO,MAAM,OAAO,SAAU,MAAM,8BAUnC,CAAA;AAGD,eAAO,MAAM,OAAO,cAQnB,CAAA;AAwBD;;;;;;;;;;;;;;;;GAgBG;AACH,UAAU,mBAAmB,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAA;IAC3C,mBAAmB,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;CAC7D;AAED,eAAO,MAAM,YAAY,2FAgCxB,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useEffect, useState, useMemo } from "react";
|
|
2
|
+
import { useRepo } from "./useRepo.js";
|
|
3
|
+
// Set URL hash
|
|
4
|
+
export const setHash = (hash, pushState = false) => {
|
|
5
|
+
// Update URL hash
|
|
6
|
+
history[pushState ? "pushState" : "replaceState"]("", "", "#" + hash);
|
|
7
|
+
// Send fake hashchange event
|
|
8
|
+
window.dispatchEvent(new HashChangeEvent("hashchange", {
|
|
9
|
+
newURL: window.location.origin + window.location.pathname + hash,
|
|
10
|
+
oldURL: window.location.href,
|
|
11
|
+
}));
|
|
12
|
+
};
|
|
13
|
+
// Get current URL hash
|
|
14
|
+
export const useHash = () => {
|
|
15
|
+
const [hashValue, setHashValue] = useState(window.location.hash);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const handler = () => void setHashValue(window.location.hash);
|
|
18
|
+
window.addEventListener("hashchange", handler);
|
|
19
|
+
return () => void window.removeEventListener("hashchange", handler);
|
|
20
|
+
}, []);
|
|
21
|
+
return hashValue;
|
|
22
|
+
};
|
|
23
|
+
// Get a key from a query-param-style URL hash
|
|
24
|
+
const getQueryParamValue = (key, hash) => new URLSearchParams(hash.substr(1)).get(key);
|
|
25
|
+
const setQueryParamValue = (key, value, hash) => {
|
|
26
|
+
const u = new URLSearchParams(hash.substr(1));
|
|
27
|
+
u.set(key, value);
|
|
28
|
+
return u.toString();
|
|
29
|
+
};
|
|
30
|
+
const getDocumentId = (key, hash) => key && (getQueryParamValue(key, hash) || localStorage.getItem(key));
|
|
31
|
+
const setDocumentId = (key, documentId) => {
|
|
32
|
+
if (key) {
|
|
33
|
+
// Only set URL hash if document ID changed
|
|
34
|
+
if (documentId !== getQueryParamValue(key, window.location.hash))
|
|
35
|
+
setHash(setQueryParamValue(key, documentId, window.location.hash));
|
|
36
|
+
}
|
|
37
|
+
if (key)
|
|
38
|
+
localStorage.setItem(key, documentId);
|
|
39
|
+
};
|
|
40
|
+
export const useBootstrap = ({ key = "documentId", onNoDocument = repo => repo.create(), onInvalidDocumentId, } = {}) => {
|
|
41
|
+
const repo = useRepo();
|
|
42
|
+
const hash = useHash();
|
|
43
|
+
// Try to get existing document; else create a new one
|
|
44
|
+
const handle = useMemo(() => {
|
|
45
|
+
const existingDocumentId = getDocumentId(key, hash);
|
|
46
|
+
try {
|
|
47
|
+
return existingDocumentId
|
|
48
|
+
? repo.find(existingDocumentId)
|
|
49
|
+
: onNoDocument(repo);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// Presumably the documentId was invalid
|
|
53
|
+
if (existingDocumentId && onInvalidDocumentId)
|
|
54
|
+
return onInvalidDocumentId(repo, error);
|
|
55
|
+
// Forward other errors
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}, [hash, repo, onNoDocument, onInvalidDocumentId]);
|
|
59
|
+
// Update hashroute & localStorage on changes
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (handle) {
|
|
62
|
+
setDocumentId(key, handle.documentId);
|
|
63
|
+
}
|
|
64
|
+
}, [hash, handle]);
|
|
65
|
+
return handle;
|
|
66
|
+
};
|
package/dist/useDocument.js
CHANGED
|
@@ -8,7 +8,7 @@ export function useDocument(documentId) {
|
|
|
8
8
|
if (!handle)
|
|
9
9
|
return;
|
|
10
10
|
handle.value().then(v => setDoc(v));
|
|
11
|
-
const onPatch = (h) => setDoc(h.after);
|
|
11
|
+
const onPatch = (h) => setDoc(h.patchInfo.after);
|
|
12
12
|
handle.on("patch", onPatch);
|
|
13
13
|
const cleanup = () => {
|
|
14
14
|
handle.removeListener("patch", onPatch);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo-react-hooks",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4-alpha.0",
|
|
4
4
|
"description": "Hooks to access an Automerge Repo from your react app.",
|
|
5
5
|
"repository": "https://github.com/automerge/automerge-repo",
|
|
6
6
|
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"watch": "npm-watch"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@automerge/automerge-repo": "^0.1.
|
|
16
|
+
"@automerge/automerge-repo": "^0.1.4-alpha.0"
|
|
17
17
|
},
|
|
18
18
|
"watch": {
|
|
19
19
|
"build": {
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
"publishConfig": {
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "9a431d695250ea3ee2e58b4d60d2fb6a90c904e4"
|
|
30
30
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { DocHandle, DocumentId, Repo } from "@automerge/automerge-repo"
|
|
2
|
+
import { useEffect, useState, useMemo } from "react"
|
|
3
|
+
import { useRepo } from "./useRepo.js"
|
|
4
|
+
|
|
5
|
+
// Set URL hash
|
|
6
|
+
export const setHash = (hash: string, pushState = false) => {
|
|
7
|
+
// Update URL hash
|
|
8
|
+
history[pushState ? "pushState" : "replaceState"]("", "", "#" + hash)
|
|
9
|
+
// Send fake hashchange event
|
|
10
|
+
window.dispatchEvent(
|
|
11
|
+
new HashChangeEvent("hashchange", {
|
|
12
|
+
newURL: window.location.origin + window.location.pathname + hash,
|
|
13
|
+
oldURL: window.location.href,
|
|
14
|
+
})
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Get current URL hash
|
|
19
|
+
export const useHash = () => {
|
|
20
|
+
const [hashValue, setHashValue] = useState(window.location.hash)
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const handler = () => void setHashValue(window.location.hash)
|
|
23
|
+
window.addEventListener("hashchange", handler)
|
|
24
|
+
return () => void window.removeEventListener("hashchange", handler)
|
|
25
|
+
}, [])
|
|
26
|
+
return hashValue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Get a key from a query-param-style URL hash
|
|
30
|
+
const getQueryParamValue = (key: string, hash) =>
|
|
31
|
+
new URLSearchParams(hash.substr(1)).get(key)
|
|
32
|
+
|
|
33
|
+
const setQueryParamValue = (key: string, value, hash): string => {
|
|
34
|
+
const u = new URLSearchParams(hash.substr(1))
|
|
35
|
+
u.set(key, value)
|
|
36
|
+
return u.toString()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const getDocumentId = (key, hash) =>
|
|
40
|
+
key && (getQueryParamValue(key, hash) || localStorage.getItem(key))
|
|
41
|
+
|
|
42
|
+
const setDocumentId = (key, documentId) => {
|
|
43
|
+
if (key) {
|
|
44
|
+
// Only set URL hash if document ID changed
|
|
45
|
+
if (documentId !== getQueryParamValue(key, window.location.hash))
|
|
46
|
+
setHash(setQueryParamValue(key, documentId, window.location.hash))
|
|
47
|
+
}
|
|
48
|
+
if (key) localStorage.setItem(key, documentId)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* This hook is used to set up a single document as the base of an app session.
|
|
53
|
+
* This is a common pattern for simple multiplayer apps with shareable URLs.
|
|
54
|
+
*
|
|
55
|
+
* It will first check for the document ID in the URL hash:
|
|
56
|
+
* //myapp/#documentId=[document ID]
|
|
57
|
+
* Failing that, it will check for a `documentId` key in localStorage.
|
|
58
|
+
* Failing that, it will call onNoDocument, expecting a handle to be returned.
|
|
59
|
+
*
|
|
60
|
+
* The URL and localStorage will then be updated.
|
|
61
|
+
* Finally, it will return the document ID.
|
|
62
|
+
*
|
|
63
|
+
* @param {string?} props.key Key to use for the URL hash and localStorage
|
|
64
|
+
* @param {function?} props.fallback Function returning a document handle called if lookup fails. Defaults to repo.create()
|
|
65
|
+
* @param {function?} props.onInvalidDocumentId Function to call if documentId is invalid; signature (error) => (repo, onCreate)
|
|
66
|
+
* @returns {DocHandle} The document handle
|
|
67
|
+
*/
|
|
68
|
+
interface UseBootstrapOptions<T> {
|
|
69
|
+
key?: string
|
|
70
|
+
onNoDocument?: (repo: Repo) => DocHandle<T>
|
|
71
|
+
onInvalidDocumentId?(repo: Repo, error: Error): DocHandle<T>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const useBootstrap = <T>({
|
|
75
|
+
key = "documentId",
|
|
76
|
+
onNoDocument = repo => repo.create(),
|
|
77
|
+
onInvalidDocumentId,
|
|
78
|
+
}: UseBootstrapOptions<T> = {}): DocHandle<T> => {
|
|
79
|
+
const repo = useRepo()
|
|
80
|
+
const hash = useHash()
|
|
81
|
+
|
|
82
|
+
// Try to get existing document; else create a new one
|
|
83
|
+
const handle = useMemo((): DocHandle<T> => {
|
|
84
|
+
const existingDocumentId = getDocumentId(key, hash)
|
|
85
|
+
try {
|
|
86
|
+
return existingDocumentId
|
|
87
|
+
? repo.find(existingDocumentId as DocumentId)
|
|
88
|
+
: onNoDocument(repo)
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Presumably the documentId was invalid
|
|
91
|
+
if (existingDocumentId && onInvalidDocumentId)
|
|
92
|
+
return onInvalidDocumentId(repo, error)
|
|
93
|
+
// Forward other errors
|
|
94
|
+
throw error
|
|
95
|
+
}
|
|
96
|
+
}, [hash, repo, onNoDocument, onInvalidDocumentId])
|
|
97
|
+
|
|
98
|
+
// Update hashroute & localStorage on changes
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (handle) {
|
|
101
|
+
setDocumentId(key, handle.documentId)
|
|
102
|
+
}
|
|
103
|
+
}, [hash, handle])
|
|
104
|
+
|
|
105
|
+
return handle
|
|
106
|
+
}
|
package/src/useDocument.ts
CHANGED
|
@@ -14,7 +14,7 @@ export function useDocument<T>(documentId?: DocumentId) {
|
|
|
14
14
|
|
|
15
15
|
handle.value().then(v => setDoc(v))
|
|
16
16
|
|
|
17
|
-
const onPatch = (h: DocHandlePatchPayload<T>) => setDoc(h.after)
|
|
17
|
+
const onPatch = (h: DocHandlePatchPayload<T>) => setDoc(h.patchInfo.after)
|
|
18
18
|
handle.on("patch", onPatch)
|
|
19
19
|
const cleanup = () => {
|
|
20
20
|
handle.removeListener("patch", onPatch)
|