update useNostr hook

This commit is contained in:
Ren Amamiya
2023-08-14 14:12:54 +07:00
parent 6c6f50444e
commit 823b203b73
8 changed files with 145 additions and 257 deletions

View File

@@ -18,11 +18,11 @@ const ITEM_PER_PAGE = 10;
export function NetworkBlock() {
// subscribe for live update
useNewsfeed();
// useNewsfeed();
const { status, data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery({
queryKey: ['newsfeed-circle'],
queryKey: ['network-widget'],
queryFn: async ({ pageParam = 0 }) => {
return await getNotes(ITEM_PER_PAGE, pageParam);
},

View File

@@ -12,7 +12,7 @@ import { useNostr } from '@utils/hooks/useNostr';
export function SplashScreen() {
const { ndk, relayUrls } = useNDK();
const { status, account } = useAccount();
const { fetchChats, fetchNotes } = useNostr();
const { fetchUserData } = useNostr();
const [isLoading, setIsLoading] = useState<boolean>(true);
const [errorMessage, setErrorMessage] = useState<null | string>(null);
@@ -27,18 +27,15 @@ export function SplashScreen() {
if (step) await invoke('close_splashscreen');
try {
const notes = await fetchNotes();
const chats = await fetchChats();
if (notes.status === 'ok' && chats.status === 'ok') {
const user = await fetchUserData();
if (user.status === 'ok') {
const now = Math.floor(Date.now() / 1000);
await updateLastLogin(now);
invoke('close_splashscreen');
} else {
setIsLoading(false);
setErrorMessage(notes.message || chats.message);
console.log('fetch notes failed, error: ', notes.message);
console.log('fetch chats failed, error: ', chats.message);
setErrorMessage(user.message);
console.log('fetch failed, error: ', user.message);
}
} catch (e) {
setIsLoading(false);

View File

@@ -1,51 +1,24 @@
import { useQuery } from '@tanstack/react-query';
import { useNDK } from '@libs/ndk/provider';
import { createNote, getNoteByID } from '@libs/storage';
import { parser } from '@utils/parser';
import { LumeEvent } from '@utils/types';
export function useEvent(id: string, fallback?: string) {
export function useEvent(id: string, embed?: string) {
const { ndk } = useNDK();
const { status, data, error, isFetching } = useQuery(
['note', id],
async () => {
const result = await getNoteByID(id);
if (result) {
return result as LumeEvent;
if (embed) {
const event: LumeEvent = JSON.parse(embed);
if (event.kind === 1) embed['content'] = parser(event);
return embed;
} else {
if (fallback) {
const embed: LumeEvent = JSON.parse(fallback);
if (embed.kind === 1) embed['content'] = parser(embed);
embed['event_id'] = embed.id;
await createNote(
embed.id,
embed.pubkey,
embed.kind,
embed.tags,
embed.content as unknown as string,
embed.created_at
);
return embed;
} else {
const event = (await ndk.fetchEvent(id)) as unknown as LumeEvent;
if (event) {
await createNote(
event.id,
event.pubkey,
event.kind,
event.tags,
event.content as unknown as string,
event.created_at
);
event['event_id'] = event.id;
if (event.kind === 1) event['content'] = parser(event);
return event as unknown as LumeEvent;
} else {
throw new Error('Event not found');
}
}
const event = (await ndk.fetchEvent(id)) as unknown as LumeEvent;
if (!event) throw new Error('Event not found');
if (event.kind === 1) event['content'] = parser(event);
return event as unknown as LumeEvent;
}
},
{

View File

@@ -1,5 +1,13 @@
import { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKUser } from '@nostr-dev-kit/ndk';
import {
NDKEvent,
NDKFilter,
NDKKind,
NDKPrivateKeySigner,
NDKSubscription,
NDKUser,
} from '@nostr-dev-kit/ndk';
import { ndkAdapter } from '@nostr-fetch/adapter-ndk';
import { useQueryClient } from '@tanstack/react-query';
import destr from 'destr';
import { LRUCache } from 'lru-cache';
import { NostrFetcher } from 'nostr-fetch';
@@ -7,152 +15,95 @@ import { nip19 } from 'nostr-tools';
import { useMemo } from 'react';
import { useNDK } from '@libs/ndk/provider';
import {
countTotalNotes,
createChat,
createNote,
getLastLogin,
updateAccount,
} from '@libs/storage';
import { updateAccount } from '@libs/storage';
import { useStronghold } from '@stores/stronghold';
import { nHoursAgo } from '@utils/date';
import { useAccount } from '@utils/hooks/useAccount';
export function useNostr() {
const { ndk, relayUrls } = useNDK();
const { account } = useAccount();
const fetcher = useMemo(() => NostrFetcher.withCustomPool(ndkAdapter(ndk)), [ndk]);
const queryClient = useQueryClient();
const privkey = useStronghold((state) => state.privkey);
const fetcher = useMemo(() => NostrFetcher.withCustomPool(ndkAdapter(ndk)), [ndk]);
const subManager = useMemo(
() =>
new LRUCache<string, NDKSubscription, void>({
max: 4,
dispose: (sub) => sub.stop(),
}),
[]
);
async function fetchNetwork(prevFollow?: string[]) {
const follows = new Set<string>(prevFollow || []);
const lruNetwork = new LRUCache<string, string, void>({ max: 300 });
const sub = async (
filter: NDKFilter,
callback: (event: NDKEvent) => void,
closeOnEose?: boolean
) => {
const subEvent = ndk.subscribe(filter, { closeOnEose: closeOnEose ?? true });
subManager.set(JSON.stringify(filter), subEvent);
let network: string[];
subEvent.addListener('event', (event: NDKEvent) => {
callback(event);
});
};
// fetch user's follows
if (!prevFollow) {
console.log("fetching user's follow...");
const user = ndk.getUser({ hexpubkey: account.pubkey });
const list = await user.follows();
list.forEach((item: NDKUser) => {
follows.add(nip19.decode(item.npub).data as string);
});
}
const fetchUserData = async (preFollows?: string[]) => {
try {
const follows = new Set<string>(preFollows || []);
const lruNetwork = new LRUCache<string, string, void>({ max: 300 });
// fetch network
if (!account.network) {
console.log("fetching user's network...");
// fetch user's follows
if (!preFollows) {
const user = ndk.getUser({ hexpubkey: account.pubkey });
const list = await user.follows();
list.forEach((item: NDKUser) => {
follows.add(nip19.decode(item.npub).data as string);
});
}
// build user's network
const events = await ndk.fetchEvents({ kinds: [3], authors: [...follows] });
events.forEach((event: NDKEvent) => {
event.tags.forEach((tag) => {
if (tag[0] === 'p') lruNetwork.set(tag[1], tag[1]);
});
});
network = [...lruNetwork.values()] as string[];
} else {
network = account.network;
}
const network = [...lruNetwork.values()] as string[];
// update user in db
await updateAccount('follows', [...follows]);
await updateAccount('network', network);
await updateAccount('follows', [...follows]);
await updateAccount('network', [...new Set([...follows, ...network])]);
return [...new Set([...follows, ...network])];
}
async function fetchNotes(prevFollow?: string[]) {
try {
if (!ndk) return { status: 'failed', message: 'NDK instance not found' };
const network = await fetchNetwork(prevFollow);
const totalNotes = await countTotalNotes();
const lastLogin = await getLastLogin();
if (network.length > 0) {
console.log('fetching notes...');
let since: number;
if (totalNotes === 0 || lastLogin === 0) {
since = nHoursAgo(24);
} else {
since = lastLogin;
}
const events = await fetcher.fetchAllEvents(
relayUrls,
{
kinds: [1],
authors: network,
},
{ since: since }
);
for (const event of events) {
await createNote(
event.id,
event.pubkey,
event.kind,
event.tags,
event.content,
event.created_at
);
}
}
queryClient.invalidateQueries(['currentAccount']);
return { status: 'ok' };
} catch (e) {
console.error('failed fetch notes, error: ', e);
return { status: 'failed', message: e };
}
}
};
async function fetchChats() {
const fetchNotes = async (since: number) => {
try {
if (!ndk) return { status: 'failed', message: 'NDK instance not found' };
const lastLogin = await getLastLogin();
const outgoingMessages = await fetcher.fetchAllEvents(
const events = await fetcher.fetchAllEvents(
relayUrls,
{
kinds: [4],
authors: [account.pubkey],
kinds: [1],
authors: account.network ?? account.follows,
},
{ since: lastLogin }
{ since: since }
);
const incomingMessages = await fetcher.fetchAllEvents(
relayUrls,
{ kinds: [4], '#p': [account.pubkey] },
{ since: lastLogin }
);
const messages = [...outgoingMessages, ...incomingMessages];
for (const event of messages) {
const receiverPubkey = event.tags.find((t) => t[0] === 'p')[1] || account.pubkey;
await createChat(
event.id,
receiverPubkey,
event.pubkey,
event.content,
event.tags,
event.created_at
);
}
return { status: 'ok' };
return { status: 'ok', notes: events };
} catch (e) {
console.error('failed fetch messages, error: ', e);
console.error('failed get notes, error: ', e);
return { status: 'failed', message: e };
}
}
};
const publish = async ({
content,
@@ -203,5 +154,5 @@ export function useNostr() {
return res;
};
return { fetchNotes, fetchChats, publish, createZap };
return { sub, fetchUserData, fetchNotes, publish, createZap };
}

View File

@@ -14,7 +14,7 @@ export function useProfile(pubkey: string, fallback?: string) {
['user', pubkey],
async () => {
if (!fallback) {
const user = await ndk.getUser({ hexpubkey: pubkey });
const user = ndk.getUser({ hexpubkey: pubkey });
await user.fetchProfile();
if (user.profile) {
user.profile.display_name = user.profile.displayName;