import CBOR from "cbor"; import { useEffect, useState } from "react"; import useWebSocket, { ReadyState } from "react-use-websocket"; import * as Automerge from "@automerge/automerge"; import * as uuid from "uuid"; const connectionStatusMap = { [ReadyState.CONNECTING]: "Connecting", [ReadyState.OPEN]: "Open", [ReadyState.CLOSING]: "Closing", [ReadyState.CLOSED]: "Closed", [ReadyState.UNINSTANTIATED]: "Uninstantiated", }; export default function Room({ roomId }) { const [readyState, setReadyState] = useState(ReadyState.CLOSED); const [connectedClients, setConnectedClients] = useState([]); const [clientId, setClientId] = useState(null); const [chats, setChats] = useState([]); const [doc, setDoc] = useState(Automerge.init()); const [syncState, setSyncState] = useState(Automerge.initSyncState()); function updateDoc(newDoc) { setDoc(newDoc); } const { sendMessage, lastJsonMessage, readyState: newReadyState, } = useWebSocket("ws://localhost:3100/ws", { onOpen: ({}) => { console.log("Shiet, connected."); }, onMessage: async (event) => { const data = CBOR.decode(await event.data.arrayBuffer()); console.log("received", data); if (data.type === "ServerHello") { setClientId(uuid.stringify(data.client_id)); } if (data.type === "RoomClientList") { setConnectedClients(data.clients.map((x) => uuid.stringify(x))); } if (data.type === "ChatMessage") { setChats([...chats, data]); } if (data.type === "Automerge") { const [nextDoc, nextSyncState, patch] = Automerge.receiveSyncMessage( doc, syncState, data.message[1] ); setDoc(nextDoc); setSyncState(nextSyncState); console.log("patch", patch); } }, }); function sendWtfMessage(data) { let cbor = CBOR.encode(data); console.log( "cbor-encoded", [...new Uint8Array(cbor)] .map((x) => x.toString(16).padStart(2, "0")) .join(" ") ); sendMessage(cbor); } useEffect(() => { console.log("hellosu", readyState, newReadyState); if ( readyState === ReadyState.CONNECTING && newReadyState === ReadyState.OPEN ) { // On Open sendWtfMessage({ type: "JoinRoom", room_id: roomId }); console.log("Sent connection message"); } setReadyState(newReadyState); }, [newReadyState]); const connectionStatus = connectionStatusMap[readyState]; return ( <>

Room Id: {roomId}

Connection status: {connectionStatus}

{newReadyState === ReadyState.OPEN && ( )} ); } function ReadyPart({ doc, syncState, roomId, clientId, chats, connectedClients, sendWtfMessage, updateDoc, }) { const [message, setMessage] = useState(""); const [addItemName, setAddItemName] = useState(""); function onSubmit(e) { e.preventDefault(); sendWtfMessage({ type: "ChatMessage", timestamp: new Date().toISOString(), message_id: uuid.v4(), room_id: roomId, author: clientId, content: message, }); setMessage(""); } const items = doc.items || []; function addItem(e) { e.preventDefault(); const newDoc = Automerge.change(doc, (doc) => { if (!doc.items) doc.items = []; doc.items.push({ id: uuid.v4(), content: addItemName, }); }); updateDoc(newDoc); const [syncMessage, binary] = Automerge.generateSyncMessage(doc, syncState); if (syncMessage) sendWtfMessage({ type: "Automerge", client_id: clientId, room_id: roomId, message: binary, }); setAddItemName(""); } return ( <> Connected: Messages:
setMessage(e.target.value)} placeholder="Type a message..." />
Grubs:
setAddItemName(e.target.value)} placeholder="Type a message..." />
); }