diff --git a/docs/snippets/examples/document-js.mdx b/docs/snippets/examples/document-js.mdx index b83f67135..bea258376 100644 --- a/docs/snippets/examples/document-js.mdx +++ b/docs/snippets/examples/document-js.mdx @@ -3,12 +3,32 @@ import { actor } from "actor-core"; export type Cursor = { x: number, y: number, userId: string }; +export type CursorUpdateEvent = { userId: string, x: number, y: number }; + +export type TextUpdatedEvent = { text: string, userId: string }; + +export type UserDisconnectedEvent = { userId: string }; + const document = actor({ state: { text: "", cursors: {} as Record, }, + onDisconnect: (c, conn) => { + console.log("onDisconnect(): " + conn.id); + delete c.state.cursors[conn.id]; + + // Broadcast removal + c.broadcastWithOptions( + { exclude: [conn.id] }, + "userDisconnected", + { + userId: conn.id + } as UserDisconnectedEvent + ); + }, + actions: { getText: (c) => c.state.text, @@ -18,25 +38,34 @@ const document = actor({ c.state.text = text; // Broadcast update - c.broadcast("textUpdated", { - text, - userId: c.conn.id - }); + c.broadcastWithOptions( + { excludeSelf: true }, + "textUpdated", + { + text, + userId: c.conn.id + } as TextUpdatedEvent + ); }, getCursors: (c) => c.state.cursors, updateCursor: (c, x: number, y: number) => { + console.log("updateCursor(): " + c.conn.id); // Update user location const userId = c.conn.id; c.state.cursors[userId] = { x, y, userId }; // Broadcast location - c.broadcast("cursorUpdated", { - userId, - x, - y - }); + c.broadcastWithOptions( + { excludeSelf: true }, + "cursorUpdated", + { + userId, + x, + y + } as CursorUpdateEvent + ); }, } }); diff --git a/docs/snippets/examples/document-react.mdx b/docs/snippets/examples/document-react.mdx index 97aa2373d..3c4318233 100644 --- a/docs/snippets/examples/document-react.mdx +++ b/docs/snippets/examples/document-react.mdx @@ -2,7 +2,10 @@ import { createClient } from "actor-core/client"; import { createReactActorCore } from "@actor-core/react"; import { useState, useEffect } from "react"; -import type { App } from "../actors/app"; +import type { + App, Cursor, TextUpdatedEvent, + CursorUpdateEvent, UserDisconnectedEvent +} from "../actors/app"; const client = createClient("http://localhost:6420"); const { useActor, useActorEvent } = createReactActorCore(client); @@ -10,65 +13,86 @@ const { useActor, useActorEvent } = createReactActorCore(client); export function DocumentEditor() { // Connect to actor for this document ID from URL const documentId = new URLSearchParams(window.location.search).get('id') || 'default-doc'; - const [{ actor, connectionId }] = useActor("document", { tags: { id: documentId } }); - + const [{ actor, state }] = useActor("document", { tags: { id: documentId } }); + // Local state const [text, setText] = useState(""); const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 }); - const [otherCursors, setOtherCursors] = useState({}); + const [otherCursors, setOtherCursors] = useState>({}); // Load initial document state useEffect(() => { - if (actor) { + if (actor && state === "created") { actor.getText().then(setText); actor.getCursors().then(setOtherCursors); } - }, [actor]); + }, [actor, state]); // Listen for updates from other users - useActorEvent({ actor, event: "textUpdated" }, ({ text: newText, userId: senderId }) => { - if (senderId !== connectionId) setText(newText); + useActorEvent({ actor, event: "textUpdated" }, (event) => { + const { text: newText, userId: _senderId } = event as TextUpdatedEvent; + + setText(newText); }); - useActorEvent({ actor, event: "cursorUpdated" }, ({ userId: cursorUserId, x, y }) => { - if (cursorUserId !== connectionId) { - setOtherCursors(prev => ({ - ...prev, - [cursorUserId]: { x, y, userId: cursorUserId } - })); - } + useActorEvent({ actor, event: "cursorUpdated" }, (event) => { + const { userId: cursorUserId, x, y } = event as CursorUpdateEvent; + + setOtherCursors(prev => ({ + ...prev, + [cursorUserId]: { x, y, userId: cursorUserId } + })); }); - - // Update cursor position - const updateCursor = (e) => { - if (!actor) return; - const rect = e.currentTarget.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; + + useActorEvent({ actor, event: "userDisconnected" }, (event) => { + const { userId } = event as UserDisconnectedEvent; - if (x !== cursorPos.x || y !== cursorPos.y) { - setCursorPos({ x, y }); - actor.updateCursor(x, y); - } - }; + setOtherCursors(prev => { + const newCursors = { ...prev }; + delete newCursors[userId]; + return newCursors; + }); + }); + + + useEffect(() => { + if (!actor || state !== "created") return; + + const updateCursor = ({ x, y }: { x: number, y: number }) => { + + if (x !== cursorPos.x || y !== cursorPos.y) { + setCursorPos({ x, y }); + actor.updateCursor(x, y); + } + }; + + window.addEventListener("mousemove", (e) => { + const x = e.clientX; + const y = e.clientY; + + updateCursor({ x, y }); + }); + }, [actor, state]); return (

Document: {documentId}

-
+