diff --git a/src/components/BookmarkList.tsx b/src/components/BookmarkList.tsx index f2d11720..e4036142 100644 --- a/src/components/BookmarkList.tsx +++ b/src/components/BookmarkList.tsx @@ -10,8 +10,8 @@ import IconButton from './IconButton' import { ViewMode } from './Bookmarks' import { extractUrlsFromContent } from '../services/bookmarkHelpers' import { UserSettings } from '../services/settingsService' -import { usePullToRefresh } from '../hooks/usePullToRefresh' -import PullToRefreshIndicator from './PullToRefreshIndicator' +import { usePullToRefresh } from 'use-pull-to-refresh' +import RefreshIndicator from './RefreshIndicator' import { BookmarkSkeleton } from './Skeletons' interface BookmarkListProps { @@ -54,14 +54,15 @@ export const BookmarkList: React.FC = ({ const bookmarksListRef = useRef(null) // Pull-to-refresh for bookmarks - const pullToRefreshState = usePullToRefresh(bookmarksListRef, { + const { isRefreshing: isPulling, pullPosition } = usePullToRefresh({ onRefresh: () => { if (onRefresh) { onRefresh() } }, - isRefreshing: isRefreshing || false, - disabled: !onRefresh + maximumPullLength: 240, + refreshThreshold: 80, + isDisabled: !onRefresh }) // Helper to check if a bookmark has either content or a URL @@ -146,13 +147,11 @@ export const BookmarkList: React.FC = ({ ) : (
-
{allIndividualBookmarks.map((individualBookmark, index) => diff --git a/src/components/Explore.tsx b/src/components/Explore.tsx index 3ca08747..c9a13ca1 100644 --- a/src/components/Explore.tsx +++ b/src/components/Explore.tsx @@ -1,6 +1,6 @@ -import React, { useState, useEffect, useRef, useMemo } from 'react' +import React, { useState, useEffect, useMemo } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faExclamationCircle, faNewspaper, faPenToSquare, faHighlighter, faUser, faUserGroup, faNetworkWired } from '@fortawesome/free-solid-svg-icons' +import { faNewspaper, faPenToSquare, faHighlighter, faUser, faUserGroup, faNetworkWired } from '@fortawesome/free-solid-svg-icons' import IconButton from './IconButton' import { BlogPostSkeleton, HighlightSkeleton } from './Skeletons' import { Hooks } from 'applesauce-react' @@ -40,7 +40,6 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti const [highlights, setHighlights] = useState([]) const [followedPubkeys, setFollowedPubkeys] = useState>(new Set()) const [loading, setLoading] = useState(true) - const [error, setError] = useState(null) const [refreshTrigger, setRefreshTrigger] = useState(0) // Visibility filters (defaults from settings) @@ -60,7 +59,6 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti useEffect(() => { const loadData = async () => { if (!activeAccount) { - setError('Please log in to explore content from your friends') setLoading(false) return } @@ -68,7 +66,6 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti try { // show spinner but keep existing data setLoading(true) - setError(null) // Seed from in-memory cache if available to avoid empty flash const cachedPosts = getCachedPosts(activeAccount.pubkey) @@ -150,15 +147,8 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti } ) - if (contacts.size === 0) { - // If we already have any cached or previously shown data, do not block the UI. - const hasAnyData = (blogPosts.length > 0) || (highlights.length > 0) - if (!hasAnyData) { - // No friends and no cached content: set a soft hint, but still proceed to load nostrverse. - setError(null) - } - // Continue without returning: still fetch nostrverse content below. - } + // Always proceed to load nostrverse content even if no contacts + // (removed blocking error for empty contacts) // Store final followed pubkeys setFollowedPubkeys(contacts) @@ -205,12 +195,7 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti }) } - if (contacts.size === 0 && uniquePosts.length === 0 && uniqueHighlights.length === 0) { - setError('You are not following anyone yet. Follow some people to see their content!') - } else if (uniquePosts.length === 0 && uniqueHighlights.length === 0) { - setError('No content found yet') - } - + // No blocking errors - let empty states handle messaging setBlogPosts(uniquePosts) setCachedPosts(activeAccount.pubkey, uniquePosts) @@ -218,13 +203,15 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti setCachedHighlights(activeAccount.pubkey, uniqueHighlights) } catch (err) { console.error('Failed to load data:', err) - setError('Failed to load content. Please try again.') + // No blocking error - user can pull-to-refresh } finally { setLoading(false) } } loadData() + // Note: intentionally not including blogPosts/highlights length to avoid re-fetch loops + // eslint-disable-next-line react-hooks/exhaustive-deps }, [relayPool, activeAccount, refreshTrigger, eventStore, settings]) // Pull-to-refresh diff --git a/src/components/HighlightsPanel.tsx b/src/components/HighlightsPanel.tsx index 461de312..f2596c89 100644 --- a/src/components/HighlightsPanel.tsx +++ b/src/components/HighlightsPanel.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef } from 'react' +import React, { useState } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faHighlighter } from '@fortawesome/free-solid-svg-icons' import { Highlight } from '../types/highlights' diff --git a/src/components/Me.tsx b/src/components/Me.tsx index 34466095..45876095 100644 --- a/src/components/Me.tsx +++ b/src/components/Me.tsx @@ -1,6 +1,6 @@ -import React, { useState, useEffect, useRef } from 'react' +import React, { useState, useEffect } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faSpinner, faExclamationCircle, faHighlighter, faBookmark, faList, faThLarge, faImage, faPenToSquare } from '@fortawesome/free-solid-svg-icons' +import { faSpinner, faHighlighter, faBookmark, faList, faThLarge, faImage, faPenToSquare } from '@fortawesome/free-solid-svg-icons' import { Hooks } from 'applesauce-react' import { BlogPostSkeleton, HighlightSkeleton, BookmarkSkeleton } from './Skeletons' import { RelayPool } from 'applesauce-relay' @@ -24,7 +24,6 @@ import { getCachedMeData, setCachedMeData, updateCachedHighlights } from '../ser import { faBooks } from '../icons/customIcons' import { usePullToRefresh } from 'use-pull-to-refresh' import RefreshIndicator from './RefreshIndicator' -import { getProfileUrl } from '../config/nostrGateways' interface MeProps { relayPool: RelayPool @@ -47,7 +46,6 @@ const Me: React.FC = ({ relayPool, activeTab: propActiveTab, pubkey: pr const [readArticles, setReadArticles] = useState([]) const [writings, setWritings] = useState([]) const [loading, setLoading] = useState(true) - const [error, setError] = useState(null) const [viewMode, setViewMode] = useState('cards') const [refreshTrigger, setRefreshTrigger] = useState(0) @@ -61,14 +59,12 @@ const Me: React.FC = ({ relayPool, activeTab: propActiveTab, pubkey: pr useEffect(() => { const loadData = async () => { if (!viewingPubkey) { - setError(isOwnProfile ? 'Please log in to view your data' : 'Invalid profile') setLoading(false) return } try { setLoading(true) - setError(null) // Seed from cache if available to avoid empty flash (own profile only) if (isOwnProfile) { @@ -114,7 +110,7 @@ const Me: React.FC = ({ relayPool, activeTab: propActiveTab, pubkey: pr } } catch (err) { console.error('Failed to load data:', err) - setError('Failed to load data. Please try again.') + // No blocking error - user can pull-to-refresh } finally { setLoading(false) } diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index a6b6ff6d..fd210500 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -85,7 +85,7 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U const fullAccount = accountManager.getActive() if (!fullAccount) throw new Error('No active account') const factory = new EventFactory({ signer: fullAccount }) - await saveSettings(relayPool, eventStore, factory, newSettings, RELAYS) + await saveSettings(relayPool, eventStore, factory, newSettings) setSettings(newSettings) setToastType('success') setToastMessage('Settings saved') diff --git a/src/services/contactService.ts b/src/services/contactService.ts index 468777b3..d7c3e780 100644 --- a/src/services/contactService.ts +++ b/src/services/contactService.ts @@ -1,5 +1,4 @@ import { RelayPool } from 'applesauce-relay' -import { Observable } from 'rxjs' import { prioritizeLocalRelays } from '../utils/helpers' import { queryEvents } from './dataFetch' import { CONTACTS_REMOTE_TIMEOUT_MS } from '../config/network' diff --git a/src/services/exploreService.ts b/src/services/exploreService.ts index 189a5bdf..b4b4c122 100644 --- a/src/services/exploreService.ts +++ b/src/services/exploreService.ts @@ -39,7 +39,7 @@ export const fetchBlogPostsFromAuthors = async ( // Group by author + d-tag identifier const uniqueEvents = new Map() - const events = await queryEvents( + await queryEvents( relayPool, { kinds: [30023], authors: pubkeys, limit: 100 }, { diff --git a/src/services/highlightCreationService.ts b/src/services/highlightCreationService.ts index e5a93d21..36d30503 100644 --- a/src/services/highlightCreationService.ts +++ b/src/services/highlightCreationService.ts @@ -119,7 +119,7 @@ export async function createHighlight( const signedEvent = await factory.sign(highlightEvent) // Use unified write service to store and publish - await publishEvent(relayPool, eventStore, signedEvent, settings) + await publishEvent(relayPool, eventStore, signedEvent) // Check current connection status for UI feedback const connectedRelays = Array.from(relayPool.relays.values()) diff --git a/src/services/nostrverseService.ts b/src/services/nostrverseService.ts index 04a98431..586136b5 100644 --- a/src/services/nostrverseService.ts +++ b/src/services/nostrverseService.ts @@ -26,7 +26,7 @@ export const fetchNostrverseBlogPosts = async ( // Deduplicate replaceable events by keeping the most recent version const uniqueEvents = new Map() - const events = await queryEvents( + await queryEvents( relayPool, { kinds: [30023], limit }, { diff --git a/src/services/settingsService.ts b/src/services/settingsService.ts index 5e081106..16458601 100644 --- a/src/services/settingsService.ts +++ b/src/services/settingsService.ts @@ -148,8 +148,7 @@ export async function saveSettings( relayPool: RelayPool, eventStore: IEventStore, factory: EventFactory, - settings: UserSettings, - _relays: string[] + settings: UserSettings ): Promise { console.log('💾 Saving settings to nostr:', settings) @@ -165,7 +164,7 @@ export async function saveSettings( const signed = await factory.sign(draft) // Use unified write service - await publishEvent(relayPool, eventStore, signed, settings) + await publishEvent(relayPool, eventStore, signed) console.log('✅ Settings published successfully') } diff --git a/src/services/writeService.ts b/src/services/writeService.ts index 55ef4e17..7ef6aa67 100644 --- a/src/services/writeService.ts +++ b/src/services/writeService.ts @@ -1,7 +1,6 @@ import { RelayPool } from 'applesauce-relay' import { NostrEvent } from 'nostr-tools' import { IEventStore } from 'applesauce-core' -import { UserSettings } from './settingsService' import { RELAYS } from '../config/relays' import { isLocalRelay, areAllRelaysLocal } from '../utils/helpers' import { markEventAsOfflineCreated } from './offlineSyncService' @@ -13,8 +12,7 @@ import { markEventAsOfflineCreated } from './offlineSyncService' export async function publishEvent( relayPool: RelayPool, eventStore: IEventStore, - event: NostrEvent, - settings?: UserSettings + event: NostrEvent ): Promise { // Store the event in the local EventStore FIRST for immediate UI display eventStore.add(event)