forked from Kieran/snort
feat: infinite scroll
This commit is contained in:
parent
02148fd97a
commit
e7d24b8c7d
13
src/element/LoadMore.tsx
Normal file
13
src/element/LoadMore.tsx
Normal file
@ -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 <div ref={ref} className="mb10">Loading...</div>;
|
||||
}
|
@ -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 <Text content={body} tags={ev.Tags} users={users || new Map()} />;
|
||||
}, [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 ?
|
||||
|
@ -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 ? <LoadMore onLoadMore={loadMore} /> : null}
|
||||
</>
|
||||
);
|
||||
}
|
@ -5,6 +5,8 @@ import { Subscriptions } from "../nostr/Subscriptions";
|
||||
import useSubscription from "./Subscription";
|
||||
|
||||
export default function useTimelineFeed(pubKeys: HexKey | Array<HexKey>, global: boolean = false) {
|
||||
const TimeRange = 60 * 60; // 1 hr
|
||||
const [since, setSince] = useState<number>(Math.floor(new Date().getTime() / 1000) - TimeRange);
|
||||
const [trackingEvents, setTrackingEvent] = useState<u256[]>([]);
|
||||
|
||||
const subTab = global ? "global" : "follows";
|
||||
@ -21,10 +23,11 @@ export default function useTimelineFeed(pubKeys: HexKey | Array<HexKey>, 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<HexKey>, 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);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user