bug: try improve loading performance

This commit is contained in:
Kieran 2023-01-22 11:17:50 +00:00
parent c91325ecd6
commit e830b39edf
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
6 changed files with 89 additions and 87 deletions

View File

@ -1,13 +1,22 @@
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { useInView } from "react-intersection-observer"; 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 { ref, inView } = useInView();
const [tick, setTick] = useState<number>(0);
useEffect(() => { useEffect(() => {
if (inView === true) { if (inView === true && shouldLoadMore === true) {
onLoadMore(); onLoadMore();
} }
}, [inView]); }, [inView, shouldLoadMore, tick]);
useEffect(() => {
let t = setInterval(() => {
setTick(x => x += 1);
}, 500);
return () => clearInterval(t);
}, []);
return <div ref={ref} className="mb10">Loading...</div>; return <div ref={ref} className="mb10">Loading...</div>;
} }

View File

@ -1,3 +1,7 @@
.note {
min-height: 110px;
}
.note.thread { .note.thread {
border-bottom: none; border-bottom: none;
} }

View File

@ -7,7 +7,7 @@ import LoadMore from "Element/LoadMore";
import Note from "Element/Note"; import Note from "Element/Note";
import NoteReaction from "Element/NoteReaction"; import NoteReaction from "Element/NoteReaction";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 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 { export interface TimelineProps {
postsOnly: boolean, postsOnly: boolean,
@ -55,7 +55,7 @@ export default function Timeline({ subject, postsOnly = false, method }: Timelin
Show latest {latestFeed.length - 1} notes Show latest {latestFeed.length - 1} notes
</div>)} </div>)}
{mainFeed.map(eventElement)} {mainFeed.map(eventElement)}
{mainFeed.length > 0 ? <LoadMore onLoadMore={loadMore} /> : null} <LoadMore onLoadMore={loadMore} shouldLoadMore={main.end}/>
</> </>
); );
} }

View File

@ -20,40 +20,54 @@ export default function useLoginFeed() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [pubKey, readNotifications] = useSelector<RootState, [HexKey | undefined, number]>(s => [s.login.publicKey, s.login.readNotifications]); const [pubKey, readNotifications] = useSelector<RootState, [HexKey | undefined, number]>(s => [s.login.publicKey, s.login.readNotifications]);
const sub = useMemo(() => { const subMetadata = useMemo(() => {
if (!pubKey) { if (!pubKey) return null;
return null;
}
let sub = new Subscriptions(); let sub = new Subscriptions();
sub.Id = `login:${sub.Id}`; sub.Id = `login:meta`;
sub.Authors = new Set([pubKey]); sub.Authors = new Set([pubKey]);
sub.Kinds = new Set([EventKind.ContactList, EventKind.SetMetadata, EventKind.DirectMessage]); sub.Kinds = new Set([EventKind.ContactList, EventKind.SetMetadata]);
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);
return sub; return sub;
}, [pubKey]); }, [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(() => { useEffect(() => {
let contactList = main.store.notes.filter(a => a.kind === EventKind.ContactList); let contactList = metadataFeed.store.notes.filter(a => a.kind === EventKind.ContactList);
let notifications = main.store.notes.filter(a => a.kind === EventKind.TextNote); let metadata = metadataFeed.store.notes.filter(a => a.kind === EventKind.SetMetadata);
let metadata = main.store.notes.filter(a => a.kind === EventKind.SetMetadata);
let profiles = metadata.map(a => mapEventToProfile(a)) let profiles = metadata.map(a => mapEventToProfile(a))
.filter(a => a !== undefined) .filter(a => a !== undefined)
.map(a => a!); .map(a => a!);
let dms = main.store.notes.filter(a => a.kind === EventKind.DirectMessage);
for (let cl of contactList) { for (let cl of contactList) {
if (cl.content !== "") { if (cl.content !== "") {
@ -64,14 +78,6 @@ export default function useLoginFeed() {
dispatch(setFollows(pTags)); 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 () => { (async () => {
let maxProfile = profiles.reduce((acc, v) => { let maxProfile = profiles.reduce((acc, v) => {
if (v.created > acc.created) { if (v.created > acc.created) {
@ -87,7 +93,25 @@ export default function useLoginFeed() {
} }
} }
})().catch(console.warn); })().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) { async function makeNotification(ev: TaggedRawEvent) {

View File

@ -13,13 +13,13 @@ export interface TimelineFeedOptions {
} }
export interface TimelineSubject { export interface TimelineSubject {
type: "pubkey" | "hashtag" | "global", type: "pubkey" | "hashtag" | "global" | "ptag",
items: string[] items: string[]
} }
export default function useTimelineFeed(subject: TimelineSubject, options: TimelineFeedOptions) { export default function useTimelineFeed(subject: TimelineSubject, options: TimelineFeedOptions) {
const now = unixNow(); const now = unixNow();
const [window, setWindow] = useState<number>(60 * 10); const [window, setWindow] = useState<number>(60 * 60);
const [until, setUntil] = useState<number>(now); const [until, setUntil] = useState<number>(now);
const [since, setSince] = useState<number>(now - window); const [since, setSince] = useState<number>(now - window);
const [trackingEvents, setTrackingEvent] = useState<u256[]>([]); const [trackingEvents, setTrackingEvent] = useState<u256[]>([]);
@ -42,6 +42,10 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
sub.HashTags = new Set(subject.items); sub.HashTags = new Set(subject.items);
break; break;
} }
case "ptag": {
sub.PTags = new Set(subject.items);
break;
}
} }
return sub; return sub;
} }
@ -68,6 +72,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
latestSub.HashTags = sub.HashTags; latestSub.HashTags = sub.HashTags;
latestSub.Kinds = sub.Kinds; latestSub.Kinds = sub.Kinds;
latestSub.Limit = 1; latestSub.Limit = 1;
latestSub.Since = Math.floor(new Date().getTime() / 1000);
sub.AddSubscription(latestSub); sub.AddSubscription(latestSub);
} }
} }
@ -81,6 +86,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
if (subLatest && !pref.autoShowLatest) { if (subLatest && !pref.autoShowLatest) {
subLatest.Id = `${subLatest.Id}:latest`; subLatest.Id = `${subLatest.Id}:latest`;
subLatest.Limit = 1; subLatest.Limit = 1;
subLatest.Since = Math.floor(new Date().getTime() / 1000);
} }
return subLatest; return subLatest;
}, [subject.type, subject.items]); }, [subject.type, subject.items]);

View File

@ -1,65 +1,24 @@
import { useEffect, useMemo } from "react"; import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux" import { useDispatch, useSelector } from "react-redux"
import Note from "Element/Note"; import { HexKey } from "Nostr";
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 { markNotificationsRead } from "State/Login"; import { markNotificationsRead } from "State/Login";
import { RootState } from "State/Store"; import { RootState } from "State/Store";
import Timeline from "Element/Timeline";
export default function NotificationsPage() { export default function NotificationsPage() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const notifications = useSelector<RootState, TaggedRawEvent[]>(s => s.login.notifications); const pubkey = useSelector<RootState, HexKey | undefined>(s => s.login.publicKey);
useEffect(() => { useEffect(() => {
dispatch(markNotificationsRead()); 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 ( return (
<> <>
{sorted?.map(a => { {pubkey ?
if (a.kind === EventKind.TextNote) { <Timeline subject={{ type: "ptag", items: [pubkey!] }} postsOnly={false} method={"TIME_RANGE"} />
return <Note data={a} key={a.id} related={otherNotes?.store.notes ?? []} /> : null}
} 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 <NoteReaction data={a} key={a.id} root={reactedNote} />
}
return null;
})}
</> </>
) )
} }