rename some files and add nip 94 widget
This commit is contained in:
211
src/utils/hooks/useNostr.ts
Normal file
211
src/utils/hooks/useNostr.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import {
|
||||
NDKEvent,
|
||||
NDKFilter,
|
||||
NDKKind,
|
||||
NDKPrivateKeySigner,
|
||||
NDKSubscription,
|
||||
NDKUser,
|
||||
} from '@nostr-dev-kit/ndk';
|
||||
import { ndkAdapter } from '@nostr-fetch/adapter-ndk';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
import { NostrFetcher } from 'nostr-fetch';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useNDK } from '@libs/ndk/provider';
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { useStronghold } from '@stores/stronghold';
|
||||
|
||||
import { nHoursAgo } from '@utils/date';
|
||||
|
||||
export function useNostr() {
|
||||
const { ndk, relayUrls } = useNDK();
|
||||
const { db } = useStorage();
|
||||
|
||||
const privkey = useStronghold((state) => state.privkey);
|
||||
const subManager = useMemo(
|
||||
() =>
|
||||
new LRUCache<string, NDKSubscription, void>({
|
||||
max: 4,
|
||||
dispose: (sub) => sub.stop(),
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const sub = async (
|
||||
filter: NDKFilter,
|
||||
callback: (event: NDKEvent) => void,
|
||||
closeOnEose?: boolean
|
||||
) => {
|
||||
if (!ndk) throw new Error('NDK instance not found');
|
||||
|
||||
const subEvent = ndk.subscribe(filter, { closeOnEose: closeOnEose ?? true });
|
||||
subManager.set(JSON.stringify(filter), subEvent);
|
||||
|
||||
subEvent.addListener('event', (event: NDKEvent) => {
|
||||
callback(event);
|
||||
});
|
||||
};
|
||||
|
||||
const fetchUserData = async (preFollows?: string[]) => {
|
||||
try {
|
||||
const follows = new Set<string>(preFollows || []);
|
||||
const lruNetwork = new LRUCache<string, string, void>({ max: 300 });
|
||||
|
||||
// fetch user's follows
|
||||
if (!preFollows) {
|
||||
const user = ndk.getUser({ hexpubkey: db.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],
|
||||
limit: 300,
|
||||
});
|
||||
|
||||
events.forEach((event: NDKEvent) => {
|
||||
event.tags.forEach((tag) => {
|
||||
if (tag[0] === 'p') lruNetwork.set(tag[1], tag[1]);
|
||||
});
|
||||
});
|
||||
|
||||
const network = [...lruNetwork.values()] as string[];
|
||||
|
||||
await db.updateAccount('follows', [...follows]);
|
||||
await db.updateAccount('network', [...new Set([...follows, ...network])]);
|
||||
|
||||
// clear lru caches
|
||||
lruNetwork.clear();
|
||||
|
||||
return { status: 'ok', message: 'User data fetched' };
|
||||
} catch (e) {
|
||||
return { status: 'failed', message: e };
|
||||
}
|
||||
};
|
||||
|
||||
const prefetchEvents = async () => {
|
||||
try {
|
||||
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(ndk));
|
||||
const dbEventsEmpty = await db.isEventsEmpty();
|
||||
|
||||
let since: number;
|
||||
if (dbEventsEmpty || db.account.last_login_at === 0) {
|
||||
since = nHoursAgo(24);
|
||||
} else {
|
||||
since = db.account.last_login_at;
|
||||
}
|
||||
|
||||
console.log("prefetching events with user's network: ", db.account.network.length);
|
||||
console.log('prefetching events since: ', since);
|
||||
|
||||
const events = await fetcher.fetchAllEvents(
|
||||
relayUrls,
|
||||
{
|
||||
kinds: [NDKKind.Text, NDKKind.Repost, 1063, NDKKind.Article],
|
||||
authors: db.account.network,
|
||||
},
|
||||
{ since: since }
|
||||
);
|
||||
|
||||
// save all events to database
|
||||
for (const event of events) {
|
||||
let root: string;
|
||||
let reply: string;
|
||||
if (event.tags?.[0]?.[0] === 'e' && !event.tags?.[0]?.[3]) {
|
||||
root = event.tags[0][1];
|
||||
} else {
|
||||
root = event.tags.find((el) => el[3] === 'root')?.[1];
|
||||
reply = event.tags.find((el) => el[3] === 'reply')?.[1];
|
||||
}
|
||||
await db.createEvent(
|
||||
event.id,
|
||||
JSON.stringify(event),
|
||||
event.pubkey,
|
||||
event.kind,
|
||||
root,
|
||||
reply,
|
||||
event.created_at
|
||||
);
|
||||
}
|
||||
|
||||
return { status: 'ok', data: [], message: 'prefetch completed' };
|
||||
} catch (e) {
|
||||
console.error('prefetch events failed, error: ', e);
|
||||
return { status: 'failed', data: [], message: e };
|
||||
}
|
||||
};
|
||||
|
||||
const fetchActivities = async () => {
|
||||
try {
|
||||
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(ndk));
|
||||
const events = await fetcher.fetchAllEvents(
|
||||
relayUrls,
|
||||
{
|
||||
kinds: [
|
||||
NDKKind.Text,
|
||||
NDKKind.Contacts,
|
||||
NDKKind.Repost,
|
||||
NDKKind.Reaction,
|
||||
NDKKind.Zap,
|
||||
],
|
||||
'#p': [db.account.pubkey],
|
||||
},
|
||||
{ since: nHoursAgo(24) },
|
||||
{ sort: true }
|
||||
);
|
||||
|
||||
return events as unknown as NDKEvent[];
|
||||
} catch (e) {
|
||||
console.error('Error fetching activities', e);
|
||||
}
|
||||
};
|
||||
|
||||
const publish = async ({
|
||||
content,
|
||||
kind,
|
||||
tags,
|
||||
}: {
|
||||
content: string;
|
||||
kind: NDKKind | number;
|
||||
tags: string[][];
|
||||
}): Promise<NDKEvent> => {
|
||||
if (!privkey) throw new Error('Private key not found');
|
||||
|
||||
const event = new NDKEvent(ndk);
|
||||
const signer = new NDKPrivateKeySigner(privkey);
|
||||
|
||||
event.content = content;
|
||||
event.kind = kind;
|
||||
event.created_at = Math.floor(Date.now() / 1000);
|
||||
event.pubkey = db.account.pubkey;
|
||||
event.tags = tags;
|
||||
|
||||
await event.sign(signer);
|
||||
await event.publish();
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
const createZap = async (event: NDKEvent, amount: number, message?: string) => {
|
||||
if (!privkey) throw new Error('Private key not found');
|
||||
|
||||
if (!ndk.signer) {
|
||||
const signer = new NDKPrivateKeySigner(privkey);
|
||||
ndk.signer = signer;
|
||||
}
|
||||
|
||||
// @ts-expect-error, NostrEvent to NDKEvent
|
||||
const ndkEvent = new NDKEvent(ndk, event);
|
||||
const res = await ndkEvent.zap(amount, message ?? 'zap from lume');
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
return { sub, fetchUserData, prefetchEvents, fetchActivities, publish, createZap };
|
||||
}
|
||||
Reference in New Issue
Block a user