From e7d24b8c7d6e04d9d3ebb976d4a11f54c7f6748b Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 17 Jan 2023 17:13:22 +0000 Subject: [PATCH] feat: infinite scroll --- src/element/LoadMore.tsx | 13 +++++++++++++ src/element/Note.tsx | 13 +++---------- src/element/Timeline.tsx | 10 ++++++++-- src/feed/TimelineFeed.ts | 16 +++++++++++++--- 4 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 src/element/LoadMore.tsx diff --git a/src/element/LoadMore.tsx b/src/element/LoadMore.tsx new file mode 100644 index 000000000..fe27d00d9 --- /dev/null +++ b/src/element/LoadMore.tsx @@ -0,0 +1,13 @@ +import { useEffect } from "react"; +import { useInView } from "react-intersection-observer"; + +export default function LoadMore({ onLoadMore }: { onLoadMore: () => void }) { + const { ref, inView } = useInView(); + + useEffect(() => { + if (inView === true) { + onLoadMore(); + } + }, [inView]); + return
Loading...
; +} \ No newline at end of file diff --git a/src/element/Note.tsx b/src/element/Note.tsx index 464537a67..40bd568e3 100644 --- a/src/element/Note.tsx +++ b/src/element/Note.tsx @@ -1,5 +1,5 @@ import "./Note.css"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { default as NEvent } from "../nostr/Event"; @@ -33,8 +33,7 @@ export default function Note(props: NoteProps) { const pubKeys = useMemo(() => ev.Thread?.PubKeys || [], [ev]); const users = useProfile(pubKeys); const deletions = useMemo(() => getReactions(related, ev.Id, EventKind.Deletion), [related]); - const { ref, inView } = useInView(); - const [visible, setVisible] = useState(false); + const { ref, inView } = useInView({triggerOnce: true}); const options = { showHeader: true, @@ -51,12 +50,6 @@ export default function Note(props: NoteProps) { return ; }, [ev]); - useEffect(() => { - if (inView && !visible) { - setVisible(true); - } - }, [inView]); - function goToEvent(e: any, id: u256) { if (!window.location.pathname.startsWith("/e/")) { e.stopPropagation(); @@ -102,7 +95,7 @@ export default function Note(props: NoteProps) { } function content() { - if (!visible) return null; + if (!inView) return null; return ( <> {options.showHeader ? diff --git a/src/element/Timeline.tsx b/src/element/Timeline.tsx index d7f1dbbe5..3e9c6e34a 100644 --- a/src/element/Timeline.tsx +++ b/src/element/Timeline.tsx @@ -2,6 +2,7 @@ import { useMemo } from "react"; import useTimelineFeed from "../feed/TimelineFeed"; import { HexKey, TaggedRawEvent, u256 } from "../nostr"; import EventKind from "../nostr/EventKind"; +import LoadMore from "./LoadMore"; import Note from "./Note"; import NoteReaction from "./NoteReaction"; @@ -15,7 +16,7 @@ export interface TimelineProps { * A list of notes by pubkeys */ export default function Timeline({ global, pubkeys, postsOnly = false }: TimelineProps) { - const { main, others } = useTimelineFeed(pubkeys, global); + const { main, others, loadMore } = useTimelineFeed(pubkeys, global); const mainFeed = useMemo(() => { return main?.sort((a, b) => b.created_at - a.created_at)?.filter(a => postsOnly ? !a.tags.some(b => b[0] === "e") : true); @@ -33,5 +34,10 @@ export default function Timeline({ global, pubkeys, postsOnly = false }: Timelin } } - return <>{mainFeed.map(eventElement)}; + return ( + <> + {mainFeed.map(eventElement)} + {mainFeed.length > 0 ? : null} + + ); } \ No newline at end of file diff --git a/src/feed/TimelineFeed.ts b/src/feed/TimelineFeed.ts index 99f4be313..27914bf96 100644 --- a/src/feed/TimelineFeed.ts +++ b/src/feed/TimelineFeed.ts @@ -5,6 +5,8 @@ import { Subscriptions } from "../nostr/Subscriptions"; import useSubscription from "./Subscription"; export default function useTimelineFeed(pubKeys: HexKey | Array, global: boolean = false) { + const TimeRange = 60 * 60; // 1 hr + const [since, setSince] = useState(Math.floor(new Date().getTime() / 1000) - TimeRange); const [trackingEvents, setTrackingEvent] = useState([]); const subTab = global ? "global" : "follows"; @@ -21,10 +23,11 @@ export default function useTimelineFeed(pubKeys: HexKey | Array, global: sub.Id = `timeline:${subTab}`; sub.Authors = global ? undefined : new Set(pubKeys); sub.Kinds = new Set([EventKind.TextNote, EventKind.Repost]); - sub.Limit = 20; + sub.Since = since; + sub.Until = since + TimeRange; return sub; - }, [pubKeys, global]); + }, [pubKeys, global, since]); const main = useSubscription(sub, { leaveOpen: true }); @@ -54,5 +57,12 @@ export default function useTimelineFeed(pubKeys: HexKey | Array, global: return () => clearTimeout(t); } }, [main.notes]); - return { main: main.notes, others: others.notes }; + + return { + main: main.notes, + others: others.notes, + loadMore: () => { + setSince(s => s - TimeRange); + } + }; } \ No newline at end of file