-
-
Notifications
You must be signed in to change notification settings - Fork 529
feat: Comments #1376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Comments #1376
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
packages/react/src/components/Comments/FloatingComposerController.tsx
Outdated
Show resolved
Hide resolved
// Positioning with [data-bn-thread-id] attribute is a bit hacky, | ||
// we could probably also use the thread position from the plugin state? | ||
// for now, this works ok | ||
const updateRef = useCallback(() => { | ||
if (!state?.selectedThreadId) { | ||
return; | ||
} | ||
|
||
const el = editor.domElement?.querySelector( | ||
`[data-bn-thread-id="${state?.selectedThreadId}"]` | ||
); | ||
if (el) { | ||
setReference(el); | ||
} | ||
}, [setReference, editor, state?.selectedThreadId]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely strange, I don't quite understand how this all works
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@YousefED can you explain what's happening here? Seems a bit hacky to use querySelector
const handleFocusIn = (event: FocusEvent) => { | ||
if (!focusedRef.current) { | ||
_setFocused(true); | ||
onFocus?.(event); | ||
} | ||
}; | ||
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
const handleFocusOut = (event: FocusEvent) => { | ||
if (focusedRef.current && !containsRelatedTarget(event)) { | ||
_setFocused(false); | ||
onBlur?.(event); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
const node = ref.current; | ||
|
||
if (node) { | ||
node.addEventListener("focusin", handleFocusIn); | ||
node.addEventListener("focusout", handleFocusOut); | ||
|
||
return () => { | ||
node?.removeEventListener("focusin", handleFocusIn); | ||
node?.removeEventListener("focusout", handleFocusOut); | ||
}; | ||
} | ||
|
||
return undefined; | ||
}, [handleFocusIn, handleFocusOut]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this was copied over, but just wanted to say that this is strangely wasteful, it constantly adds and removes the event listeners when it doesn't need to
This reverts commit a647ec3.
# Conflicts: # docs/pages/docs/editor-basics/setup.mdx # package-lock.json # packages/core/src/editor/BlockNoteEditor.ts # packages/react/package.json
@@ -1431,11 +1368,11 @@ export class BlockNoteEditor< | |||
); | |||
} | |||
|
|||
public get ForceSelectionVisible() { | |||
return this.showSelectionPlugin.ForceSelectionVisible; | |||
public getForceSelectionVisible() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use a getter / setter for this? (like we do for "isEditable")?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@matthewlipski can you add a comment?
@@ -32,17 +32,18 @@ export const FloatingComposerController = < | |||
} | |||
|
|||
const comments = editor.comments; | |||
useEffect(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was this a code style preference to go from:
useEffect(() => {
editor.ForceSelectionVisible = !!state?.pendingComment;
}, [editor, state?.pendingComment]);
to
useEffect(() => {
comments.onUpdate((state) =>
editor.setForceSelectionVisible(state.pendingComment)
);
}, [comments, editor]);
or does it solve a bug?
In any case, we're not unsubscribing from onUpdate
so that would need to be fixed I think
@@ -48,7 +48,8 @@ test.describe("Check Dark Theme is Automatically Applied", () => { | |||
await page.keyboard.press("ArrowLeft"); | |||
|
|||
await page.waitForTimeout(500); | |||
expect(await page.screenshot()).toMatchSnapshot("dark-link-toolbar.png"); | |||
// expect(await page.screenshot()).toMatchSnapshot("dark-link-toolbar.png"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dev leftover?
💖 This feature is sponsored by DINUM 🇫🇷 and ZenDiS 🇩🇪
This PR adds comments to BlockNote!
See docs
ThreadStores
YjsThreadStore
The YjsThreadStore provides direct Yjs-based storage for comments, storing thread data directly in the Yjs document. This implementation is ideal for simple collaborative setups where all users have write access to the document.
The downside of this is that comments require a more advanced permission model than text documents, and Yjs doesn't provide a straightforward way to secure document updates.
RESTYjsThreadStore
The RESTYjsThreadStore combines Yjs storage with a REST API backend, providing secure comment management while maintaining real-time collaboration. This implementation is ideal when you have strong authentication requirements, but is a little more work to set up.
In this implementation, data is written to the Yjs document via a REST API which can handle access control. Data is still retrieved from the Yjs document directly (after it's been updated by the REST API), this way all comment information automatically syncs between clients using the existing collaboration provider.
We created a separate repository with an example server.
TipTap
We integrate directly with the TiptapCollabProvider with
TiptapThreadStore
.LiveBlocks (later)
The UI Components are roughly based on the LiveBlocks open source components. LiveBlocks users will have two different ways to use LiveBlocks:
TODO
TBD:
Yjs next steps
To fix later
Important:
Nice to haves:
closes #695
closes #1109
closes #1148
closes #703
closes #1200