From e830b39edf3626953f82be3e580c0b5ed464d2f9 Mon Sep 17 00:00:00 2001 From: Kieran Date: Sun, 22 Jan 2023 11:17:50 +0000 Subject: [PATCH] bug: try improve loading performance --- src/Element/LoadMore.tsx | 17 ++++++-- src/Element/Note.css | 4 ++ src/Element/Timeline.tsx | 4 +- src/Feed/LoginFeed.ts | 86 ++++++++++++++++++++++++------------- src/Feed/TimelineFeed.ts | 10 ++++- src/Pages/Notifications.tsx | 55 +++--------------------- 6 files changed, 89 insertions(+), 87 deletions(-) diff --git a/src/Element/LoadMore.tsx b/src/Element/LoadMore.tsx index fe27d00d..cc3e817e 100644 --- a/src/Element/LoadMore.tsx +++ b/src/Element/LoadMore.tsx @@ -1,13 +1,22 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useInView } from "react-intersection-observer"; -export default function LoadMore({ onLoadMore }: { onLoadMore: () => void }) { +export default function LoadMore({ onLoadMore, shouldLoadMore }: { onLoadMore: () => void, shouldLoadMore: boolean }) { const { ref, inView } = useInView(); + const [tick, setTick] = useState(0); useEffect(() => { - if (inView === true) { + if (inView === true && shouldLoadMore === true) { onLoadMore(); } - }, [inView]); + }, [inView, shouldLoadMore, tick]); + + useEffect(() => { + let t = setInterval(() => { + setTick(x => x += 1); + }, 500); + return () => clearInterval(t); + }, []); + return
Loading...
; } \ No newline at end of file diff --git a/src/Element/Note.css b/src/Element/Note.css index 8b192fe3..ffe85383 100644 --- a/src/Element/Note.css +++ b/src/Element/Note.css @@ -1,3 +1,7 @@ +.note { + min-height: 110px; +} + .note.thread { border-bottom: none; } diff --git a/src/Element/Timeline.tsx b/src/Element/Timeline.tsx index a176896c..075d8a87 100644 --- a/src/Element/Timeline.tsx +++ b/src/Element/Timeline.tsx @@ -7,7 +7,7 @@ import LoadMore from "Element/LoadMore"; import Note from "Element/Note"; import NoteReaction from "Element/NoteReaction"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faFastForward, faForward } from "@fortawesome/free-solid-svg-icons"; +import { faForward } from "@fortawesome/free-solid-svg-icons"; export interface TimelineProps { postsOnly: boolean, @@ -55,7 +55,7 @@ export default function Timeline({ subject, postsOnly = false, method }: Timelin Show latest {latestFeed.length - 1} notes )} {mainFeed.map(eventElement)} - {mainFeed.length > 0 ? : null} + ); } \ No newline at end of file diff --git a/src/Feed/LoginFeed.ts b/src/Feed/LoginFeed.ts index ce643426..1810a0ce 100644 --- a/src/Feed/LoginFeed.ts +++ b/src/Feed/LoginFeed.ts @@ -20,40 +20,54 @@ export default function useLoginFeed() { const dispatch = useDispatch(); const [pubKey, readNotifications] = useSelector(s => [s.login.publicKey, s.login.readNotifications]); - const sub = useMemo(() => { - if (!pubKey) { - return null; - } + const subMetadata = useMemo(() => { + if (!pubKey) return null; let sub = new Subscriptions(); - sub.Id = `login:${sub.Id}`; + sub.Id = `login:meta`; sub.Authors = new Set([pubKey]); - sub.Kinds = new Set([EventKind.ContactList, EventKind.SetMetadata, EventKind.DirectMessage]); - - let notifications = new Subscriptions(); - notifications.Kinds = new Set([EventKind.TextNote]); - notifications.PTags = new Set([pubKey]); - notifications.Limit = 100; - sub.AddSubscription(notifications); - - let dms = new Subscriptions(); - dms.Kinds = new Set([EventKind.DirectMessage]); - dms.PTags = new Set([pubKey]); - sub.AddSubscription(dms); + sub.Kinds = new Set([EventKind.ContactList, EventKind.SetMetadata]); return sub; }, [pubKey]); - const main = useSubscription(sub, { leaveOpen: true }); + const subNotification = useMemo(() => { + if (!pubKey) return null; + + let sub = new Subscriptions(); + sub.Id = "login:notifications"; + sub.Kinds = new Set([EventKind.TextNote]); + sub.PTags = new Set([pubKey]); + sub.Limit = 1; + return sub; + }, [pubKey]); + + const subDms = useMemo(() => { + if (!pubKey) return null; + + let dms = new Subscriptions(); + dms.Id = "login:dms"; + dms.Kinds = new Set([EventKind.DirectMessage]); + dms.PTags = new Set([pubKey]); + + let dmsFromME = new Subscriptions(); + dmsFromME.Authors = new Set([pubKey]); + dmsFromME.Kinds = new Set([EventKind.DirectMessage]); + dms.AddSubscription(dmsFromME); + + return dms; + }, [pubKey]); + + const metadataFeed = useSubscription(subMetadata, { leaveOpen: true }); + const notificationFeed = useSubscription(subNotification, { leaveOpen: true }); + const dmsFeed = useSubscription(subDms, { leaveOpen: true }); useEffect(() => { - let contactList = main.store.notes.filter(a => a.kind === EventKind.ContactList); - let notifications = main.store.notes.filter(a => a.kind === EventKind.TextNote); - let metadata = main.store.notes.filter(a => a.kind === EventKind.SetMetadata); + let contactList = metadataFeed.store.notes.filter(a => a.kind === EventKind.ContactList); + let metadata = metadataFeed.store.notes.filter(a => a.kind === EventKind.SetMetadata); let profiles = metadata.map(a => mapEventToProfile(a)) .filter(a => a !== undefined) .map(a => a!); - let dms = main.store.notes.filter(a => a.kind === EventKind.DirectMessage); for (let cl of contactList) { if (cl.content !== "") { @@ -64,14 +78,6 @@ export default function useLoginFeed() { dispatch(setFollows(pTags)); } - if ("Notification" in window && Notification.permission === "granted") { - for (let nx of notifications.filter(a => (a.created_at * 1000) > readNotifications)) { - sendNotification(nx) - .catch(console.warn); - } - } - dispatch(addNotifications(notifications)); - dispatch(addDirectMessage(dms)); (async () => { let maxProfile = profiles.reduce((acc, v) => { if (v.created > acc.created) { @@ -87,7 +93,25 @@ export default function useLoginFeed() { } } })().catch(console.warn); - }, [main.store]); + }, [metadataFeed.store]); + + useEffect(() => { + let notifications = notificationFeed.store.notes.filter(a => a.kind === EventKind.TextNote); + + if ("Notification" in window && Notification.permission === "granted") { + for (let nx of notifications.filter(a => (a.created_at * 1000) > readNotifications)) { + sendNotification(nx) + .catch(console.warn); + } + } + + dispatch(addNotifications(notifications)); + }, [notificationFeed.store]); + + useEffect(() => { + let dms = dmsFeed.store.notes.filter(a => a.kind === EventKind.DirectMessage); + dispatch(addDirectMessage(dms)); + }, [dmsFeed.store]); } async function makeNotification(ev: TaggedRawEvent) { diff --git a/src/Feed/TimelineFeed.ts b/src/Feed/TimelineFeed.ts index 80be5fe9..d71a72b2 100644 --- a/src/Feed/TimelineFeed.ts +++ b/src/Feed/TimelineFeed.ts @@ -13,13 +13,13 @@ export interface TimelineFeedOptions { } export interface TimelineSubject { - type: "pubkey" | "hashtag" | "global", + type: "pubkey" | "hashtag" | "global" | "ptag", items: string[] } export default function useTimelineFeed(subject: TimelineSubject, options: TimelineFeedOptions) { const now = unixNow(); - const [window, setWindow] = useState(60 * 10); + const [window, setWindow] = useState(60 * 60); const [until, setUntil] = useState(now); const [since, setSince] = useState(now - window); const [trackingEvents, setTrackingEvent] = useState([]); @@ -42,6 +42,10 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel sub.HashTags = new Set(subject.items); break; } + case "ptag": { + sub.PTags = new Set(subject.items); + break; + } } return sub; } @@ -68,6 +72,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel latestSub.HashTags = sub.HashTags; latestSub.Kinds = sub.Kinds; latestSub.Limit = 1; + latestSub.Since = Math.floor(new Date().getTime() / 1000); sub.AddSubscription(latestSub); } } @@ -81,6 +86,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel if (subLatest && !pref.autoShowLatest) { subLatest.Id = `${subLatest.Id}:latest`; subLatest.Limit = 1; + subLatest.Since = Math.floor(new Date().getTime() / 1000); } return subLatest; }, [subject.type, subject.items]); diff --git a/src/Pages/Notifications.tsx b/src/Pages/Notifications.tsx index f09c269b..0b063492 100644 --- a/src/Pages/Notifications.tsx +++ b/src/Pages/Notifications.tsx @@ -1,65 +1,24 @@ -import { useEffect, useMemo } from "react"; +import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux" -import Note from "Element/Note"; -import NoteReaction from "Element/NoteReaction"; -import useSubscription from "Feed/Subscription"; -import { TaggedRawEvent } from "Nostr"; -import Event from "Nostr/Event"; -import EventKind from "Nostr/EventKind"; -import { Subscriptions } from "Nostr/Subscriptions"; +import { HexKey } from "Nostr"; import { markNotificationsRead } from "State/Login"; import { RootState } from "State/Store"; +import Timeline from "Element/Timeline"; export default function NotificationsPage() { const dispatch = useDispatch(); - const notifications = useSelector(s => s.login.notifications); + const pubkey = useSelector(s => s.login.publicKey); useEffect(() => { dispatch(markNotificationsRead()); }, []); - const etagged = useMemo(() => { - return notifications?.filter(a => a.kind === EventKind.Reaction) - .map(a => { - let ev = new Event(a); - return ev.Thread?.ReplyTo?.Event ?? ev.Thread?.Root?.Event; - }).filter(a => a !== undefined).map(a => a!); - }, [notifications]); - - const subEvents = useMemo(() => { - let sub = new Subscriptions(); - sub.Id = `reactions:${sub.Id}`; - sub.Kinds = new Set([EventKind.Reaction]); - sub.ETags = new Set(notifications?.filter(b => b.kind === EventKind.TextNote).map(b => b.id)); - - if (etagged.length > 0) { - let reactionsTo = new Subscriptions(); - reactionsTo.Kinds = new Set([EventKind.TextNote]); - reactionsTo.Ids = new Set(etagged); - sub.OrSubs.push(reactionsTo); - } - return sub; - }, [etagged]); - - const otherNotes = useSubscription(subEvents, { leaveOpen: true }); - - const sorted = [ - ...notifications - ].sort((a, b) => b.created_at - a.created_at); return ( <> - {sorted?.map(a => { - if (a.kind === EventKind.TextNote) { - return - } else if (a.kind === EventKind.Reaction) { - let ev = new Event(a); - let reactedTo = ev.Thread?.ReplyTo?.Event ?? ev.Thread?.Root?.Event; - let reactedNote = otherNotes?.store.notes?.find(c => c.id === reactedTo); - return - } - return null; - })} + {pubkey ? + + : null} ) } \ No newline at end of file