Examples
Collaboration
Comments & Threads

Comments & Threads

TODO

"use client";
 
import { DefaultThreadStoreAuth, User, YjsThreadStore } from "@blocknote/core";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";
import { Select } from "@mantine/core";
import { useYDoc, useYjsProvider, YDocProvider } from "@y-sweet/react";
import { useMemo, useState } from "react";
 
const colors = [
  "#958DF1",
  "#F98181",
  "#FBBC88",
  "#FAF594",
  "#70CFF8",
  "#94FADB",
  "#B9F18D",
];
 
const getRandomElement = (list: any[]) =>
  list[Math.floor(Math.random() * list.length)];
 
const getRandomColor = () => getRandomElement(colors);
 
type MyUserType = User & {
  role: "editor" | "comment";
};
 
const HARDCODED_USERS: MyUserType[] = [
  {
    id: "1",
    username: "John Doe",
    avatarUrl: "https://placehold.co/100x100?text=John",
    role: "editor",
  },
  {
    id: "2",
    username: "Jane Doe",
    avatarUrl: "https://placehold.co/100x100?text=Jane",
    role: "editor",
  },
  {
    id: "3",
    username: "Bob Smith",
    avatarUrl: "https://placehold.co/100x100?text=Bob",
    role: "comment",
  },
  {
    id: "4",
    username: "Betty Smith",
    avatarUrl: "https://placehold.co/100x100?text=Betty",
    role: "comment",
  },
];
 
// The resolveUsers function fetches information about your users
// (e.g. their name, avatar, etc.). Usually, you'd fetch this from your
// own database or user management system.
// Here, we just return the hardcoded users.
async function resolveUsers(userIds: string[]) {
  // fake a (slow) network request
  await new Promise((resolve) => setTimeout(resolve, 1000));
 
  return HARDCODED_USERS.filter((user) => userIds.includes(user.id));
}
 
// This follows the Y-Sweet demo to setup a collabotive editor
// (but of course, you also use other collaboration providers)
export default function App() {
  const docId = "my-blocknote-document-with-comments";
 
  return (
    <YDocProvider
      docId={docId}
      authEndpoint="https://demos.y-sweet.dev/api/auth">
      <Document />
    </YDocProvider>
  );
}
 
function Document() {
  const [user, setUser] = useState<MyUserType>(HARDCODED_USERS[0]);
  const provider = useYjsProvider();
 
  // take the Y.Doc collaborative document from Y-Sweet
  const doc = useYDoc();
 
  // setup the thread store which stores / and syncs thread / comment data
  const threadStore = useMemo(() => {
    return new YjsThreadStore(
      user.id,
      doc.getMap("threads"),
      new DefaultThreadStoreAuth(user.id, user.role)
    );
  }, [doc, user]);
 
  // setup the editor with comments and collaboration
  const editor = useCreateBlockNote(
    {
      resolveUsers,
      comments: {
        threadStore,
      },
      collaboration: {
        provider,
        fragment: doc.getXmlFragment("blocknote"),
        user: { color: getRandomColor(), name: user.username },
      },
    },
    [user, threadStore]
  );
 
  // TODO: make sure comment button / formatting toolbar appears for comment-only users
  return (
    <div>
      <Select
        style={{ maxWidth: "300px" }}
        required
        label="Active user:"
        placeholder="Pick value"
        data={HARDCODED_USERS.map((user) => ({
          value: user.id,
          label: user.username + " (" + user.role + ")",
        }))}
        onChange={(value) => {
          if (!value) {
            return;
          }
          setUser(HARDCODED_USERS.find((user) => user.id === value)!);
        }}
        value={user.id}
      />
      <BlockNoteView editor={editor} editable={user.role === "editor"} />
    </div>
  );
}