diff --git a/src/App.tsx b/src/App.tsx index 68486c89..942b8f20 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -215,8 +215,7 @@ function App() { console.log('🔗 Created keep-alive subscription for', RELAYS.length, 'relay(s)') // Store subscription for cleanup - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(pool as any)._keepAliveSubscription = keepAliveSub + ;(pool as unknown as { _keepAliveSubscription: typeof keepAliveSub })._keepAliveSubscription = keepAliveSub // Attach address/replaceable loaders so ProfileModel can fetch profiles const addressLoader = createAddressLoader(pool, { @@ -235,10 +234,9 @@ function App() { accountsSub.unsubscribe() activeSub.unsubscribe() // Clean up keep-alive subscription if it exists - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if ((pool as any)._keepAliveSubscription) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (pool as any)._keepAliveSubscription.unsubscribe() + const poolWithSub = pool as unknown as { _keepAliveSubscription?: { unsubscribe: () => void } } + if (poolWithSub._keepAliveSubscription) { + poolWithSub._keepAliveSubscription.unsubscribe() } } } diff --git a/src/components/AddBookmarkModal.tsx b/src/components/AddBookmarkModal.tsx index 26601351..b5eff9c3 100644 --- a/src/components/AddBookmarkModal.tsx +++ b/src/components/AddBookmarkModal.tsx @@ -140,8 +140,7 @@ const AddBookmarkModal: React.FC = ({ onClose, onSave }) clearTimeout(fetchTimeoutRef.current) } } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [url]) // Only depend on url - title, description, tagsInput are intentionally checked but not dependencies + }, [url, title, description, tagsInput]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() diff --git a/src/components/BookmarkItem.tsx b/src/components/BookmarkItem.tsx index 0345bc37..bd97cbdd 100644 --- a/src/components/BookmarkItem.tsx +++ b/src/components/BookmarkItem.tsx @@ -11,17 +11,15 @@ import { getPreviewImage, fetchOgImage } from '../utils/imagePreview' import { CompactView } from './BookmarkViews/CompactView' import { LargeView } from './BookmarkViews/LargeView' import { CardView } from './BookmarkViews/CardView' -import { UserSettings } from '../services/settingsService' interface BookmarkItemProps { bookmark: IndividualBookmark index: number onSelectUrl?: (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => void viewMode?: ViewMode - settings?: UserSettings } -export const BookmarkItem: React.FC = ({ bookmark, index, onSelectUrl, viewMode = 'cards', settings }) => { +export const BookmarkItem: React.FC = ({ bookmark, index, onSelectUrl, viewMode = 'cards' }) => { const [ogImage, setOgImage] = useState(null) const short = (v: string) => `${v.slice(0, 8)}...${v.slice(-8)}` @@ -115,8 +113,7 @@ export const BookmarkItem: React.FC = ({ bookmark, index, onS getAuthorDisplayName, handleReadNow, articleImage, - articleSummary, - settings + articleSummary } if (viewMode === 'compact') { diff --git a/src/components/BookmarkList.tsx b/src/components/BookmarkList.tsx index e4036142..6b7a6244 100644 --- a/src/components/BookmarkList.tsx +++ b/src/components/BookmarkList.tsx @@ -9,7 +9,6 @@ import SidebarHeader from './SidebarHeader' import IconButton from './IconButton' import { ViewMode } from './Bookmarks' import { extractUrlsFromContent } from '../services/bookmarkHelpers' -import { UserSettings } from '../services/settingsService' import { usePullToRefresh } from 'use-pull-to-refresh' import RefreshIndicator from './RefreshIndicator' import { BookmarkSkeleton } from './Skeletons' @@ -29,7 +28,6 @@ interface BookmarkListProps { lastFetchTime?: number | null loading?: boolean relayPool: RelayPool | null - settings?: UserSettings isMobile?: boolean } @@ -48,7 +46,6 @@ export const BookmarkList: React.FC = ({ lastFetchTime, loading = false, relayPool, - settings, isMobile = false }) => { const bookmarksListRef = useRef(null) @@ -161,7 +158,6 @@ export const BookmarkList: React.FC = ({ index={index} onSelectUrl={onSelectUrl} viewMode={viewMode} - settings={settings} /> )} diff --git a/src/components/BookmarkViews/CardView.tsx b/src/components/BookmarkViews/CardView.tsx index 39361651..8af56045 100644 --- a/src/components/BookmarkViews/CardView.tsx +++ b/src/components/BookmarkViews/CardView.tsx @@ -10,7 +10,6 @@ import { classifyUrl } from '../../utils/helpers' import { IconGetter } from './shared' import { useImageCache } from '../../hooks/useImageCache' import { getPreviewImage, fetchOgImage } from '../../utils/imagePreview' -import { UserSettings } from '../../services/settingsService' import { getEventUrl } from '../../config/nostrGateways' interface CardViewProps { @@ -26,7 +25,6 @@ interface CardViewProps { handleReadNow: (e: React.MouseEvent) => void articleImage?: string articleSummary?: string - settings?: UserSettings } export const CardView: React.FC = ({ @@ -41,8 +39,7 @@ export const CardView: React.FC = ({ getAuthorDisplayName, handleReadNow, articleImage, - articleSummary, - settings + articleSummary }) => { const firstUrl = hasUrls ? extractedUrls[0] : null const firstUrlClassificationType = firstUrl ? classifyUrl(firstUrl)?.type : null @@ -59,7 +56,7 @@ export const CardView: React.FC = ({ // Determine which image to use (article image, instant preview, or OG image) const previewImage = articleImage || instantPreview || ogImage - const cachedImage = useImageCache(previewImage || undefined, settings) + const cachedImage = useImageCache(previewImage || undefined) // Fetch OG image if we don't have any other image React.useEffect(() => { diff --git a/src/components/BookmarkViews/CompactView.tsx b/src/components/BookmarkViews/CompactView.tsx index 86613d4b..f4d61499 100644 --- a/src/components/BookmarkViews/CompactView.tsx +++ b/src/components/BookmarkViews/CompactView.tsx @@ -5,7 +5,6 @@ import { IndividualBookmark } from '../../types/bookmarks' import { formatDateCompact } from '../../utils/bookmarkUtils' import ContentWithResolvedProfiles from '../ContentWithResolvedProfiles' import { useImageCache } from '../../hooks/useImageCache' -import { UserSettings } from '../../services/settingsService' interface CompactViewProps { bookmark: IndividualBookmark @@ -15,7 +14,6 @@ interface CompactViewProps { onSelectUrl?: (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => void articleImage?: string articleSummary?: string - settings?: UserSettings } export const CompactView: React.FC = ({ @@ -25,15 +23,14 @@ export const CompactView: React.FC = ({ extractedUrls, onSelectUrl, articleImage, - articleSummary, - settings + articleSummary }) => { const isArticle = bookmark.kind === 30023 const isWebBookmark = bookmark.kind === 39701 const isClickable = hasUrls || isArticle || isWebBookmark // Get cached image for thumbnail - const cachedImage = useImageCache(articleImage || undefined, settings) + const cachedImage = useImageCache(articleImage || undefined) const handleCompactClick = () => { if (!onSelectUrl) return diff --git a/src/components/BookmarkViews/LargeView.tsx b/src/components/BookmarkViews/LargeView.tsx index 116b3190..80001976 100644 --- a/src/components/BookmarkViews/LargeView.tsx +++ b/src/components/BookmarkViews/LargeView.tsx @@ -6,7 +6,6 @@ import { formatDate } from '../../utils/bookmarkUtils' import ContentWithResolvedProfiles from '../ContentWithResolvedProfiles' import { IconGetter } from './shared' import { useImageCache } from '../../hooks/useImageCache' -import { UserSettings } from '../../services/settingsService' import { getEventUrl } from '../../config/nostrGateways' interface LargeViewProps { @@ -22,7 +21,6 @@ interface LargeViewProps { getAuthorDisplayName: () => string handleReadNow: (e: React.MouseEvent) => void articleSummary?: string - settings?: UserSettings } export const LargeView: React.FC = ({ @@ -37,10 +35,9 @@ export const LargeView: React.FC = ({ eventNevent, getAuthorDisplayName, handleReadNow, - articleSummary, - settings + articleSummary }) => { - const cachedImage = useImageCache(previewImage || undefined, settings) + const cachedImage = useImageCache(previewImage || undefined) const isArticle = bookmark.kind === 30023 const triggerOpen = () => handleReadNow({ preventDefault: () => {} } as React.MouseEvent) diff --git a/src/components/Bookmarks.tsx b/src/components/Bookmarks.tsx index bddc1d6e..3718f036 100644 --- a/src/components/Bookmarks.tsx +++ b/src/components/Bookmarks.tsx @@ -130,8 +130,7 @@ const Bookmarks: React.FC = ({ relayPool, onLogout }) => { if (isMobile && isSidebarOpen) { toggleSidebar() } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location.pathname]) + }, [location.pathname, isMobile, isSidebarOpen, toggleSidebar]) // Handle highlight navigation from explore page useEffect(() => { diff --git a/src/components/Explore.tsx b/src/components/Explore.tsx index b02e81dd..ab7559fc 100644 --- a/src/components/Explore.tsx +++ b/src/components/Explore.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useMemo } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faNewspaper, faPenToSquare, faHighlighter, faUser, faUserGroup, faNetworkWired } from '@fortawesome/free-solid-svg-icons' +import { faNewspaper, faHighlighter, faUser, faUserGroup, faNetworkWired, faArrowsRotate } from '@fortawesome/free-solid-svg-icons' import IconButton from './IconButton' import { BlogPostSkeleton, HighlightSkeleton } from './Skeletons' import { Hooks } from 'applesauce-react' @@ -42,11 +42,11 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti const [loading, setLoading] = useState(true) const [refreshTrigger, setRefreshTrigger] = useState(0) - // Visibility filters (defaults from settings) + // Visibility filters (defaults from settings, or friends only) const [visibility, setVisibility] = useState({ - nostrverse: settings?.defaultHighlightVisibilityNostrverse !== false, - friends: settings?.defaultHighlightVisibilityFriends !== false, - mine: settings?.defaultHighlightVisibilityMine !== false + nostrverse: settings?.defaultHighlightVisibilityNostrverse ?? false, + friends: settings?.defaultHighlightVisibilityFriends ?? true, + mine: settings?.defaultHighlightVisibilityMine ?? false }) // Update local state when prop changes @@ -374,31 +374,18 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti Explore -

- Discover highlights and blog posts from your friends and others -

- -
- - -
{/* Visibility filters */} -
+
+ setRefreshTrigger(prev => prev + 1)} + title="Refresh content" + ariaLabel="Refresh content" + variant="ghost" + spin={loading || isRefreshing} + disabled={loading || isRefreshing} + /> setVisibility({ ...visibility, nostrverse: !visibility.nostrverse })} @@ -435,6 +422,25 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti }} />
+ +
+ + +
{renderTabContent()} diff --git a/src/components/ReaderHeader.tsx b/src/components/ReaderHeader.tsx index d6e348de..3a93f6ac 100644 --- a/src/components/ReaderHeader.tsx +++ b/src/components/ReaderHeader.tsx @@ -33,7 +33,7 @@ const ReaderHeader: React.FC = ({ highlights = [], highlightVisibility = { nostrverse: true, friends: true, mine: true } }) => { - const cachedImage = useImageCache(image, settings) + const cachedImage = useImageCache(image) const formattedDate = published ? format(new Date(published * 1000), 'MMM d, yyyy') : null const isLongSummary = summary && summary.length > 150 diff --git a/src/components/ThreePaneLayout.tsx b/src/components/ThreePaneLayout.tsx index 016f3650..05036530 100644 --- a/src/components/ThreePaneLayout.tsx +++ b/src/components/ThreePaneLayout.tsx @@ -303,7 +303,6 @@ const ThreePaneLayout: React.FC = (props) => { lastFetchTime={props.lastFetchTime} loading={props.bookmarksLoading} relayPool={props.relayPool} - settings={props.settings} isMobile={isMobile} /> diff --git a/src/hooks/useBookmarksData.ts b/src/hooks/useBookmarksData.ts index 2eb550f4..6505ad6a 100644 --- a/src/hooks/useBookmarksData.ts +++ b/src/hooks/useBookmarksData.ts @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback } from 'react' import { RelayPool } from 'applesauce-relay' +import { IAccount, AccountManager } from 'applesauce-accounts' import { Bookmark } from '../types/bookmarks' import { Highlight } from '../types/highlights' import { fetchBookmarks } from '../services/bookmarkService' @@ -9,10 +10,8 @@ import { UserSettings } from '../services/settingsService' interface UseBookmarksDataParams { relayPool: RelayPool | null - // eslint-disable-next-line @typescript-eslint/no-explicit-any - activeAccount: any - // eslint-disable-next-line @typescript-eslint/no-explicit-any - accountManager: any + activeAccount: IAccount | undefined + accountManager: AccountManager naddr?: string currentArticleCoordinate?: string currentArticleEventId?: string diff --git a/src/hooks/useHighlightCreation.ts b/src/hooks/useHighlightCreation.ts index 2539b8cb..0a9bfafe 100644 --- a/src/hooks/useHighlightCreation.ts +++ b/src/hooks/useHighlightCreation.ts @@ -3,6 +3,7 @@ import { flushSync } from 'react-dom' import { RelayPool } from 'applesauce-relay' import { NostrEvent } from 'nostr-tools' import { IEventStore } from 'applesauce-core' +import { IAccount } from 'applesauce-accounts' import { Highlight } from '../types/highlights' import { ReadableContent } from '../services/readerService' import { createHighlight } from '../services/highlightCreationService' @@ -10,8 +11,7 @@ import { HighlightButtonRef } from '../components/HighlightButton' import { UserSettings } from '../services/settingsService' interface UseHighlightCreationParams { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - activeAccount: any + activeAccount: IAccount | undefined relayPool: RelayPool | null eventStore: IEventStore | null currentArticle: NostrEvent | undefined diff --git a/src/hooks/useImageCache.ts b/src/hooks/useImageCache.ts index f3d52b90..6d93fe41 100644 --- a/src/hooks/useImageCache.ts +++ b/src/hooks/useImageCache.ts @@ -1,5 +1,3 @@ -import { UserSettings } from '../services/settingsService' - /** * Hook to return image URL for display * Service Worker handles all caching transparently @@ -9,9 +7,7 @@ import { UserSettings } from '../services/settingsService' * @returns The image URL (Service Worker handles caching) */ export function useImageCache( - imageUrl: string | undefined, - // eslint-disable-next-line no-unused-vars - _settings?: UserSettings + imageUrl: string | undefined ): string | undefined { // Service Worker handles everything - just return the URL as-is return imageUrl @@ -22,9 +18,7 @@ export function useImageCache( * Triggers a fetch so the SW can cache it even if not visible yet */ export function useCacheImageOnLoad( - imageUrl: string | undefined, - // eslint-disable-next-line no-unused-vars - _settings?: UserSettings + imageUrl: string | undefined ): void { // Service Worker will cache on first fetch // This hook is now a no-op, kept for API compatibility diff --git a/src/services/dataFetch.ts b/src/services/dataFetch.ts index f4f31bb4..011c71d2 100644 --- a/src/services/dataFetch.ts +++ b/src/services/dataFetch.ts @@ -1,6 +1,7 @@ import { RelayPool, completeOnEose, onlyEvents } from 'applesauce-relay' import { Observable, merge, takeUntil, timer, toArray, tap, lastValueFrom } from 'rxjs' import { NostrEvent } from 'nostr-tools' +import { Filter } from 'nostr-tools/filter' import { prioritizeLocalRelays, partitionRelays } from '../utils/helpers' import { LOCAL_TIMEOUT_MS, REMOTE_TIMEOUT_MS } from '../config/network' @@ -17,8 +18,7 @@ export interface QueryOptions { */ export async function queryEvents( relayPool: RelayPool, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - filter: any, + filter: Filter, options: QueryOptions = {} ): Promise { const { diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 071dac6a..b3fcafa4 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -102,13 +102,13 @@ export const prioritizeLocalRelays = (relayUrls: string[]): string[] => { // Parallel request helper import { completeOnEose, onlyEvents, RelayPool } from 'applesauce-relay' import { Observable, takeUntil, timer } from 'rxjs' +import { Filter } from 'nostr-tools/filter' export function createParallelReqStreams( relayPool: RelayPool, localRelays: string[], remoteRelays: string[], - // eslint-disable-next-line @typescript-eslint/no-explicit-any - filter: any, + filter: Filter, localTimeoutMs = 1200, remoteTimeoutMs = 6000 ): { local$: Observable; remote$: Observable } {