feat: defer loading notes

This commit is contained in:
Kieran 2023-01-17 14:00:59 +00:00
parent 0d9aa0d5ca
commit dda07d66d5
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
4 changed files with 35 additions and 35 deletions

View File

@ -1,18 +0,0 @@
import { useEffect, useState } from 'react';
import { useInView } from 'react-intersection-observer';
export default function LazyImage(props: any) {
const { ref, inView, entry } = useInView();
const [shown, setShown] = useState(false);
useEffect(() => {
setShown(s => {
if (!s && inView) {
return true;
}
return s;
})
}, [inView]);
return shown ? <img {...props} /> : <div ref={ref}></div>
}

View File

@ -1,6 +1,7 @@
.note { .note {
margin-bottom: 10px; margin-bottom: 10px;
border-bottom: 1px solid var(--gray); border-bottom: 1px solid var(--gray);
min-height: 140px;
} }
.note.thread { .note.thread {

View File

@ -1,5 +1,5 @@
import "./Note.css"; import "./Note.css";
import { useCallback, useMemo } from "react"; import { useCallback, useEffect, useMemo, useState } 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";
@ -11,6 +11,7 @@ import NoteTime from "./NoteTime";
import EventKind from "../nostr/EventKind"; import EventKind from "../nostr/EventKind";
import useProfile from "../feed/ProfileFeed"; import useProfile from "../feed/ProfileFeed";
import { TaggedRawEvent, u256 } from "../nostr"; import { TaggedRawEvent, u256 } from "../nostr";
import { useInView } from "react-intersection-observer";
export interface NoteProps { export interface NoteProps {
data?: TaggedRawEvent, data?: TaggedRawEvent,
@ -32,6 +33,8 @@ 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 [visible, setVisible] = useState(false);
const options = { const options = {
showHeader: true, showHeader: true,
@ -46,7 +49,13 @@ export default function Note(props: NoteProps) {
return (<b className="error">Deleted</b>); return (<b className="error">Deleted</b>);
} }
return <Text content={body} tags={ev.Tags} users={users || new Map()} />; return <Text content={body} tags={ev.Tags} users={users || new Map()} />;
}, [props]); }, [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/")) {
@ -92,20 +101,29 @@ export default function Note(props: NoteProps) {
); );
} }
function content() {
if (!visible) return null;
return (
<>
{options.showHeader ?
<div className="header flex">
<ProfileImage pubkey={ev.RootPubKey} subHeader={replyTag() ?? undefined} />
{options.showTime ?
<div className="info">
<NoteTime from={ev.CreatedAt * 1000} />
</div> : null}
</div> : null}
<div className="body" onClick={(e) => goToEvent(e, ev.Id)}>
{transformBody()}
</div>
{options.showFooter ? <NoteFooter ev={ev} related={related} /> : null}
</>
)
}
return ( return (
<div className={`note ${highlight ? "active" : ""} ${isThread ? "thread" : ""}`}> <div className={`note${highlight ? " active" : ""}${isThread ? " thread" : ""}`} ref={ref}>
{options.showHeader ? {content()}
<div className="header flex">
<ProfileImage pubkey={ev.RootPubKey} subHeader={replyTag() ?? undefined} />
{options.showTime ?
<div className="info">
<NoteTime from={ev.CreatedAt * 1000} />
</div> : null}
</div> : null}
<div className="body" onClick={(e) => goToEvent(e, ev.Id)}>
{transformBody()}
</div>
{options.showFooter ? <NoteFooter ev={ev} related={related} /> : null}
</div> </div>
) )
} }

View File

@ -5,7 +5,6 @@ import { TwitterTweetEmbed } from "react-twitter-embed";
import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex, YoutubeUrlRegex, TweetUrlRegex, HashtagRegex } from "../Const"; import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex, YoutubeUrlRegex, TweetUrlRegex, HashtagRegex } from "../Const";
import { eventLink, hexToBech32 } from "../Util"; import { eventLink, hexToBech32 } from "../Util";
import Invoice from "./Invoice"; import Invoice from "./Invoice";
import LazyImage from "./LazyImage";
import Hashtag from "./Hashtag"; import Hashtag from "./Hashtag";
import './Text.css' import './Text.css'
@ -28,7 +27,7 @@ function transformHttpLink(a: string) {
case "png": case "png":
case "bmp": case "bmp":
case "webp": { case "webp": {
return <LazyImage key={url} src={url} />; return <img key={url.toString()} src={url.toString()} />;
} }
case "mp4": case "mp4":
case "mov": case "mov":