diff --git a/src/components/CacheRelaysSetting/index.tsx b/src/components/CacheRelaysSetting/index.tsx index 9887e6a..9c11e28 100644 --- a/src/components/CacheRelaysSetting/index.tsx +++ b/src/components/CacheRelaysSetting/index.tsx @@ -2,7 +2,7 @@ import { Button } from '@/components/ui/button' import { normalizeUrl, isLocalNetworkUrl } from '@/lib/url' import { useNostr } from '@/providers/NostrProvider' import { TMailboxRelay, TMailboxRelayScope } from '@/types' -import { useEffect, useState, useMemo } from 'react' +import { useEffect, useState, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { DndContext, @@ -45,6 +45,7 @@ export default function CacheRelaysSetting() { const [relays, setRelays] = useState([]) const [hasChange, setHasChange] = useState(false) const [pushing, setPushing] = useState(false) + const justSavedRef = useRef(false) const [cacheInfo, setCacheInfo] = useState>({}) const [browsingCache, setBrowsingCache] = useState(false) const [selectedStore, setSelectedStore] = useState(null) @@ -92,8 +93,32 @@ export default function CacheRelaysSetting() { } const cacheRelayList = getRelayListFromEvent(cacheRelayListEvent) - setRelays(cacheRelayList.originalRelays) - setHasChange(false) + const newRelays = cacheRelayList.originalRelays + + // Use functional update to compare with current state + setRelays((currentRelays) => { + // Check if relays are actually different (deep comparison) + const areRelaysEqual = + newRelays.length === currentRelays.length && + newRelays.every((relay, index) => + relay.url === currentRelays[index]?.url && + relay.scope === currentRelays[index]?.scope + ) + + // Only update and reset hasChange if relays actually changed AND we just saved + // This prevents resetting hasChange when user is actively making changes + if (!areRelaysEqual) { + if (justSavedRef.current) { + // We just saved, so this update is expected - reset hasChange + justSavedRef.current = false + setHasChange(false) + } + return newRelays + } + + // If relays are equal, don't update state (prevents unnecessary re-render) + return currentRelays + }) }, [cacheRelayListEvent]) if (!pubkey) { @@ -369,8 +394,9 @@ export default function CacheRelaysSetting() { try { const event = createCacheRelaysDraftEvent(relays) const result = await publish(event) + // Set flag before updating so useEffect knows to reset hasChange + justSavedRef.current = true await updateCacheRelayListEvent(result) - setHasChange(false) // Show publishing feedback if ((result as any).relayStatuses) { @@ -387,6 +413,8 @@ export default function CacheRelaysSetting() { showSimplePublishSuccess(t('Cache relays saved')) } } catch (error) { + // Reset flag on error + justSavedRef.current = false console.error('Failed to save cache relays:', error) // Show error feedback if (error instanceof Error && (error as any).relayStatuses) { diff --git a/src/providers/NostrProvider/index.tsx b/src/providers/NostrProvider/index.tsx index d96cd74..11535c7 100644 --- a/src/providers/NostrProvider/index.tsx +++ b/src/providers/NostrProvider/index.tsx @@ -29,7 +29,8 @@ import { TDraftEvent, TProfile, TPublishOptions, - TRelayList + TRelayList, + TMailboxRelay } from '@/types' import { hexToBytes } from '@noble/hashes/utils' import dayjs from 'dayjs' @@ -265,8 +266,41 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { setBlockedRelaysEvent(storedBlockedRelaysEvent) } - if (storedRelayListEvent) { - setRelayList(getRelayListFromEvent(storedRelayListEvent, blockedRelays)) + // Set initial relay list from stored events (will be updated with merged list later) + // Merge cache relays even at initial load so cache relays are available immediately + if (storedRelayListEvent || storedCacheRelayListEvent) { + const baseRelayList = storedRelayListEvent + ? getRelayListFromEvent(storedRelayListEvent, blockedRelays) + : { write: [], read: [], originalRelays: [] } + + // Merge cache relays if available + if (storedCacheRelayListEvent) { + const cacheRelayList = getRelayListFromEvent(storedCacheRelayListEvent) + + // Merge read relays - cache relays first, then others (for offline priority) + const mergedRead = [...cacheRelayList.read, ...baseRelayList.read] + const mergedWrite = [...cacheRelayList.write, ...baseRelayList.write] + const mergedOriginalRelays = new Map() + + // Add cache relay original relays first (prioritized) + cacheRelayList.originalRelays.forEach(relay => { + mergedOriginalRelays.set(relay.url, relay) + }) + // Then add regular relay original relays + baseRelayList.originalRelays.forEach(relay => { + if (!mergedOriginalRelays.has(relay.url)) { + mergedOriginalRelays.set(relay.url, relay) + } + }) + + setRelayList({ + write: Array.from(new Set(mergedWrite)), + read: Array.from(new Set(mergedRead)), + originalRelays: Array.from(mergedOriginalRelays.values()) + }) + } else { + setRelayList(baseRelayList) + } } if (storedProfileEvent) { setProfileEvent(storedProfileEvent)