diff --git a/src/components/NoteList/index.tsx b/src/components/NoteList/index.tsx index 86cd06a9..1441823d 100644 --- a/src/components/NoteList/index.tsx +++ b/src/components/NoteList/index.tsx @@ -92,6 +92,7 @@ const NoteList = forwardRef< const supportTouch = useMemo(() => isTouchDevice(), []) const bottomRef = useRef(null) const topRef = useRef(null) + const loadingRef = useRef(false) const shouldHideEvent = useCallback( (evt: Event) => { @@ -273,12 +274,14 @@ const NoteList = forwardRef< if (!subRequests.length) return async function init() { + loadingRef.current = true setLoading(true) setEvents([]) setNewEvents([]) setHasMore(true) if (showKinds?.length === 0 && subRequests.every(({ filter }) => !filter.kinds)) { + loadingRef.current = false setLoading(false) setHasMore(false) return () => {} @@ -309,6 +312,7 @@ const NoteList = forwardRef< setHasMore(false) } if (eosed) { + loadingRef.current = false setLoading(false) addReplies(events) } @@ -374,13 +378,15 @@ const NoteList = forwardRef< } } - if (!timelineKey || loading || !hasMore) return + if (!timelineKey || loadingRef.current || !hasMore) return + loadingRef.current = true setLoading(true) const newEvents = await client.loadMoreTimeline( timelineKey, events.length ? events[events.length - 1].created_at - 1 : dayjs().unix(), LIMIT ) + loadingRef.current = false setLoading(false) if (newEvents.length === 0) { setHasMore(false) @@ -406,7 +412,7 @@ const NoteList = forwardRef< observerInstance.unobserve(currentBottomRef) } } - }, [loading, hasMore, events, showCount, timelineKey]) + }, [hasMore, events, showCount, timelineKey]) const showNewEvents = () => { setEvents((oldEvents) => [...newEvents, ...oldEvents]) diff --git a/src/components/ReplyNote/index.tsx b/src/components/ReplyNote/index.tsx index 1c3c4662..61d4eeb5 100644 --- a/src/components/ReplyNote/index.tsx +++ b/src/components/ReplyNote/index.tsx @@ -88,7 +88,7 @@ export default function ReplyNote({ )} onClick={() => push(toNote(event))} > - {hasReplies &&
} + {hasReplies &&
}
diff --git a/src/components/ReplyNoteList/SubReplies.tsx b/src/components/ReplyNoteList/SubReplies.tsx index e7527401..9da774a1 100644 --- a/src/components/ReplyNoteList/SubReplies.tsx +++ b/src/components/ReplyNoteList/SubReplies.tsx @@ -94,7 +94,7 @@ export default function SubReplies({ parentKey }: { parentKey: string }) { className="relative w-full flex items-center gap-1.5 pl-14 py-2 text-sm text-muted-foreground hover:text-foreground transition-colors clickable" >
-
+
{index < replies.length - 1 && ( -
+
)} a.created_at - b.created_at) + return replyEvents.sort((a, b) => b.created_at - a.created_at) }, [ stuffKey, repliesMap, @@ -79,8 +79,9 @@ export default function ReplyNoteList({ ]) const [timelineKey, setTimelineKey] = useState(undefined) const [until, setUntil] = useState(undefined) - const [loading, setLoading] = useState(false) const [showCount, setShowCount] = useState(SHOW_COUNT) + const [loading, setLoading] = useState(false) + const loadingRef = useRef(false) const bottomRef = useRef(null) useEffect(() => { @@ -125,9 +126,10 @@ export default function ReplyNoteList({ }, [event]) useEffect(() => { - if (loading || !rootInfo || currentIndex !== index) return + if (loadingRef.current || !rootInfo || currentIndex !== index) return const init = async () => { + loadingRef.current = true setLoading(true) try { @@ -195,6 +197,7 @@ export default function ReplyNoteList({ addReplies(evts) } if (eosed) { + loadingRef.current = false setUntil(evts.length >= LIMIT ? evts[evts.length - 1].created_at - 1 : undefined) setLoading(false) } @@ -207,6 +210,7 @@ export default function ReplyNoteList({ setTimelineKey(timelineKey) return closer } catch { + loadingRef.current = false setLoading(false) } return @@ -218,12 +222,6 @@ export default function ReplyNoteList({ } }, [rootInfo, currentIndex, index]) - useEffect(() => { - if (replies.length === 0) { - loadMore() - } - }, [replies]) - useEffect(() => { const options = { root: null, @@ -231,9 +229,28 @@ export default function ReplyNoteList({ threshold: 0.1 } - const observerInstance = new IntersectionObserver((entries) => { - if (entries[0].isIntersecting && showCount < replies.length) { + const loadMore = async () => { + if (showCount < replies.length) { setShowCount((prev) => prev + SHOW_COUNT) + // preload more + if (replies.length - showCount > LIMIT / 2) { + return + } + } + + if (loadingRef.current || !until || !timelineKey) return + loadingRef.current = true + setLoading(true) + const events = await client.loadMoreTimeline(timelineKey, until, LIMIT) + addReplies(events) + setUntil(events.length ? events[events.length - 1].created_at - 1 : undefined) + loadingRef.current = false + setLoading(false) + } + + const observerInstance = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && !!until) { + loadMore() } }, options) @@ -248,41 +265,24 @@ export default function ReplyNoteList({ observerInstance.unobserve(currentBottomRef) } } - }, [replies, showCount]) - - const loadMore = useCallback(async () => { - if (loading || !until || !timelineKey) return - - setLoading(true) - const events = await client.loadMoreTimeline(timelineKey, until, LIMIT) - addReplies(events) - setUntil(events.length ? events[events.length - 1].created_at - 1 : undefined) - setLoading(false) - }, [loading, until, timelineKey]) + }, [replies, showCount, until, timelineKey]) return (
{loading && } - {!loading && until && (!event || until > event.created_at) && ( -
- {t('load more older replies')} -
- )}
{replies.slice(0, showCount).map((reply) => ( ))}
- {!loading && ( + {!!until || showCount < replies.length || loading ? ( + + ) : (
{replies.length > 0 ? t('no more replies') : t('no replies')}
)}
- {loading && }
) }