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 "./Note.css";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { default as NEvent } from "../nostr/Event";
|
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 pubKeys = useMemo(() => ev.Thread?.PubKeys || [], [ev]);
|
||||||
const users = useProfile(pubKeys);
|
const users = useProfile(pubKeys);
|
||||||
const deletions = useMemo(() => getReactions(related, ev.Id, EventKind.Deletion), [related]);
|
const deletions = useMemo(() => getReactions(related, ev.Id, EventKind.Deletion), [related]);
|
||||||
const { ref, inView } = useInView();
|
const { ref, inView } = useInView({triggerOnce: true});
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
@ -51,12 +50,6 @@ export default function Note(props: NoteProps) {
|
|||||||
return <Text content={body} tags={ev.Tags} users={users || new Map()} />;
|
return <Text content={body} tags={ev.Tags} users={users || new Map()} />;
|
||||||
}, [ev]);
|
}, [ev]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (inView && !visible) {
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
}, [inView]);
|
|
||||||
|
|
||||||
function goToEvent(e: any, id: u256) {
|
function goToEvent(e: any, id: u256) {
|
||||||
if (!window.location.pathname.startsWith("/e/")) {
|
if (!window.location.pathname.startsWith("/e/")) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -102,7 +95,7 @@ export default function Note(props: NoteProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function content() {
|
function content() {
|
||||||
if (!visible) return null;
|
if (!inView) return null;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{options.showHeader ?
|
{options.showHeader ?
|
||||||
|
@ -2,6 +2,7 @@ import { useMemo } from "react";
|
|||||||
import useTimelineFeed from "../feed/TimelineFeed";
|
import useTimelineFeed from "../feed/TimelineFeed";
|
||||||
import { HexKey, TaggedRawEvent, u256 } from "../nostr";
|
import { HexKey, TaggedRawEvent, u256 } from "../nostr";
|
||||||
import EventKind from "../nostr/EventKind";
|
import EventKind from "../nostr/EventKind";
|
||||||
|
import LoadMore from "./LoadMore";
|
||||||
import Note from "./Note";
|
import Note from "./Note";
|
||||||
import NoteReaction from "./NoteReaction";
|
import NoteReaction from "./NoteReaction";
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ export interface TimelineProps {
|
|||||||
* A list of notes by pubkeys
|
* A list of notes by pubkeys
|
||||||
*/
|
*/
|
||||||
export default function Timeline({ global, pubkeys, postsOnly = false }: TimelineProps) {
|
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(() => {
|
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);
|
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";
|
import useSubscription from "./Subscription";
|
||||||
|
|
||||||
export default function useTimelineFeed(pubKeys: HexKey | Array<HexKey>, global: boolean = false) {
|
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 [trackingEvents, setTrackingEvent] = useState<u256[]>([]);
|
||||||
|
|
||||||
const subTab = global ? "global" : "follows";
|
const subTab = global ? "global" : "follows";
|
||||||
@ -21,10 +23,11 @@ export default function useTimelineFeed(pubKeys: HexKey | Array<HexKey>, global:
|
|||||||
sub.Id = `timeline:${subTab}`;
|
sub.Id = `timeline:${subTab}`;
|
||||||
sub.Authors = global ? undefined : new Set(pubKeys);
|
sub.Authors = global ? undefined : new Set(pubKeys);
|
||||||
sub.Kinds = new Set([EventKind.TextNote, EventKind.Repost]);
|
sub.Kinds = new Set([EventKind.TextNote, EventKind.Repost]);
|
||||||
sub.Limit = 20;
|
sub.Since = since;
|
||||||
|
sub.Until = since + TimeRange;
|
||||||
|
|
||||||
return sub;
|
return sub;
|
||||||
}, [pubKeys, global]);
|
}, [pubKeys, global, since]);
|
||||||
|
|
||||||
const main = useSubscription(sub, { leaveOpen: true });
|
const main = useSubscription(sub, { leaveOpen: true });
|
||||||
|
|
||||||
@ -54,5 +57,12 @@ export default function useTimelineFeed(pubKeys: HexKey | Array<HexKey>, global:
|
|||||||
return () => clearTimeout(t);
|
return () => clearTimeout(t);
|
||||||
}
|
}
|
||||||
}, [main.notes]);
|
}, [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