use swr for note fetcher

This commit is contained in:
Ren Amamiya
2023-04-27 08:57:41 +07:00
parent 38c6b2c76d
commit 8cadb7467f
7 changed files with 204 additions and 312 deletions

View File

@@ -1,4 +1,3 @@
import { AccountContext } from '@components/accountProvider';
import { NoteMetadata } from '@components/note/metadata';
import { RelayContext } from '@components/relaysProvider';
import { UserExtend } from '@components/user/extend';
@@ -6,126 +5,90 @@ import { UserExtend } from '@components/user/extend';
import { READONLY_RELAYS } from '@stores/constants';
import { contentParser } from '@utils/parser';
import { createNote, getNoteByID } from '@utils/storage';
import { getParentID } from '@utils/transform';
import { memo, useCallback, useContext, useEffect, useState } from 'react';
import { memo, useContext } from 'react';
import useSWRSubscription from 'swr/subscription';
export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
const pool: any = useContext(RelayContext);
const activeAccount: any = useContext(AccountContext);
const [event, setEvent] = useState(null);
const content = event ? contentParser(event.content, event.tags) : '';
const fetchEvent = useCallback(async () => {
const unsubscribe = pool.subscribe(
[
{
ids: [id],
kinds: [1],
const { data, error } = useSWRSubscription(
id
? [
{
ids: [id],
kinds: [1],
},
]
: null,
(key, { next }) => {
const unsubscribe = pool.subscribe(
key,
READONLY_RELAYS,
(event: any) => {
next(null, event);
},
],
READONLY_RELAYS,
(event: any) => {
// update state
setEvent(event);
// insert to database
const parentID = getParentID(event.tags, event.id);
// insert event to local database
createNote(
event.id,
activeAccount.id,
event.pubkey,
event.kind,
event.tags,
event.content,
event.created_at,
parentID
);
},
undefined,
undefined,
{
unsubscribeOnEose: true,
}
);
return () => {
unsubscribe();
};
}, [activeAccount.id, id, pool]);
const checkNoteIsSaved = useCallback(async () => {
getNoteByID(id)
.then((res) => {
if (res) {
setEvent(res);
} else {
fetchEvent();
undefined,
undefined,
{
unsubscribeOnEose: true,
}
})
.catch(console.error);
}, [fetchEvent, id]);
);
useEffect(() => {
let ignore = false;
if (!ignore) {
checkNoteIsSaved();
return () => {
unsubscribe();
};
}
);
return () => {
ignore = true;
};
}, [checkNoteIsSaved]);
if (event) {
return (
<div className="relative pb-5">
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
<div className="relative z-10 flex flex-col">
<UserExtend pubkey={event.pubkey} time={event.created_at} />
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">{content}</div>
return (
<div className="relative pb-5">
{error && <div>failed to load</div>}
{!data ? (
<div className="animated-pulse">
<div className="flex items-start gap-2">
<div className="relative h-11 w-11 shrink overflow-hidden rounded-md bg-zinc-700" />
<div className="flex w-full flex-1 items-start justify-between">
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-2 text-sm">
<div className="h-4 w-16 rounded bg-zinc-700" />
<span className="text-zinc-500">·</span>
<div className="h-4 w-12 rounded bg-zinc-700" />
</div>
</div>
</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={event.event_id}
eventPubkey={event.pubkey}
eventContent={event.content}
eventTime={event.created_at}
/>
</div>
</div>
</div>
);
} else {
return (
<div className="relative z-10 flex h-min animate-pulse select-text flex-col pb-5">
<div className="flex items-start gap-2">
<div className="relative h-11 w-11 shrink overflow-hidden rounded-md bg-zinc-700" />
<div className="flex w-full flex-1 items-start justify-between">
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-2 text-sm">
<div className="h-4 w-16 rounded bg-zinc-700" />
<span className="text-zinc-500">·</span>
<div className="-mt-5 pl-[52px]">
<div className="flex flex-col gap-6">
<div className="h-16 w-full rounded bg-zinc-700" />
<div className="flex items-center gap-8">
<div className="h-4 w-12 rounded bg-zinc-700" />
<div className="h-4 w-12 rounded bg-zinc-700" />
</div>
<div className="h-3 w-3 rounded-full bg-zinc-700" />
</div>
</div>
</div>
<div className="-mt-5 pl-[52px]">
<div className="flex flex-col gap-6">
<div className="h-16 w-full rounded bg-zinc-700" />
<div className="flex items-center gap-8">
<div className="h-4 w-12 rounded bg-zinc-700" />
<div className="h-4 w-12 rounded bg-zinc-700" />
) : (
<>
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
<div className="relative z-10 flex flex-col">
<UserExtend pubkey={data.pubkey} time={data.created_at} />
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">
{contentParser(data.content, data.tags)}
</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={data.event_id}
eventPubkey={data.pubkey}
eventContent={data.content}
eventTime={data.created_at}
/>
</div>
</div>
</div>
</div>
);
}
</>
)}
</div>
);
});

View File

@@ -1,95 +1,60 @@
import { AccountContext } from '@components/accountProvider';
import { RelayContext } from '@components/relaysProvider';
import { UserExtend } from '@components/user/extend';
import { READONLY_RELAYS } from '@stores/constants';
import { contentParser } from '@utils/parser';
import { createNote, getNoteByID } from '@utils/storage';
import { getParentID } from '@utils/transform';
import { memo, useCallback, useContext, useEffect, useState } from 'react';
import { memo, useContext } from 'react';
import useSWRSubscription from 'swr/subscription';
export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
const pool: any = useContext(RelayContext);
const activeAccount: any = useContext(AccountContext);
const [event, setEvent] = useState(null);
const content = event ? contentParser(event.content, event.tags) : '';
const fetchEvent = useCallback(async () => {
const unsubscribe = pool.subscribe(
[
{
ids: [id],
kinds: [1],
const { data, error } = useSWRSubscription(
id
? [
{
ids: [id],
kinds: [1],
},
]
: null,
(key, { next }) => {
const unsubscribe = pool.subscribe(
key,
READONLY_RELAYS,
(event: any) => {
next(null, event);
},
],
READONLY_RELAYS,
(event: any) => {
// update state
setEvent(event);
// insert to database
const parentID = getParentID(event.tags, event.id);
createNote(
event.id,
activeAccount.id,
event.pubkey,
event.kind,
event.tags,
event.content,
event.created_at,
parentID
);
},
undefined,
undefined,
{
unsubscribeOnEose: true,
}
);
return () => {
unsubscribe();
};
}, [activeAccount.id, id, pool]);
const checkNoteIsSaved = useCallback(async () => {
getNoteByID(id)
.then((res) => {
if (res) {
setEvent(res);
} else {
fetchEvent();
undefined,
undefined,
{
unsubscribeOnEose: true,
}
})
.catch(console.error);
}, [fetchEvent, id]);
);
useEffect(() => {
let ignore = false;
if (!ignore) {
checkNoteIsSaved();
return () => {
unsubscribe();
};
}
);
return () => {
ignore = true;
};
}, [checkNoteIsSaved]);
if (event) {
return (
<div className="relative mb-2 mt-3 rounded-lg border border-zinc-700 bg-zinc-800 p-2 py-3">
return (
<div className="relative mb-2 mt-3 rounded-lg border border-zinc-700 bg-zinc-800 p-2 py-3">
{error && <div>failed to load</div>}
{!data ? (
<div className="h-6 w-full animate-pulse select-text flex-col rounded bg-zinc-800"></div>
) : (
<div className="relative z-10 flex flex-col">
<UserExtend pubkey={event.pubkey} time={event.created_at} />
<UserExtend pubkey={data.pubkey} time={data.created_at} />
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">{content}</div>
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">
{contentParser(data.content, data.tags)}
</div>
</div>
</div>
</div>
);
} else {
return <div className="mt-2 h-6 animate-pulse select-text flex-col rounded bg-zinc-700 pb-5"></div>;
}
)}
</div>
);
});

View File

@@ -1,24 +1,12 @@
import { RootNote } from '@components/note/rootNote';
import { UserQuoteRepost } from '@components/user/quoteRepost';
import destr from 'destr';
import { getQuoteID } from '@utils/transform';
import { memo } from 'react';
export const NoteQuoteRepost = memo(function NoteQuoteRepost({ event }: { event: any }) {
const rootNote = () => {
let note = null;
if (event.content) {
const content = destr(event.content);
if (content) {
note = <RootNote event={content} />;
}
} else {
note = <RootNote event={event.tags[0][1]} />;
}
return note;
};
const rootID = getQuoteID(event.tags);
return (
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 px-3 py-5 hover:bg-black/20">
@@ -26,7 +14,7 @@ export const NoteQuoteRepost = memo(function NoteQuoteRepost({ event }: { event:
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
<UserQuoteRepost pubkey={event.pubkey} time={event.created_at} />
</div>
{rootNote()}
<RootNote id={rootID} />
</div>
);
});

View File

@@ -6,42 +6,37 @@ import { READONLY_RELAYS } from '@stores/constants';
import { contentParser } from '@utils/parser';
import { memo, useCallback, useContext, useEffect, useState } from 'react';
import { memo, useContext } from 'react';
import useSWRSubscription from 'swr/subscription';
import { navigate } from 'vite-plugin-ssr/client/router';
export const RootNote = memo(function RootNote({ event }: { event: any }) {
export const RootNote = memo(function RootNote({ id }: { id: string }) {
const pool: any = useContext(RelayContext);
const [data, setData] = useState(null);
const [content, setContent] = useState('');
const openUserPage = (e) => {
e.stopPropagation();
navigate(`/user?pubkey=${event.pubkey}`);
};
const openThread = (e) => {
const selection = window.getSelection();
if (selection.toString().length === 0) {
navigate(`/newsfeed/note?id=${event.parent_id}`);
navigate(`/newsfeed/note?id=${id}`);
} else {
e.stopPropagation();
}
};
const fetchEvent = useCallback(
async (id: string) => {
const unsubscribe = pool.subscribe(
[
const { data, error } = useSWRSubscription(
id
? [
{
ids: [id],
kinds: [1],
},
],
]
: null,
(key, { next }) => {
const unsubscribe = pool.subscribe(
key,
READONLY_RELAYS,
(event: any) => {
setData(event);
setContent(contentParser(event.content, event.tags));
next(null, event);
},
undefined,
undefined,
@@ -53,72 +48,32 @@ export const RootNote = memo(function RootNote({ event }: { event: any }) {
return () => {
unsubscribe();
};
},
[pool]
}
);
useEffect(() => {
let ignore = false;
if (!ignore) {
if (typeof event === 'object') {
setData(event);
setContent(contentParser(event.content, event.tags));
} else {
fetchEvent(event);
}
}
return () => {
ignore = true;
};
}, [event, fetchEvent]);
if (data) {
return (
<div onClick={(e) => openThread(e)} className="relative z-10 flex flex-col">
<div onClick={(e) => openUserPage(e)}>
return (
<>
{error && <div>failed to load</div>}
{!data ? (
<div className="h-6 w-full animate-pulse select-text flex-col rounded bg-zinc-800"></div>
) : (
<div onClick={(e) => openThread(e)} className="relative z-10 flex flex-col">
<UserExtend pubkey={data.pubkey} time={data.created_at} />
</div>
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">{content}</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={data.id}
eventPubkey={data.pubkey}
eventContent={data.content}
eventTime={data.created_at}
/>
</div>
</div>
);
} else {
return (
<div className="relative z-10 flex h-min animate-pulse select-text flex-col pb-5">
<div className="flex items-start gap-2">
<div className="relative h-11 w-11 shrink overflow-hidden rounded-md bg-zinc-700" />
<div className="flex w-full flex-1 items-start justify-between">
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-2 text-sm">
<div className="h-4 w-16 rounded bg-zinc-700" />
<span className="text-zinc-500">·</span>
<div className="h-4 w-12 rounded bg-zinc-700" />
</div>
<div className="h-3 w-3 rounded-full bg-zinc-700" />
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">
{contentParser(data.content, data.tags)}
</div>
</div>
</div>
<div className="-mt-5 pl-[52px]">
<div className="flex flex-col gap-6">
<div className="h-16 w-full rounded bg-zinc-700" />
<div className="flex items-center gap-8">
<div className="h-4 w-12 rounded bg-zinc-700" />
<div className="h-4 w-12 rounded bg-zinc-700" />
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={data.id}
eventPubkey={data.pubkey}
eventContent={data.content}
eventTime={data.created_at}
/>
</div>
</div>
</div>
);
}
)}
</>
);
});