From 353c18bb76bd9f6385bed8454a39e658cac5f9e8 Mon Sep 17 00:00:00 2001 From: reya Date: Sat, 27 Jan 2024 09:08:41 +0700 Subject: [PATCH 1/3] feat: update ark provider --- packages/ark/src/ark.ts | 4 +- packages/ark/src/hooks/useProfile.ts | 7 +- packages/ark/src/provider.tsx | 227 ++++++++++++--------------- 3 files changed, 110 insertions(+), 128 deletions(-) diff --git a/packages/ark/src/ark.ts b/packages/ark/src/ark.ts index 6fb98464..0b60f4e1 100644 --- a/packages/ark/src/ark.ts +++ b/packages/ark/src/ark.ts @@ -145,7 +145,6 @@ export class Ark { cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST, }); - if (!profile) return null; return profile; } catch { throw new Error("user not found"); @@ -167,8 +166,9 @@ export class Ark { (user) => user.pubkey, ); - if (!pubkey || pubkey === this.account.pubkey) + if (!pubkey || pubkey === this.account.pubkey) { this.account.contacts = contacts; + } return contacts; } catch (e) { diff --git a/packages/ark/src/hooks/useProfile.ts b/packages/ark/src/hooks/useProfile.ts index 30c4f734..ac197554 100644 --- a/packages/ark/src/hooks/useProfile.ts +++ b/packages/ark/src/hooks/useProfile.ts @@ -1,8 +1,10 @@ -import { useQuery } from "@tanstack/react-query"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useArk } from "./useArk"; export function useProfile(pubkey: string) { const ark = useArk(); + const queryClient = useQueryClient(); + const { isLoading, isError, @@ -17,6 +19,9 @@ export function useProfile(pubkey: string) { ); return profile; }, + initialData: () => { + return queryClient.getQueryData(["user", pubkey]); + }, refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, diff --git a/packages/ark/src/provider.tsx b/packages/ark/src/provider.tsx index 24817ce9..a9c2abfd 100644 --- a/packages/ark/src/provider.tsx +++ b/packages/ark/src/provider.tsx @@ -23,6 +23,7 @@ import { useSetAtom } from "jotai"; import Linkify from "linkify-react"; import { normalizeRelayUrlSet } from "nostr-fetch"; import { PropsWithChildren, useEffect, useState } from "react"; +import { toast } from "sonner"; import { Ark } from "./ark"; import { LumeContext } from "./context"; @@ -80,55 +81,65 @@ export const LumeProvider = ({ children }: PropsWithChildren) => { return new NDKPrivateKeySigner(userPrivkey); } catch (e) { - console.error(e); + toast.error(String(e)); return null; } } async function initNDK() { - const explicitRelayUrls = normalizeRelayUrlSet([ - "wss://nostr.mutinywallet.com/", - "wss://bostr.nokotaro.com/", - ]); + try { + const explicitRelayUrls = normalizeRelayUrlSet([ + "wss://nostr.mutinywallet.com/", + "wss://bostr.nokotaro.com/", + ]); - const tauriCache = new NDKCacheAdapterTauri(storage); - const ndk = new NDK({ - cacheAdapter: tauriCache, - explicitRelayUrls, - enableOutboxModel: !storage.settings.lowPower, - autoConnectUserRelays: !storage.settings.lowPower, - autoFetchUserMutelist: !storage.settings.lowPower, - clientName: "Lume", - }); + const outboxRelayUrls = normalizeRelayUrlSet(["wss://purplepag.es/"]); - // use tauri fetch - ndk.httpFetch = fetch; + const tauriCache = new NDKCacheAdapterTauri(storage); + const ndk = new NDK({ + cacheAdapter: tauriCache, + explicitRelayUrls, + outboxRelayUrls, + enableOutboxModel: !storage.settings.lowPower, + autoConnectUserRelays: !storage.settings.lowPower, + autoFetchUserMutelist: false, // #TODO: add support mute list + clientName: "Lume", + }); - // add signer - const signer = await initNostrSigner({ - nsecbunker: storage.settings.nsecbunker, - }); + // use tauri fetch + ndk.httpFetch = fetch; - if (signer) ndk.signer = signer; + // add signer + const signer = await initNostrSigner({ + nsecbunker: storage.settings.nsecbunker, + }); - // connect - await ndk.connect(3000); + if (signer) ndk.signer = signer; - // auth - ndk.relayAuthDefaultPolicy = async (relay: NDKRelay, challenge: string) => { - const signIn = NDKRelayAuthPolicies.signIn({ ndk }); - const event = await signIn(relay, challenge).catch((e) => - console.log("auth failed", e), - ); - if (event) { - await sendNativeNotification( - `You've sign in sucessfully to relay: ${relay.url}`, + // connect + await ndk.connect(3000); + + // auth + ndk.relayAuthDefaultPolicy = async ( + relay: NDKRelay, + challenge: string, + ) => { + const signIn = NDKRelayAuthPolicies.signIn({ ndk }); + const event = await signIn(relay, challenge).catch((e) => + console.log("auth failed", e), ); - return event; - } - }; + if (event) { + await sendNativeNotification( + `You've sign in sucessfully to relay: ${relay.url}`, + ); + return event; + } + }; - setNDK(ndk); + setNDK(ndk); + } catch (e) { + toast.error(String(e)); + } } async function initArk() { @@ -137,102 +148,68 @@ export const LumeProvider = ({ children }: PropsWithChildren) => { // ark utils const ark = new Ark({ ndk, account: storage.currentUser }); - if (ndk && storage.currentUser) { - const user = new NDKUser({ pubkey: storage.currentUser.pubkey }); - ndk.activeUser = user; + try { + if (ndk && storage.currentUser) { + const user = new NDKUser({ pubkey: storage.currentUser.pubkey }); + ndk.activeUser = user; - // update contacts - await ark.getUserContacts(); + // update contacts + const contacts = await ark.getUserContacts(); - // subscribe for new activity - const activitySub = ndk.subscribe( - { - kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Zap], - since: Math.floor(Date.now() / 1000), - "#p": [ark.account.pubkey], - }, - { closeOnEose: false, groupable: false }, - ); - - activitySub.addListener("event", async (event: NDKEvent) => { - if (event.pubkey === storage.currentUser.pubkey) return; - - setUnreadActivity((state) => state + 1); - const profile = await ark.getUserProfile(event.pubkey); - - switch (event.kind) { - case NDKKind.Text: - return await sendNativeNotification( - `${ - profile.displayName || profile.name || "Anon" - } has replied to your note`, - ); - case NDKKind.Repost: - return await sendNativeNotification( - `${ - profile.displayName || profile.name || "Anon" - } has reposted to your note`, - ); - case NDKKind.Zap: - return await sendNativeNotification( - `${ - profile.displayName || profile.name || "Anon" - } has zapped to your note`, - ); - default: - break; + if (contacts?.length) { + console.log("total contacts: ", contacts.length); + for (const pubkey of ark.account.contacts) { + await queryClient.prefetchQuery({ + queryKey: ["user", pubkey], + queryFn: async () => { + return await ark.getUserProfile(pubkey); + }, + }); + } } - }); - // prefetch activty - await queryClient.prefetchInfiniteQuery({ - queryKey: ["activity"], - initialPageParam: 0, - queryFn: async ({ - signal, - pageParam, - }: { - signal: AbortSignal; - pageParam: number; - }) => { - const events = await ark.getInfiniteEvents({ - filter: { - kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Zap], - "#p": [ark.account.pubkey], - }, - limit: FETCH_LIMIT, - pageParam, - signal, - }); + // subscribe for new activity + const activitySub = ndk.subscribe( + { + kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Zap], + since: Math.floor(Date.now() / 1000), + "#p": [ark.account.pubkey], + }, + { closeOnEose: false, groupable: false }, + ); - return events; - }, - }); + activitySub.addListener("event", async (event: NDKEvent) => { + if (event.pubkey === storage.currentUser.pubkey) return; - // prefetch timeline - await queryClient.prefetchInfiniteQuery({ - queryKey: ["timeline-9999"], - initialPageParam: 0, - queryFn: async ({ - signal, - pageParam, - }: { - signal: AbortSignal; - pageParam: number; - }) => { - const events = await ark.getInfiniteEvents({ - filter: { - kinds: [NDKKind.Text, NDKKind.Repost], - authors: ark.account.contacts, - }, - limit: FETCH_LIMIT, - pageParam, - signal, - }); + setUnreadActivity((state) => state + 1); + const profile = await ark.getUserProfile(event.pubkey); - return events; - }, - }); + switch (event.kind) { + case NDKKind.Text: + return await sendNativeNotification( + `${ + profile.displayName || profile.name || "Anon" + } has replied to your note`, + ); + case NDKKind.Repost: + return await sendNativeNotification( + `${ + profile.displayName || profile.name || "Anon" + } has reposted to your note`, + ); + case NDKKind.Zap: + return await sendNativeNotification( + `${ + profile.displayName || profile.name || "Anon" + } has zapped to your note`, + ); + default: + break; + } + }); + } + } catch (e) { + toast.error(String(e)); } setArk(ark); From b11e2a4291965b8c7ebe2aa02bfd937885a8412a Mon Sep 17 00:00:00 2001 From: reya Date: Sat, 27 Jan 2024 11:16:17 +0700 Subject: [PATCH 2/3] fix: update mention popup style --- packages/ark/src/components/user/avatar.tsx | 7 +++++-- packages/ui/src/editor/form.tsx | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/ark/src/components/user/avatar.tsx b/packages/ark/src/components/user/avatar.tsx index 60b01a19..22d14057 100644 --- a/packages/ark/src/components/user/avatar.tsx +++ b/packages/ark/src/components/user/avatar.tsx @@ -39,7 +39,10 @@ export function UserAvatar({ className }: { className?: string }) { alt={user.pubkey} loading="eager" decoding="async" - className={cn("bg-black dark:bg-white", className)} + className={cn( + "bg-black dark:bg-white ring-1 ring-black/5 dark:ring-white/5", + className, + )} /> ) : ( )} diff --git a/packages/ui/src/editor/form.tsx b/packages/ui/src/editor/form.tsx index 91c4d272..85e29d5a 100644 --- a/packages/ui/src/editor/form.tsx +++ b/packages/ui/src/editor/form.tsx @@ -356,7 +356,7 @@ export function EditorForm() {
{filters.map((contact, i) => (
diff --git a/packages/ark/src/components/user/followButton.tsx b/packages/ark/src/components/user/followButton.tsx index d7dc3672..20bf6ada 100644 --- a/packages/ark/src/components/user/followButton.tsx +++ b/packages/ark/src/components/user/followButton.tsx @@ -13,6 +13,7 @@ export function UserFollowButton({ const [followed, setFollowed] = useState(false); const toggleFollow = async () => { + setLoading(true); if (!followed) { const add = await ark.createContact(target); if (add) setFollowed(true); @@ -20,6 +21,7 @@ export function UserFollowButton({ const remove = await ark.deleteContact(target); if (remove) setFollowed(false); } + setLoading(false); }; useEffect(() => { @@ -37,7 +39,12 @@ export function UserFollowButton({ }, []); return ( -