-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathEditor.tsx
More file actions
130 lines (122 loc) · 3.77 KB
/
Editor.tsx
File metadata and controls
130 lines (122 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"use client"; // this registers <Editor> as a Client Component
import {
DefaultThreadStoreAuth,
RESTYjsThreadStore,
} from "@blocknote/core/comments";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";
import { HocuspocusProvider } from "@hocuspocus/provider";
import { useEffect } from "react";
// Hardcoded settings for demo purposes
const USER_ID = "user123";
const USER_ROLE: "COMMENT-ONLY" | "READ-WRITE" = "READ-WRITE";
const DOCUMENT_ID = "mydoc1234";
const TOKEN = `${USER_ID}__${USER_ROLE}`;
// Setup Hocuspocus provider
const provider = new HocuspocusProvider({
url: "ws://localhost:8787/hocuspocus",
token: TOKEN,
name: DOCUMENT_ID,
});
// The thread store auth is used by the BlockNote interface to determine which actions are allowed
// (and which elements should be shown)
const threadStoreAuth = new DefaultThreadStoreAuth(
USER_ID,
USER_ROLE === "READ-WRITE" ? "editor" : "comment"
);
// set up the thread store using the REST API
const threadStore = new RESTYjsThreadStore(
`http://localhost:8787/documents/${DOCUMENT_ID}/threads`,
{
Authorization: `Bearer ${TOKEN}`,
},
provider.document.getMap("threads"),
threadStoreAuth
);
// Instead of using the REST API, you could also use a YjsThreadStore
// however, this lacks good authentication on comment operations
//
// const threadStore = new YjsThreadStore(
// USER_ID,
// provider.document.getMap("threads"),
// threadStoreAuth
// );
// Our <Editor> component we can reuse later
export default function Editor() {
// Creates a new editor instance.
const editor = useCreateBlockNote({
collaboration: {
provider,
fragment: provider.document.getXmlFragment("doc"),
user: {
name: "John Doe",
color: "#ff0000",
},
},
resolveUsers: async (userIds) => {
// sample implementation, replace this with a call to your own user database for example
return userIds.map((userId) => ({
id: userId,
username: "John Doe",
avatarUrl: "https://placehold.co/100x100",
}));
},
comments: {
threadStore,
},
});
useEffect(() => {
provider.document.on("update", (update) => {
console.log(provider.document.getMap("threads").toJSON());
});
}, [provider.document]);
// Renders the editor instance using a React component.
return (
<>
<button
onClick={() => {
const comments = provider.document.getMap("threads");
comments.set("unauthorized-thread", {
id: "unauthorized-thread",
createdAt: 1741265978860,
updatedAt: 1741265978860,
comments: [
{
id: "unauthorized-comment",
userId: "unauthorized-user",
createdAt: 1741265978860,
updatedAt: 1741265978860,
body: [
{
id: "unauthorized-comment-body",
type: "paragraph",
props: {},
content: [
{
type: "text",
text: "This comment should not be visible",
styles: {},
},
],
children: [],
},
],
reactionsByUser: {},
},
],
resolved: false,
});
}}
>
Unauthorized comment modification
</button>
<p>
Pressing the button above will add a new comment to the threads map, but
this change will be rejected by the server.
</p>
<BlockNoteView editor={editor} />
</>
);
}