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.
Definitely strange, I don't quite understand how this all works
There was a problem hiding this comment.
@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.
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
There was a problem hiding this comment.
why not use a getter / setter for this? (like we do for "isEditable")?
| } | ||
|
|
||
| const comments = editor.comments; | ||
| useEffect(() => { |
There was a problem hiding this comment.
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
|
|
||
| await page.waitForTimeout(500); | ||
| expect(await page.screenshot()).toMatchSnapshot("dark-link-toolbar.png"); | ||
| // expect(await page.screenshot()).toMatchSnapshot("dark-link-toolbar.png"); |
💖 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