Skip to content

Commit

Permalink
Merge pull request #4 from nikgraf/implement-storage-of-docs
Browse files Browse the repository at this point in the history
implement docs storage
  • Loading branch information
nikgraf committed Jun 6, 2024
2 parents 70f39d2 + b3f48d5 commit cc27742
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 18 deletions.
4 changes: 2 additions & 2 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
"react-native-screens": "3.31.1",
"react-native-svg": "15.2.0",
"react-native-web": "~0.19.10",
"secsync": "^0.4.0",
"secsync-react-yjs": "^0.4.0",
"secsync": "^0.5.0",
"secsync-react-yjs": "^0.5.0",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
Expand Down
48 changes: 46 additions & 2 deletions apps/app/src/app/list/[listId].tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useLocalSearchParams } from "expo-router";
import React, { useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { View } from "react-native";
import sodium, { KeyPair } from "react-native-libsodium";
import { useYjsSync } from "secsync-react-yjs";
Expand All @@ -9,6 +9,12 @@ import { Input } from "~/components/ui/input";
import { Text } from "~/components/ui/text";
import { useLocker } from "../../hooks/useLocker";
import { useYArray } from "../../hooks/useYArray";
import { deserialize } from "../../utils/deserialize";
import {
documentPendingChangesStorage,
documentStorage,
} from "../../utils/documentStorage";
import { serialize } from "../../utils/serialize";

const websocketEndpoint =
process.env.NODE_ENV === "development"
Expand All @@ -27,15 +33,53 @@ const List: React.FC<Props> = () => {
return sodium.crypto_sign_keypair();
});

const yDocRef = useRef<Yjs.Doc>(new Yjs.Doc());
// load initial data
const [initialData] = useState(() => {
const yDoc = new Yjs.Doc();
// load full document
const serializedDoc = documentStorage.getString(documentId);
if (serializedDoc) {
Yjs.applyUpdateV2(yDoc, deserialize(serializedDoc));
}

// loads the pendingChanges
const pendingChanges = documentPendingChangesStorage.getString(documentId);

return {
yDoc,
pendingChanges: pendingChanges ? deserialize(pendingChanges) : [],
};
});

const yDocRef = useRef<Yjs.Doc>(initialData.yDoc);

// update the document after every change (could be debounced)
useEffect(() => {
const onUpdate = (update: any) => {
const fullYDoc = Yjs.encodeStateAsUpdateV2(yDocRef.current);
documentStorage.set(documentId, serialize(fullYDoc));
};
yDocRef.current.on("updateV2", onUpdate);

return () => {
yDocRef.current.off("updateV2", onUpdate);
};
}, []);

const yTodos: Yjs.Array<string> = yDocRef.current.getArray("todos");
const todos = useYArray(yTodos);
const [newTodoText, setNewTodoText] = useState("");

const { content } = useLocker();
const documentKey = sodium.from_base64(content[`document:${documentId}`]);

const [state, send] = useYjsSync({
yDoc: yDocRef.current,
pendingChanges: initialData.pendingChanges,
// callback to store the pending changes in
onPendingChangesUpdated: (allChanges) => {
documentPendingChangesStorage.set(documentId, serialize(allChanges));
},
documentId,
signatureKeyPair: authorKeyPair,
websocketEndpoint,
Expand Down
12 changes: 12 additions & 0 deletions apps/app/src/utils/deserialize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const deserialize = (data: string) => {
return JSON.parse(data, (key, value) => {
if (
typeof value === "object" &&
value !== null &&
value.type === "Uint8Array"
) {
return new Uint8Array(value.data);
}
return value;
});
};
9 changes: 9 additions & 0 deletions apps/app/src/utils/documentStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { MMKV } from "react-native-mmkv";

export const documentStorage = new MMKV({
id: `document-storage`,
});

export const documentPendingChangesStorage = new MMKV({
id: `document-pending-changes-storage`,
});
8 changes: 8 additions & 0 deletions apps/app/src/utils/serialize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const serialize = (data: any) => {
return JSON.stringify(data, (key, value) => {
if (value instanceof Uint8Array) {
return { type: "Uint8Array", data: Array.from(value) };
}
return value;
});
};
4 changes: 2 additions & 2 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"dotenv": "^16.4.5",
"express": "^4.19.2",
"isomorphic-ws": "^5.0.0",
"secsync": "^0.4.0",
"secsync-server": "^0.4.0",
"secsync": "^0.5.0",
"secsync-server": "^0.5.0",
"ws": "^8.17.0",
"zod": "^3.23.8"
},
Expand Down
24 changes: 12 additions & 12 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8568,27 +8568,27 @@ schema-utils@^4.0.1:
ajv-formats "^2.1.1"
ajv-keywords "^5.1.0"

secsync-react-yjs@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/secsync-react-yjs/-/secsync-react-yjs-0.4.0.tgz#aa35fc5c8a9c78d69c4470213e91bae94ecaa888"
integrity sha512-xdBVxFSbuc9pTmvRN+3AXqmeW2WgxFrlPW7sHazSX9o675ai99O/Tf9YdBT/ls41XaCU6hxmI/Oc9ETHxtkziw==
secsync-react-yjs@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/secsync-react-yjs/-/secsync-react-yjs-0.5.0.tgz#a30cc4340b0ebe999d6232c3dbe16e553fe5d0e4"
integrity sha512-UTTGIf3o50RCfsgVjASFYlqYp4j+OCfbDhAejFTipkyPqP868kheafKQpIA2bZU1gFPtmlKRoXW0DsYSPXK1yg==
dependencies:
"@xstate/react" "^4.1.1"
lib0 "^0.2.94"
y-protocols "^1.0.6"

secsync-server@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/secsync-server/-/secsync-server-0.4.0.tgz#37c87e43453154635bbfee2bc93ba6e624a1e005"
integrity sha512-ZoK+aOvVcDFEd4+cZeOiumgB2klPXHqxeZf7otuXkzY7ZI4JG7egu8aN2oed+iJqAWxbNaBLKRQdToZ+o9C68g==
secsync-server@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/secsync-server/-/secsync-server-0.5.0.tgz#9bb436e173b1b7c5b8d2bae8e801ed881188cecd"
integrity sha512-NkjfS5AVAF5qk6AFKJg6bRsDPUGvd/dYBavGSPDppJ8hv9DTg+6GhJc8bCDZ9Jr8uWnpLPyOcTMk6/oe4EIrAw==
dependencies:
canonicalize "^2.0.0"
libsodium-wrappers "^0.7.13"

secsync@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/secsync/-/secsync-0.4.0.tgz#b07eb3bcb92e3b47d8e90fcc9c81ab3c152108ad"
integrity sha512-b5pMmjcJKVMyZNZcXDF8mADTy5zxOl1ABP3zvc7lVcWt38d0ynEbvQSxJtaQ9gbRTYwEviGuDU+uMsLCy9AVEw==
secsync@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/secsync/-/secsync-0.5.0.tgz#64541623c843cdde63c6ae608e130c18dfd45759"
integrity sha512-OdIP660UH2FVFBMnPzruW2LIEKbRc2L7rdqcnI/LWu2M7p+dTA+OzCZd87a09MxL0xIDx/7tUYkmwJZknlwo0w==
dependencies:
canonicalize "^2.0.0"
libsodium-wrappers "^0.7.13"
Expand Down

0 comments on commit cc27742

Please sign in to comment.