Browse Source

show spinnners when fetching and loading

imwald
Silberengel 1 month ago
parent
commit
baceadf7a3
  1. 33
      src/components/NoteList/index.tsx
  2. 4
      src/constants.ts
  3. 7
      src/pages/primary/SpellsPage/index.tsx

33
src/components/NoteList/index.tsx

@ -41,6 +41,7 @@ import PullToRefresh from 'react-simple-pull-to-refresh'
import { formatPubkey, pubkeyToNpub } from '@/lib/pubkey' import { formatPubkey, pubkeyToNpub } from '@/lib/pubkey'
import { NoteFeedProfileContext, type NoteFeedProfileContextValue } from '@/providers/NoteFeedProfileContext' import { NoteFeedProfileContext, type NoteFeedProfileContextValue } from '@/providers/NoteFeedProfileContext'
import type { TProfile } from '@/types' import type { TProfile } from '@/types'
import { Loader2 } from 'lucide-react'
import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard' import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard'
const LIMIT = 100 // Increased from 200 to load more events per request const LIMIT = 100 // Increased from 200 to load more events per request
@ -87,7 +88,8 @@ const NoteList = forwardRef(
*/ */
preserveTimelineOnSubRequestsChange = false, preserveTimelineOnSubRequestsChange = false,
/** /**
* Spells page: after this many ms, clear the loading skeleton so the list area renders; subscription keeps running. * Spells / one-shot feeds: when the initial fetch finishes with zero rows, show explicit empty copy
* (see list footer). Does not end loading early loading stays until EOSE, first events, or safety timeouts.
*/ */
spellFetchTimeoutMs, spellFetchTimeoutMs,
/** Spells page: bumps when user picks a feed; used with {@link onSpellFeedFirstPaint}. */ /** Spells page: bumps when user picks a feed; used with {@link onSpellFeedFirstPaint}. */
@ -117,7 +119,7 @@ const NoteList = forwardRef(
extraShouldHideEvent?: (evt: Event) => boolean extraShouldHideEvent?: (evt: Event) => boolean
feedSubscriptionKey?: string feedSubscriptionKey?: string
preserveTimelineOnSubRequestsChange?: boolean preserveTimelineOnSubRequestsChange?: boolean
/** When set (spells), max time to show the initial loading skeleton (ms). */ /** When set (e.g. spells), use explicit empty-feed copy after load completes with no rows. */
spellFetchTimeoutMs?: number spellFetchTimeoutMs?: number
spellFeedInstrumentToken?: number spellFeedInstrumentToken?: number
onSpellFeedFirstPaint?: (detail: { eventCount: number; firstEventId: string }) => void onSpellFeedFirstPaint?: (detail: { eventCount: number; firstEventId: string }) => void
@ -732,7 +734,6 @@ const NoteList = forwardRef(
showKind1111, showKind1111,
useFilterAsIs, useFilterAsIs,
areAlgoRelays, areAlgoRelays,
spellFetchTimeoutMs,
oneShotFetch oneShotFetch
]) ])
@ -758,21 +759,6 @@ const NoteList = forwardRef(
} }
}, [timelineSubscriptionKey, refreshCount]) }, [timelineSubscriptionKey, refreshCount])
/** Spells: drop loading skeleton quickly so rows (or empty + reload) appear while REQ continues. */
useEffect(() => {
if (spellFetchTimeoutMs == null || spellFetchTimeoutMs <= 0) return
if (!subRequestsRef.current.length) return
let cancelled = false
const id = window.setTimeout(() => {
if (cancelled) return
setLoading(false)
}, spellFetchTimeoutMs)
return () => {
cancelled = true
clearTimeout(id)
}
}, [timelineSubscriptionKey, refreshCount, spellFetchTimeoutMs])
// Use refs to avoid dependency issues and ensure latest values in async callbacks // Use refs to avoid dependency issues and ensure latest values in async callbacks
const showCountRef = useRef(showCount) const showCountRef = useRef(showCount)
const loadingRef = useRef(loading) const loadingRef = useRef(loading)
@ -1132,8 +1118,15 @@ const NoteList = forwardRef(
/> />
))} ))}
{events.length === 0 && loading ? ( {events.length === 0 && loading ? (
<div ref={bottomRef}> <div
<NoteCardLoadingSkeleton /> ref={bottomRef}
className="flex min-h-[40vh] flex-col items-center justify-center gap-3 px-4 py-8"
role="status"
aria-live="polite"
aria-busy="true"
>
<Loader2 className="size-8 shrink-0 animate-spin text-muted-foreground" aria-hidden />
<p className="text-sm text-muted-foreground">{t('Loading...')}</p>
</div> </div>
) : events.length > 0 && (hasMore || loading) ? ( ) : events.length > 0 && (hasMore || loading) ? (
<div ref={bottomRef}> <div ref={bottomRef}>

4
src/constants.ts

@ -29,10 +29,10 @@ export const MAX_REQ_RELAY_URLS = MAX_CONCURRENT_RELAY_CONNECTIONS
/** Multi-relay queries and timeline initial REQ: after the first event, wait this long then close (query) or finalize EOSE (live feed) while keeping the subscription open for new events. */ /** Multi-relay queries and timeline initial REQ: after the first event, wait this long then close (query) or finalize EOSE (live feed) while keeping the subscription open for new events. */
export const FIRST_RELAY_RESULT_GRACE_MS = 2000 export const FIRST_RELAY_RESULT_GRACE_MS = 2000
/** Spells page NoteList: drop the loading skeleton after this long so the feed can render; REQ stays open and rows stream in. */ /** Legacy name: was used to cap spell NoteList skeleton time; loading now ends on EOSE / first events / safety timeouts. Kept for forks. */
export const SPELL_FEED_LOADING_MAX_MS = 1000 export const SPELL_FEED_LOADING_MAX_MS = 1000
/** @deprecated Use {@link SPELL_FEED_LOADING_MAX_MS}; kept so old imports do not break. */ /** @deprecated Alias of {@link SPELL_FEED_LOADING_MAX_MS}. */
export const SPELL_FEED_FIRST_RELAY_GRACE_MS = SPELL_FEED_LOADING_MAX_MS export const SPELL_FEED_FIRST_RELAY_GRACE_MS = SPELL_FEED_LOADING_MAX_MS
/** /**

7
src/pages/primary/SpellsPage/index.tsx

@ -37,8 +37,7 @@ import {
ExtendedKind, ExtendedKind,
FAUX_SPELL_ORDER, FAUX_SPELL_ORDER,
FIRST_RELAY_RESULT_GRACE_MS, FIRST_RELAY_RESULT_GRACE_MS,
PROFILE_FEED_KINDS, PROFILE_FEED_KINDS
SPELL_FEED_LOADING_MAX_MS
} from '@/constants' } from '@/constants'
import { isUserInEventMentions } from '@/lib/event' import { isUserInEventMentions } from '@/lib/event'
import { formatPubkey } from '@/lib/pubkey' import { formatPubkey } from '@/lib/pubkey'
@ -1334,7 +1333,7 @@ const SpellsPage = forwardRef<TPageRef>(function SpellsPage(
subRequests={subRequests} subRequests={subRequests}
feedSubscriptionKey={spellFeedSubscriptionKey} feedSubscriptionKey={spellFeedSubscriptionKey}
showKinds={showKinds} showKinds={showKinds}
spellFetchTimeoutMs={SPELL_FEED_LOADING_MAX_MS} spellFetchTimeoutMs={1}
spellFeedInstrumentToken={spellFeedInstrumentToken} spellFeedInstrumentToken={spellFeedInstrumentToken}
onSpellFeedFirstPaint={handleSpellFeedFirstPaint} onSpellFeedFirstPaint={handleSpellFeedFirstPaint}
useFilterAsIs={fauxNoteListUseFilterAsIs} useFilterAsIs={fauxNoteListUseFilterAsIs}
@ -1361,7 +1360,7 @@ const SpellsPage = forwardRef<TPageRef>(function SpellsPage(
subRequests={subRequests} subRequests={subRequests}
feedSubscriptionKey={spellFeedSubscriptionKey} feedSubscriptionKey={spellFeedSubscriptionKey}
showKinds={showKinds} showKinds={showKinds}
spellFetchTimeoutMs={SPELL_FEED_LOADING_MAX_MS} spellFetchTimeoutMs={1}
spellFeedInstrumentToken={spellFeedInstrumentToken} spellFeedInstrumentToken={spellFeedInstrumentToken}
onSpellFeedFirstPaint={handleSpellFeedFirstPaint} onSpellFeedFirstPaint={handleSpellFeedFirstPaint}
useFilterAsIs useFilterAsIs

Loading…
Cancel
Save