mirror of
https://github.com/layer-systems/website.git
synced 2026-09-13 06:07:29 +02:00
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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({
|
||||
<div ref={containerRef} className="relative">
|
||||
{children}
|
||||
|
||||
{user && selection && (
|
||||
{user && address && selection && (
|
||||
<button
|
||||
type="button"
|
||||
// Selection collapses on mousedown before onClick fires unless
|
||||
|
||||
@@ -321,7 +321,10 @@ function ArticleView({ event }: { event: NostrEvent }) {
|
||||
</header>
|
||||
|
||||
<HighlightLayer
|
||||
address={`${event.kind}:${event.pubkey}:${tagValue(event, 'd') ?? ''}`}
|
||||
// An empty string (rather than a malformed "kind:pubkey:" address)
|
||||
// when the article has no `d` tag — HighlightLayer treats a falsy
|
||||
// address as "highlighting isn't available for this article."
|
||||
address={tagValue(event, 'd') ? `${event.kind}:${event.pubkey}:${tagValue(event, 'd')}` : ''}
|
||||
authorPubkey={event.pubkey}
|
||||
>
|
||||
<div className="mt-6 text-[15px]">
|
||||
|
||||
Reference in New Issue
Block a user