From 7c9eec8c826d86ee07308be861d7df08a028a18c Mon Sep 17 00:00:00 2001 From: highperfocused Date: Sun, 6 Sep 2026 18:27:13 +0200 Subject: [PATCH] fix: don't highlight against a malformed address, gate the listener Per a "needs a closer look" review pass: - articles/index.tsx now passes an empty string, not a malformed "kind:pubkey:" address, when an article has no d tag. HighlightLayer treats a falsy address as "highlighting isn't available here." - The selectionchange listener is only registered when both user and address are present (in the effect's deps), instead of always running selection tracking regardless of whether a highlight could ever be published. - handleHighlight and the floating button both guard on address too, not just selection, so stale selection state from before a prop change went missing can't still trigger a publish. - docs/apps.md corrected: the saved text comes from Selection.toString() (window.getSelection()), not Range.toString(). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto --- docs/apps.md | 4 ++-- src/apps/articles/HighlightLayer.tsx | 12 +++++++++--- src/apps/articles/index.tsx | 5 ++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/apps.md b/docs/apps.md index 5ede84e..6bfd5b0 100644 --- a/docs/apps.md +++ b/docs/apps.md @@ -105,8 +105,8 @@ export default function ExampleApp({ setTitle }: AppProps) { `HighlightLayer` (`src/apps/articles/HighlightLayer.tsx`) tracks `window.getSelection()` against the rendered article, not the raw markdown — the highlighted text saved to a kind -9802 event is whatever `Range.toString()` returns, i.e. the plain-text content the reader -actually saw, not markdown syntax. +9802 event is whatever that `Selection`'s `.toString()` returns, i.e. the plain-text content +the reader actually saw, not markdown syntax. ### Follow lists are a whole-list replacement diff --git a/src/apps/articles/HighlightLayer.tsx b/src/apps/articles/HighlightLayer.tsx index 29bb723..1aad1b4 100644 --- a/src/apps/articles/HighlightLayer.tsx +++ b/src/apps/articles/HighlightLayer.tsx @@ -34,6 +34,12 @@ export function HighlightLayer({ const { toast } = useToast(); useEffect(() => { + // No point tracking selection at all when highlighting can't happen — + // signed out, or the article has no usable address to attach one to. + // (Stale selection state from before either went missing is harmless: + // the button below also requires `user && address` to render.) + if (!user || !address) return; + function handleSelectionChange() { const sel = window.getSelection(); const container = containerRef.current; @@ -60,10 +66,10 @@ export function HighlightLayer({ document.addEventListener('selectionchange', handleSelectionChange); return () => document.removeEventListener('selectionchange', handleSelectionChange); - }, []); + }, [user, address]); const handleHighlight = async () => { - if (!selection) return; + if (!selection || !address) return; const { text } = selection; window.getSelection()?.removeAllRanges(); setSelection(null); @@ -84,7 +90,7 @@ export function HighlightLayer({
{children} - {user && selection && ( + {user && address && selection && (