diff --git a/package.json b/package.json index df0d9f9f..a82eecc2 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,8 @@ "parser": "@typescript-eslint/parser", "plugins": [ "@typescript-eslint", - "react-refresh" + "react-refresh", + "react-hooks" ], "rules": { "react-refresh/only-export-components": [ @@ -68,6 +69,7 @@ "allowConstantExport": true } ], + "react-hooks/exhaustive-deps": "warn", "@typescript-eslint/no-unused-vars": [ "error", { diff --git a/src/components/AddBookmarkModal.tsx b/src/components/AddBookmarkModal.tsx index 5d6e7c0c..feaf3ace 100644 --- a/src/components/AddBookmarkModal.tsx +++ b/src/components/AddBookmarkModal.tsx @@ -139,7 +139,8 @@ const AddBookmarkModal: React.FC = ({ onClose, onSave }) clearTimeout(fetchTimeoutRef.current) } } - }, [url]) // Only depend on url + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [url]) // Only depend on url - title, description, tagsInput are intentionally checked but not dependencies const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() diff --git a/src/components/RelayStatusIndicator.tsx b/src/components/RelayStatusIndicator.tsx index 0684399d..1926171d 100644 --- a/src/components/RelayStatusIndicator.tsx +++ b/src/components/RelayStatusIndicator.tsx @@ -52,7 +52,7 @@ export const RelayStatusIndicator: React.FC = ({ rela hasRemoteRelay, isConnecting }) - }, [offlineMode, localOnlyMode, connectedUrls.length, relayStatuses.length, hasLocalRelay, hasRemoteRelay, isConnecting]) + }, [offlineMode, localOnlyMode, connectedUrls, relayStatuses.length, hasLocalRelay, hasRemoteRelay, isConnecting]) // Don't show indicator when fully connected (but show when connecting) if (!localOnlyMode && !offlineMode && !isConnecting) return null diff --git a/src/components/ThreePaneLayout.tsx b/src/components/ThreePaneLayout.tsx index ea588fc2..de118c52 100644 --- a/src/components/ThreePaneLayout.tsx +++ b/src/components/ThreePaneLayout.tsx @@ -113,22 +113,24 @@ const ThreePaneLayout: React.FC = (props) => { // Handle ESC key to close sidebar or highlights useEffect(() => { + const { isSidebarOpen, isHighlightsCollapsed, onToggleSidebar, onToggleHighlightsPanel } = props + if (!isMobile) return - if (!props.isSidebarOpen && props.isHighlightsCollapsed) return + if (!isSidebarOpen && isHighlightsCollapsed) return const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') { - if (props.isSidebarOpen) { - props.onToggleSidebar() - } else if (!props.isHighlightsCollapsed) { - props.onToggleHighlightsPanel() + if (isSidebarOpen) { + onToggleSidebar() + } else if (!isHighlightsCollapsed) { + onToggleHighlightsPanel() } } } document.addEventListener('keydown', handleEscape) return () => document.removeEventListener('keydown', handleEscape) - }, [isMobile, props.isSidebarOpen, props.isHighlightsCollapsed, props.onToggleSidebar, props.onToggleHighlightsPanel]) + }, [isMobile, props]) // Trap focus in sidebar when open on mobile useEffect(() => { diff --git a/src/hooks/useBookmarksData.ts b/src/hooks/useBookmarksData.ts index 68a3766f..e9c519f8 100644 --- a/src/hooks/useBookmarksData.ts +++ b/src/hooks/useBookmarksData.ts @@ -110,7 +110,7 @@ export const useBookmarksData = ({ handleFetchHighlights() } handleFetchContacts() - }, [relayPool, activeAccount?.pubkey, naddr, handleFetchBookmarks, handleFetchHighlights, handleFetchContacts]) + }, [relayPool, activeAccount, naddr, handleFetchBookmarks, handleFetchHighlights, handleFetchContacts]) return { bookmarks, diff --git a/src/utils/nostrUriResolver.tsx b/src/utils/nostrUriResolver.tsx index 804faddb..1542760b 100644 --- a/src/utils/nostrUriResolver.tsx +++ b/src/utils/nostrUriResolver.tsx @@ -1,6 +1,4 @@ -import React from 'react' import { decode, npubEncode, noteEncode } from 'nostr-tools/nip19' -import { DecodeResult } from 'nostr-tools/nip19' /** * Regular expression to match nostr: URIs and bare NIP-19 identifiers @@ -75,15 +73,17 @@ export function getNostrUriLabel(encoded: string): string { switch (decoded.type) { case 'npub': return `@${encoded.slice(0, 12)}...` - case 'nprofile': + case 'nprofile': { const npub = npubEncode(decoded.data.pubkey) return `@${npub.slice(0, 12)}...` + } case 'note': return `note:${encoded.slice(5, 12)}...` - case 'nevent': + case 'nevent': { const note = noteEncode(decoded.data.id) return `note:${note.slice(5, 12)}...` - case 'naddr': + } + case 'naddr': { // For articles, show the identifier if available const identifier = decoded.data.identifier if (identifier && identifier.length > 0) { @@ -91,6 +91,7 @@ export function getNostrUriLabel(encoded: string): string { return identifier.length > 40 ? `${identifier.slice(0, 37)}...` : identifier } return 'nostr article' + } default: return encoded.slice(0, 16) + '...' } @@ -166,11 +167,11 @@ export function replaceNostrUrisInHTML(html: string): string { */ export function getNostrUriInfo(encoded: string): { type: string - decoded: DecodeResult | null + decoded: ReturnType | null link: string label: string } { - let decoded: DecodeResult | null = null + let decoded: ReturnType | null = null try { decoded = decode(encoded) } catch (error) {