diff --git a/src/components/NotificationList/index.tsx b/src/components/NotificationList/index.tsx index 6c0ca88..4ebc1a6 100644 --- a/src/components/NotificationList/index.tsx +++ b/src/components/NotificationList/index.tsx @@ -1,9 +1,10 @@ -import { BIG_RELAY_URLS, ExtendedKind, NOTIFICATION_LIST_STYLE } from '@/constants' +import { ExtendedKind, NOTIFICATION_LIST_STYLE, FAST_READ_RELAY_URLS } from '@/constants' import { compareEvents } from '@/lib/event' import { usePrimaryPage } from '@/PageManager' import { useNostr } from '@/providers/NostrProvider' import { useNotification } from '@/providers/NotificationProvider' import { useUserPreferences } from '@/providers/UserPreferencesProvider' +import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' import client from '@/services/client.service' import noteStatsService from '@/services/note-stats.service' import { TNotificationType } from '@/types' @@ -33,9 +34,10 @@ const NotificationList = forwardRef((_, ref) => { const { t } = useTranslation() const { current, display } = usePrimaryPage() const active = useMemo(() => current === 'notifications' && display, [current, display]) - const { pubkey } = useNostr() + const { pubkey, relayList } = useNostr() const { getNotificationsSeenAt } = useNotification() const { notificationListStyle } = useUserPreferences() + const { favoriteRelays } = useFavoriteRelays() const [notificationType, setNotificationType] = useState('all') const [lastReadTime, setLastReadTime] = useState(0) const [refreshCount, setRefreshCount] = useState(0) @@ -121,7 +123,27 @@ const NotificationList = forwardRef((_, ref) => { setNotifications([]) setShowCount(SHOW_COUNT) setLastReadTime(getNotificationsSeenAt()) - const relayList = await client.fetchRelayList(pubkey) + // Use proper fallback hierarchy: user's read/inbox relays → favorite relays → fast read relays + const userRelayList = relayList || { read: [], write: [] } + const userReadRelays = userRelayList.read || [] + const userFavoriteRelays = favoriteRelays || [] + + // Build relay list with proper fallback hierarchy + let primaryRelays: string[] = [] + + if (userReadRelays.length > 0) { + // Priority 1: User's read/inbox relays (kind 10002) + primaryRelays = userReadRelays.slice(0, 5) + console.debug('[NotificationList] Using user read relays:', primaryRelays.length, 'relays') + } else if (userFavoriteRelays.length > 0) { + // Priority 2: User's favorite relays (kind 10012) + primaryRelays = userFavoriteRelays.slice(0, 5) + console.debug('[NotificationList] Using user favorite relays:', primaryRelays.length, 'relays') + } else { + // Priority 3: Fast read relays (reliable defaults) + primaryRelays = FAST_READ_RELAY_URLS.slice(0, 5) + console.debug('[NotificationList] Using fast read relays fallback:', primaryRelays.length, 'relays') + } // Create separate subscriptions for different notification types const subscriptions = [] @@ -130,7 +152,7 @@ const NotificationList = forwardRef((_, ref) => { const mentionKinds = filterKinds.filter(kind => kind !== 11) if (mentionKinds.length > 0) { subscriptions.push({ - urls: relayList.read.length > 0 ? relayList.read.slice(0, 5) : BIG_RELAY_URLS, + urls: primaryRelays, filter: { '#p': [pubkey], kinds: mentionKinds, @@ -142,7 +164,7 @@ const NotificationList = forwardRef((_, ref) => { // Separate subscription for discussion notifications (kind 11) - no p-tag requirement if (filterKinds.includes(11)) { subscriptions.push({ - urls: relayList.read.length > 0 ? relayList.read.slice(0, 5) : BIG_RELAY_URLS, + urls: primaryRelays, filter: { kinds: [11], limit: LIMIT diff --git a/src/providers/NotificationProvider.tsx b/src/providers/NotificationProvider.tsx index 36e93da..553361b 100644 --- a/src/providers/NotificationProvider.tsx +++ b/src/providers/NotificationProvider.tsx @@ -1,7 +1,8 @@ -import { BIG_RELAY_URLS, ExtendedKind } from '@/constants' +import { ExtendedKind, FAST_READ_RELAY_URLS } from '@/constants' import { compareEvents } from '@/lib/event' import { notificationFilter } from '@/lib/notification' import { usePrimaryPage } from '@/PageManager' +import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' import client from '@/services/client.service' import storage from '@/services/local-storage.service' import { kinds, NostrEvent } from 'nostr-tools' @@ -33,7 +34,8 @@ export const useNotification = () => { export function NotificationProvider({ children }: { children: React.ReactNode }) { const { current } = usePrimaryPage() const active = useMemo(() => current === 'notifications', [current]) - const { pubkey, notificationsSeenAt, updateNotificationsSeenAt } = useNostr() + const { pubkey, relayList, notificationsSeenAt, updateNotificationsSeenAt } = useNostr() + const { favoriteRelays } = useFavoriteRelays() const { hideUntrustedNotifications, isUserTrusted } = useUserTrust() const { mutePubkeySet } = useMuteList() const { hideContentMentioningMutedUsers } = useContentPolicy() @@ -106,8 +108,27 @@ export function NotificationProvider({ children }: { children: React.ReactNode } try { let eosed = false - const relayList = await client.fetchRelayList(pubkey) - const notificationRelays = relayList.read.length > 0 ? relayList.read.slice(0, 5) : BIG_RELAY_URLS + // Use proper fallback hierarchy: user's read/inbox relays → favorite relays → fast read relays + const userRelayList = relayList || { read: [], write: [] } + const userReadRelays = userRelayList.read || [] + const userFavoriteRelays = favoriteRelays || [] + + // Build relay list with proper fallback hierarchy + let notificationRelays: string[] = [] + + if (userReadRelays.length > 0) { + // Priority 1: User's read/inbox relays (kind 10002) + notificationRelays = userReadRelays.slice(0, 5) + console.debug('[NotificationProvider] Using user read relays:', notificationRelays.length, 'relays') + } else if (userFavoriteRelays.length > 0) { + // Priority 2: User's favorite relays (kind 10012) + notificationRelays = userFavoriteRelays.slice(0, 5) + console.debug('[NotificationProvider] Using user favorite relays:', notificationRelays.length, 'relays') + } else { + // Priority 3: Fast read relays (reliable defaults) + notificationRelays = FAST_READ_RELAY_URLS.slice(0, 5) + console.debug('[NotificationProvider] Using fast read relays fallback:', notificationRelays.length, 'relays') + } // Subscribe to discussion notifications (kind 11) // Subscribe to all discussions, not just subscribed topics diff --git a/src/services/client.service.ts b/src/services/client.service.ts index a60580c..3b5fbca 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -56,15 +56,15 @@ class ClientService extends EventTarget { { cacheMap: this.eventCacheMap } ) private requestThrottle = new Map() // Track request timestamps per relay - private readonly REQUEST_COOLDOWN = 3000 // 3 second cooldown between requests to prevent "too many REQs" + private readonly REQUEST_COOLDOWN = 1000 // 1 second cooldown between requests (reduced from 3s) private failureCount = new Map() // Track consecutive failures per relay - private readonly MAX_FAILURES = 1 // Max failures before exponential backoff (reduced to 1 for faster circuit breaker activation) + private readonly MAX_FAILURES = 3 // Max failures before exponential backoff (increased from 1 for better reliability) private circuitBreaker = new Map() // Track when relays are temporarily disabled - private readonly CIRCUIT_BREAKER_TIMEOUT = 60000 // 60 second timeout for circuit breaker (increased for better stability) + private readonly CIRCUIT_BREAKER_TIMEOUT = 30000 // 30 second timeout for circuit breaker (reduced from 60s) private concurrentRequests = new Map() // Track concurrent requests per relay - private readonly MAX_CONCURRENT_REQUESTS = 1 // Max concurrent requests per relay (reduced to prevent "too many REQs") + private readonly MAX_CONCURRENT_REQUESTS = 2 // Max concurrent requests per relay (increased from 1) private globalRequestThrottle = 0 // Global request throttle to prevent overwhelming all relays - private readonly GLOBAL_REQUEST_COOLDOWN = 1000 // 1 second global cooldown between any relay requests + private readonly GLOBAL_REQUEST_COOLDOWN = 500 // 0.5 second global cooldown (reduced from 1s) private blacklistedRelays = new Map() // Temporarily blacklist problematic relays private readonly BLACKLIST_TIMEOUT = 300000 // 5 minutes blacklist timeout