From 95124e5ded56990f58eccf098ec4ef36ba09d88d Mon Sep 17 00:00:00 2001 From: reya Date: Thu, 7 Dec 2023 11:50:25 +0700 Subject: [PATCH 1/8] wip: ark --- src/app/auth/create.tsx | 42 +- src/app/auth/follow.tsx | 22 +- src/app/auth/import.tsx | 33 +- src/app/auth/tutorials/note.tsx | 24 +- src/app/chats/chat.tsx | 25 +- src/app/chats/index.tsx | 13 +- src/app/new/article.tsx | 24 +- src/app/new/file.tsx | 22 +- src/app/new/post.tsx | 13 +- src/app/new/privkey.tsx | 18 +- src/app/settings/components/contactCard.tsx | 11 +- src/app/settings/components/relayCard.tsx | 13 +- src/app/settings/editContact.tsx | 15 +- src/app/settings/editProfile.tsx | 49 +- src/libs/ark/ark.ts | 661 ++++++++++++++++++ src/libs/ark/cache.ts | 539 ++++++++++++++ src/libs/ark/index.ts | 3 + src/libs/ark/provider.tsx | 123 ++++ src/main.jsx | 11 +- src/shared/accounts/logout.tsx | 14 +- src/shared/notes/repost.tsx | 15 +- src/shared/widgets/other/liveUpdater.tsx | 32 +- .../widgets/other/nostrBandUserProfile.tsx | 16 +- src/utils/hooks/useEvent.ts | 32 +- src/utils/hooks/useProfile.ts | 11 +- src/utils/hooks/useRelay.ts | 53 +- src/utils/hooks/useRichContent.tsx | 8 +- src/utils/types.d.ts | 6 + 28 files changed, 1547 insertions(+), 301 deletions(-) create mode 100644 src/libs/ark/ark.ts create mode 100644 src/libs/ark/cache.ts create mode 100644 src/libs/ark/index.ts create mode 100644 src/libs/ark/provider.tsx diff --git a/src/app/auth/create.tsx b/src/app/auth/create.tsx index aa8f5d40..a1ebd64c 100644 --- a/src/app/auth/create.tsx +++ b/src/app/auth/create.tsx @@ -1,4 +1,4 @@ -import { NDKEvent, NDKKind, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; +import { NDKKind, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; import { downloadDir } from '@tauri-apps/api/path'; import { writeText } from '@tauri-apps/plugin-clipboard-manager'; import { save } from '@tauri-apps/plugin-dialog'; @@ -11,8 +11,7 @@ import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { AvatarUploader } from '@shared/avatarUploader'; import { ArrowLeftIcon, InfoIcon, LoaderIcon } from '@shared/icons'; @@ -29,13 +28,12 @@ export function CreateAccountScreen() { privkey: string; }>(null); + const { ark } = useArk(); const { register, handleSubmit, formState: { isDirty, isValid }, } = useForm(); - const { db } = useStorage(); - const { ndk } = useNDK(); const navigate = useNavigate(); @@ -62,28 +60,22 @@ export function CreateAccountScreen() { const userNsec = nip19.nsecEncode(userPrivkey); const signer = new NDKPrivateKeySigner(userPrivkey); - ndk.signer = signer; + ark.updateNostrSigner({ signer }); - const event = new NDKEvent(ndk); - event.content = JSON.stringify(profile); - event.kind = NDKKind.Metadata; - event.pubkey = userPubkey; - event.tags = []; - - const publish = await event.publish(); + const publish = await ark.createEvent({ + content: JSON.stringify(profile), + kind: NDKKind.Metadata, + tags: [], + publish: true, + }); if (publish) { - await db.createAccount(userNpub, userPubkey); - await db.secureSave(userPubkey, userPrivkey); - - const relayListEvent = new NDKEvent(ndk); - relayListEvent.kind = NDKKind.RelayList; - relayListEvent.tags = [...ndk.pool.relays.values()].map((item) => [ - 'r', - item.url, - ]); - - await relayListEvent.publish(); + await ark.createAccount(userNpub, userPubkey, userPrivkey); + await ark.createEvent({ + kind: NDKKind.RelayList, + tags: [ark.relays], + publish: true, + }); setKeys({ npub: userNpub, @@ -93,7 +85,7 @@ export function CreateAccountScreen() { }); setLoading(false); } else { - toast('Create account failed'); + toast('Cannot publish user profile, please try again later.'); setLoading(false); } } catch (e) { diff --git a/src/app/auth/follow.tsx b/src/app/auth/follow.tsx index cf436143..6805f6c7 100644 --- a/src/app/auth/follow.tsx +++ b/src/app/auth/follow.tsx @@ -1,4 +1,4 @@ -import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; +import { NDKKind } from '@nostr-dev-kit/ndk'; import * as Accordion from '@radix-ui/react-accordion'; import { useQuery } from '@tanstack/react-query'; import { nip19 } from 'nostr-tools'; @@ -7,8 +7,7 @@ import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { twMerge } from 'tailwind-merge'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ArrowLeftIcon, @@ -37,8 +36,7 @@ const POPULAR_USERS = [ const LUME_USERS = ['npub1zfss807aer0j26mwp2la0ume0jqde3823rmu97ra6sgyyg956e0s6xw445']; export function FollowScreen() { - const { ndk } = useNDK(); - const { db } = useStorage(); + const { ark } = useArk(); const { status, data } = useQuery({ queryKey: ['trending-profiles-widget'], queryFn: async () => { @@ -68,16 +66,16 @@ export function FollowScreen() { setLoading(true); if (!follows.length) return navigate('/auth/finish'); - const event = new NDKEvent(ndk); - event.kind = NDKKind.Contacts; - event.tags = follows.map((item) => { - if (item.startsWith('npub')) return ['p', nip19.decode(item).data as string]; - return ['p', item]; + const publish = await ark.createEvent({ + kind: NDKKind.Contacts, + tags: follows.map((item) => { + if (item.startsWith('npub')) return ['p', nip19.decode(item).data as string]; + return ['p', item]; + }), }); - const publish = await event.publish(); if (publish) { - db.account.contacts = follows.map((item) => { + ark.account.contacts = follows.map((item) => { if (item.startsWith('npub')) return nip19.decode(item).data as string; return item; }); diff --git a/src/app/auth/import.tsx b/src/app/auth/import.tsx index 7a995d3a..78744aac 100644 --- a/src/app/auth/import.tsx +++ b/src/app/auth/import.tsx @@ -8,16 +8,12 @@ import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { twMerge } from 'tailwind-merge'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ArrowLeftIcon, LoaderIcon } from '@shared/icons'; import { User } from '@shared/user'; export function ImportAccountScreen() { - const { db } = useStorage(); - const { ndk } = useNDK(); - const [npub, setNpub] = useState(''); const [nsec, setNsec] = useState(''); const [pubkey, setPubkey] = useState(undefined); @@ -25,6 +21,7 @@ export function ImportAccountScreen() { const [created, setCreated] = useState({ ok: false, remote: false }); const [savedPrivkey, setSavedPrivkey] = useState(false); + const { ark } = useArk(); const navigate = useNavigate(); const submitNpub = async () => { @@ -47,8 +44,8 @@ export function ImportAccountScreen() { const pubkey = nip19.decode(npub.split('#')[0]).data as string; const localSigner = NDKPrivateKeySigner.generate(); - await db.createSetting('nsecbunker', '1'); - await db.secureSave(`${pubkey}-nsecbunker`, localSigner.privateKey); + await ark.createSetting('nsecbunker', '1'); + await ark.createPrivkey(`${pubkey}-nsecbunker`, localSigner.privateKey); // open nsecbunker web app in default browser await open('https://app.nsecbunker.com/keys'); @@ -60,8 +57,7 @@ export function ImportAccountScreen() { const remoteSigner = new NDKNip46Signer(bunker, npub, localSigner); await remoteSigner.blockUntilReady(); - - ndk.signer = remoteSigner; + ark.updateNostrSigner({ signer: remoteSigner }); setPubkey(pubkey); setCreated({ ok: false, remote: true }); @@ -80,14 +76,10 @@ export function ImportAccountScreen() { setLoading(true); // add account to db - await db.createAccount(npub, pubkey); + await ark.createAccount(npub, pubkey); - // get account metadata - const user = ndk.getUser({ pubkey }); - if (user) { - db.account.contacts = [...(await user.follows())].map((user) => user.pubkey); - db.account.relayList = await user.relayList(); - } + // get account contacts + await ark.getUserContacts({ pubkey }); setCreated((prev) => ({ ...prev, ok: true })); setLoading(false); @@ -109,9 +101,8 @@ export function ImportAccountScreen() { if (nsec.length > 50 && nsec.startsWith('nsec1')) { try { const privkey = nip19.decode(nsec).data as string; - await db.secureSave(pubkey, privkey); - - ndk.signer = new NDKPrivateKeySigner(privkey); + await ark.createPrivkey(pubkey, privkey); + ark.updateNostrSigner({ signer: new NDKPrivateKeySigner(privkey) }); setSavedPrivkey(true); } catch (e) { @@ -290,9 +281,9 @@ export function ImportAccountScreen() {

Lume will put your private key to{' '} - {db.platform === 'macos' + {ark.platform === 'macos' ? 'Apple Keychain (macOS)' - : db.platform === 'windows' + : ark.platform === 'windows' ? 'Credential Manager (Windows)' : 'Secret Service (Linux)'} diff --git a/src/app/auth/tutorials/note.tsx b/src/app/auth/tutorials/note.tsx index 5a6a7170..b8a21434 100644 --- a/src/app/auth/tutorials/note.tsx +++ b/src/app/auth/tutorials/note.tsx @@ -1,21 +1,23 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; import { Link } from 'react-router-dom'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { EditIcon, ReactionIcon, ReplyIcon, RepostIcon, ZapIcon } from '@shared/icons'; import { TextNote } from '@shared/notes'; export function TutorialNoteScreen() { - const { ndk } = useNDK(); - const exampleEvent = new NDKEvent(ndk, { - id: 'a3527670dd9b178bf7c2a9ea673b63bc8bfe774942b196691145343623c45821', - pubkey: '04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9', - created_at: 1701355223, - kind: 1, - tags: [], - content: 'good morning nostr, stay humble and stack sats 🫡', - sig: '9e0bd67ec25598744f20bff0fe360fdf190c4240edb9eea260e50f77e07f94ea767ececcc6270819b7f64e5e7ca1fe20b4971f46dc120e6db43114557f3a6dae', + const { ark } = useArk(); + + const exampleEvent = ark.createNDKEvent({ + event: { + id: 'a3527670dd9b178bf7c2a9ea673b63bc8bfe774942b196691145343623c45821', + pubkey: '04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9', + created_at: 1701355223, + kind: 1, + tags: [], + content: 'good morning nostr, stay humble and stack sats 🫡', + sig: '9e0bd67ec25598744f20bff0fe360fdf190c4240edb9eea260e50f77e07f94ea767ececcc6270819b7f64e5e7ca1fe20b4971f46dc120e6db43114557f3a6dae', + }, }); return ( diff --git a/src/app/chats/chat.tsx b/src/app/chats/chat.tsx index 4767cc6f..5f66f3d8 100644 --- a/src/app/chats/chat.tsx +++ b/src/app/chats/chat.tsx @@ -1,4 +1,4 @@ -import { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'; +import { NDKEvent } from '@nostr-dev-kit/ndk'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useRef } from 'react'; import { useParams } from 'react-router-dom'; @@ -7,8 +7,7 @@ import { VList, VListHandle } from 'virtua'; import { ChatForm } from '@app/chats/components/chatForm'; import { ChatMessage } from '@app/chats/components/message'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { User } from '@shared/user'; @@ -16,8 +15,7 @@ import { User } from '@shared/user'; import { useNostr } from '@utils/hooks/useNostr'; export function ChatScreen() { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { pubkey } = useParams(); const { fetchNIP04Messages } = useNostr(); const { status, data } = useQuery({ @@ -59,7 +57,7 @@ export function ChatScreen() { ); }, @@ -71,20 +69,15 @@ export function ChatScreen() { }, [data]); useEffect(() => { - const sub: NDKSubscription = ndk.subscribe( - { + const sub = ark.subscribe({ + filter: { kinds: [4], - authors: [db.account.pubkey], + authors: [ark.account.pubkey], '#p': [pubkey], since: Math.floor(Date.now() / 1000), }, - { - closeOnEose: false, - } - ); - - sub.addListener('event', (event) => { - newMessage.mutate(event); + closeOnEose: false, + cb: (event) => newMessage.mutate(event), }); return () => { diff --git a/src/app/chats/index.tsx b/src/app/chats/index.tsx index 40364e94..67698c9a 100644 --- a/src/app/chats/index.tsx +++ b/src/app/chats/index.tsx @@ -1,20 +1,15 @@ import { NDKEvent } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; -import { useCallback, useEffect } from 'react'; -import { Outlet, useNavigate } from 'react-router-dom'; +import { useCallback } from 'react'; +import { Outlet } from 'react-router-dom'; import { ChatListItem } from '@app/chats/components/chatListItem'; -import { useNDK } from '@libs/ndk/provider'; - import { LoaderIcon } from '@shared/icons'; import { useNostr } from '@utils/hooks/useNostr'; export function ChatsScreen() { - const navigate = useNavigate(); - - const { ndk } = useNDK(); const { getAllNIP04Chats } = useNostr(); const { status, data } = useQuery({ queryKey: ['nip04-chats'], @@ -34,10 +29,6 @@ export function ChatsScreen() { [data] ); - useEffect(() => { - if (!ndk.signer) navigate('/new/privkey'); - }, []); - return (

diff --git a/src/app/new/article.tsx b/src/app/new/article.tsx index ad9e0bd4..47b2ef21 100644 --- a/src/app/new/article.tsx +++ b/src/app/new/article.tsx @@ -1,4 +1,4 @@ -import { NDKEvent, NDKKind, NDKTag } from '@nostr-dev-kit/ndk'; +import { NDKKind, NDKTag } from '@nostr-dev-kit/ndk'; import CharacterCount from '@tiptap/extension-character-count'; import Image from '@tiptap/extension-image'; import Placeholder from '@tiptap/extension-placeholder'; @@ -12,7 +12,7 @@ import { Markdown } from 'tiptap-markdown'; import { ArticleCoverUploader, MediaUploader, MentionPopup } from '@app/new/components'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { BoldIcon, @@ -25,7 +25,7 @@ import { } from '@shared/icons'; export function NewArticleScreen() { - const { ndk } = useNDK(); + const { ark } = useArk(); const [height, setHeight] = useState(0); const [loading, setLoading] = useState(false); @@ -69,7 +69,7 @@ export function NewArticleScreen() { const submit = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setLoading(true); @@ -91,16 +91,16 @@ export function NewArticleScreen() { tags.push(['t', tag.replace('#', '')]); }); - const event = new NDKEvent(ndk); - event.content = content; - event.kind = NDKKind.Article; - event.tags = tags; - // publish - const publishedRelays = await event.publish(); + const publish = await ark.createEvent({ + content, + tags, + kind: NDKKind.Article, + publish: true, + }); - if (publishedRelays) { - toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`); + if (publish) { + toast.success(`Broadcasted to ${publish} relays successfully.`); // update state setLoading(false); diff --git a/src/app/new/file.tsx b/src/app/new/file.tsx index f67c618c..d4c8cddf 100644 --- a/src/app/new/file.tsx +++ b/src/app/new/file.tsx @@ -1,16 +1,15 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; import { message, open } from '@tauri-apps/plugin-dialog'; import { readBinaryFile } from '@tauri-apps/plugin-fs'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; export function NewFileScreen() { - const { ndk } = useNDK(); + const { ark } = useArk(); const navigate = useNavigate(); const [loading, setLoading] = useState(false); @@ -86,20 +85,21 @@ export function NewFileScreen() { const submit = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setIsPublish(true); - const event = new NDKEvent(ndk); - event.content = caption; - event.kind = 1063; - event.tags = metadata; + const publish = await ark.createEvent({ + kind: 1063, + tags: metadata, + content: caption, + publish: true, + }); - const publishedRelays = await event.publish(); - if (publishedRelays) { + if (publish) { + toast.success(`Broadcasted to ${publish} relays successfully.`); setMetadata(null); setIsPublish(false); - toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`); } } catch (e) { setIsPublish(false); diff --git a/src/app/new/post.tsx b/src/app/new/post.tsx index 68f36526..6a6def9a 100644 --- a/src/app/new/post.tsx +++ b/src/app/new/post.tsx @@ -13,7 +13,7 @@ import { toast } from 'sonner'; import { MediaUploader, MentionPopup } from '@app/new/components'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { CancelIcon, LoaderIcon } from '@shared/icons'; import { MentionNote } from '@shared/notes'; @@ -23,7 +23,7 @@ import { useSuggestion } from '@utils/hooks/useSuggestion'; import { useWidget } from '@utils/hooks/useWidget'; export function NewPostScreen() { - const { ndk } = useNDK(); + const { ark } = useArk(); const { addWidget } = useWidget(); const { suggestion } = useSuggestion(); @@ -68,7 +68,7 @@ export function NewPostScreen() { const submit = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setLoading(true); @@ -81,7 +81,7 @@ export function NewPostScreen() { ], }); - const event = new NDKEvent(ndk); + const event = new NDKEvent(); event.content = serializedContent; event.kind = NDKKind.Text; @@ -100,7 +100,10 @@ export function NewPostScreen() { } // publish event - const publishedRelays = await event.publish(); + const publishedRelays = await ark.createEvent({ + kind: NDKKind.Text, + content: serializedContent, + }); if (publishedRelays) { toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`); diff --git a/src/app/new/privkey.tsx b/src/app/new/privkey.tsx index 29ae67d6..771b9f6d 100644 --- a/src/app/new/privkey.tsx +++ b/src/app/new/privkey.tsx @@ -4,19 +4,13 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; export function NewPrivkeyScreen() { - const { db } = useStorage(); - const { ndk } = useNDK(); - - const [nsec, setNsec] = useState(''); + const { ark } = useArk(); const navigate = useNavigate(); - const save = async (content: string) => { - return await db.secureSave(db.account.pubkey, content); - }; + const [nsec, setNsec] = useState(''); const submit = async (isSave?: boolean) => { try { @@ -30,15 +24,15 @@ export function NewPrivkeyScreen() { const privkey = decoded.data; const pubkey = getPublicKey(privkey); - if (pubkey !== db.account.pubkey) + if (pubkey !== ark.account.pubkey) return toast.info( 'Your nsec is not match your current public key, please make sure you enter right nsec' ); const signer = new NDKPrivateKeySigner(privkey); - ndk.signer = signer; + ark.updateNostrSigner({ signer }); - if (isSave) await save(privkey); + if (isSave) await ark.createPrivkey(ark.account.pubkey, privkey); navigate(-1); } catch (e) { diff --git a/src/app/settings/components/contactCard.tsx b/src/app/settings/components/contactCard.tsx index 29d3da76..eeda0f86 100644 --- a/src/app/settings/components/contactCard.tsx +++ b/src/app/settings/components/contactCard.tsx @@ -1,22 +1,19 @@ import { useQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { EditIcon, LoaderIcon } from '@shared/icons'; import { compactNumber } from '@utils/number'; export function ContactCard() { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { status, data } = useQuery({ queryKey: ['contacts'], queryFn: async () => { - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const follows = await user.follows(); - return [...follows]; + const contacts = await ark.getUserContacts({}); + return contacts; }, refetchOnWindowFocus: false, }); diff --git a/src/app/settings/components/relayCard.tsx b/src/app/settings/components/relayCard.tsx index f5591c1c..b05d3cf7 100644 --- a/src/app/settings/components/relayCard.tsx +++ b/src/app/settings/components/relayCard.tsx @@ -1,23 +1,18 @@ import { useQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { EditIcon, LoaderIcon } from '@shared/icons'; import { compactNumber } from '@utils/number'; export function RelayCard() { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { status, data } = useQuery({ - queryKey: ['relays'], + queryKey: ['relays', ark.account.pubkey], queryFn: async () => { - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const relays = await user.relayList(); - - if (!relays) return Promise.reject(new Error("user's relay set not found")); + const relays = await ark.getUserRelays({}); return relays; }, refetchOnWindowFocus: false, diff --git a/src/app/settings/editContact.tsx b/src/app/settings/editContact.tsx index 30dcd118..cbc873e7 100644 --- a/src/app/settings/editContact.tsx +++ b/src/app/settings/editContact.tsx @@ -1,21 +1,16 @@ import { useQuery } from '@tanstack/react-query'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { User } from '@shared/user'; export function EditContactScreen() { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { status, data } = useQuery({ queryKey: ['contacts'], queryFn: async () => { - const user = ndk.getUser({ pubkey: db.account.pubkey }); - - const follows = await user.follows(); - return [...follows]; + return await ark.getUserContacts({}); }, refetchOnWindowFocus: false, }); @@ -29,10 +24,10 @@ export function EditContactScreen() { ) : ( data.map((item) => (
- +
)) )} diff --git a/src/app/settings/editProfile.tsx b/src/app/settings/editProfile.tsx index 3ba8ff21..03bc3633 100644 --- a/src/app/settings/editProfile.tsx +++ b/src/app/settings/editProfile.tsx @@ -1,29 +1,21 @@ -import { NDKEvent, NDKKind, NDKUserProfile } from '@nostr-dev-kit/ndk'; +import { NDKKind, NDKUserProfile } from '@nostr-dev-kit/ndk'; import { useQueryClient } from '@tanstack/react-query'; import { message } from '@tauri-apps/plugin-dialog'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { CheckCircleIcon, LoaderIcon, PlusIcon, UnverifiedIcon } from '@shared/icons'; -import { useNostr } from '@utils/hooks/useNostr'; - export function EditProfileScreen() { - const queryClient = useQueryClient(); - const navigate = useNavigate(); - const [loading, setLoading] = useState(false); const [picture, setPicture] = useState(''); const [banner, setBanner] = useState(''); const [nip05, setNIP05] = useState({ verified: true, text: '' }); - const { db } = useStorage(); - const { ndk } = useNDK(); - const { upload } = useNostr(); + const { ark } = useArk(); const { register, handleSubmit, @@ -32,7 +24,7 @@ export function EditProfileScreen() { formState: { isValid, errors }, } = useForm({ defaultValues: async () => { - const res: NDKUserProfile = queryClient.getQueryData(['user', db.account.pubkey]); + const res: NDKUserProfile = queryClient.getQueryData(['user', ark.account.pubkey]); if (res.image) { setPicture(res.image); } @@ -46,13 +38,16 @@ export function EditProfileScreen() { }, }); + const queryClient = useQueryClient(); + const navigate = useNavigate(); + const uploadAvatar = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setLoading(true); - const image = await upload(); + const image = await ark.upload({}); if (image) { setPicture(image); setLoading(false); @@ -67,7 +62,7 @@ export function EditProfileScreen() { try { setLoading(true); - const image = await upload(); + const image = await ark.upload({}); if (image) { setBanner(image); @@ -83,7 +78,7 @@ export function EditProfileScreen() { // start loading setLoading(true); - const content = { + let content = { ...data, username: data.name, display_name: data.name, @@ -91,15 +86,10 @@ export function EditProfileScreen() { image: data.picture, }; - const event = new NDKEvent(ndk); - event.kind = NDKKind.Metadata; - event.tags = []; - if (data.nip05) { - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const verify = await user.validateNip05(data.nip05); + const verify = ark.validateNIP05({ pubkey: ark.account.pubkey, nip05: data.nip05 }); if (verify) { - event.content = JSON.stringify({ ...content, nip05: data.nip05 }); + content = { ...content, nip05: data.nip05 }; } else { setNIP05((prev) => ({ ...prev, verified: false })); setError('nip05', { @@ -107,16 +97,19 @@ export function EditProfileScreen() { message: "Can't verify your Lume ID / NIP-05, please check again", }); } - } else { - event.content = JSON.stringify(content); } - const publishedRelays = await event.publish(); + const publish = await ark.createEvent({ + kind: NDKKind.Metadata, + tags: [], + content: JSON.stringify(content), + publish: true, + }); - if (publishedRelays) { + if (publish) { // invalid cache queryClient.invalidateQueries({ - queryKey: ['user', db.account.pubkey], + queryKey: ['user', ark.account.pubkey], }); // reset form reset(); diff --git a/src/libs/ark/ark.ts b/src/libs/ark/ark.ts new file mode 100644 index 00000000..cad18714 --- /dev/null +++ b/src/libs/ark/ark.ts @@ -0,0 +1,661 @@ +import NDK, { + NDKEvent, + NDKFilter, + NDKKind, + NDKNip46Signer, + NDKPrivateKeySigner, + NDKSubscriptionCacheUsage, + NDKTag, + NDKUser, + NostrEvent, +} from '@nostr-dev-kit/ndk'; +import { ndkAdapter } from '@nostr-fetch/adapter-ndk'; +import { invoke } from '@tauri-apps/api/primitives'; +import { open } from '@tauri-apps/plugin-dialog'; +import { readBinaryFile } from '@tauri-apps/plugin-fs'; +import { Platform } from '@tauri-apps/plugin-os'; +import Database from '@tauri-apps/plugin-sql'; +import { NostrEventExt, NostrFetcher, normalizeRelayUrlSet } from 'nostr-fetch'; +import { toast } from 'sonner'; + +import { NDKCacheAdapterTauri } from '@libs/ark'; + +import { + Account, + NDKCacheUser, + NDKCacheUserProfile, + NDKEventWithReplies, + NIP05, + Widget, +} from '@utils/types'; + +export class Ark { + #ndk: NDK; + #fetcher: NostrFetcher; + #storage: Database; + public account: Account | null; + public relays: string[] | null; + public readyToSign: boolean; + readonly platform: Platform | null; + readonly settings: { + autoupdate: boolean; + outbox: boolean; + media: boolean; + hashtag: boolean; + }; + + constructor({ storage }: { storage: Database }) { + this.#storage = storage; + this.#init(); + } + + async #keyring_save(key: string, value: string) { + return await invoke('secure_save', { key, value }); + } + + async #keyring_load(key: string) { + try { + const value: string = await invoke('secure_load', { key }); + if (!value) return null; + return value; + } catch { + return null; + } + } + + async #keyring_remove(key: string) { + return await invoke('secure_remove', { key }); + } + + async #initNostrSigner({ nsecbunker }: { nsecbunker?: boolean }) { + if (!this.account) { + this.readyToSign = false; + return null; + } + + try { + // NIP-46 Signer + if (nsecbunker) { + const localSignerPrivkey = await this.#keyring_load( + `${this.account.pubkey}-nsecbunker` + ); + + if (!localSignerPrivkey) { + this.readyToSign = false; + return null; + } + + const localSigner = new NDKPrivateKeySigner(localSignerPrivkey); + const bunker = new NDK({ + explicitRelayUrls: ['wss://relay.nsecbunker.com', 'wss://nostr.vulpem.com'], + }); + bunker.connect(); + + const remoteSigner = new NDKNip46Signer(bunker, this.account.id, localSigner); + await remoteSigner.blockUntilReady(); + + this.readyToSign = true; + return remoteSigner; + } + + // Privkey Signer + const userPrivkey = await this.#keyring_load(this.account.pubkey); + + if (!userPrivkey) { + this.readyToSign = false; + return null; + } + + this.readyToSign = true; + return new NDKPrivateKeySigner(userPrivkey); + } catch (e) { + console.log(e); + if (e === 'Token already redeemed') { + toast.info( + 'nsecbunker token already redeemed. You need to re-login with another token.' + ); + await this.logout(); + } + + this.readyToSign = false; + return null; + } + } + + async #init() { + const outboxSetting = await this.getSettingValue('outbox'); + const bunkerSetting = await this.getSettingValue('nsecbunker'); + + const bunker = !!parseInt(bunkerSetting); + const enableOutboxModel = !!parseInt(outboxSetting); + + const explicitRelayUrls = normalizeRelayUrlSet([ + 'wss://relay.damus.io', + 'wss://relay.nostr.band', + 'wss://nos.lol', + 'wss://nostr.mutinywallet.com', + ]); + + // #TODO: user should config outbox relays + const outboxRelayUrls = normalizeRelayUrlSet(['wss://purplepag.es']); + + // #TODO: user should config blacklist relays + const blacklistRelayUrls = normalizeRelayUrlSet(['wss://brb.io']); + + const cacheAdapter = new NDKCacheAdapterTauri(this.#storage); + const ndk = new NDK({ + cacheAdapter, + explicitRelayUrls, + outboxRelayUrls, + blacklistRelayUrls, + enableOutboxModel, + autoConnectUserRelays: true, + autoFetchUserMutelist: true, + // clientName: 'Lume', + // clientNip89: '', + }); + + // add signer if exist + const signer = await this.#initNostrSigner({ nsecbunker: bunker }); + if (signer) ndk.signer = signer; + + // connect + await ndk.connect(); + const fetcher = NostrFetcher.withCustomPool(ndkAdapter(ndk)); + + // update account's metadata + if (this.account) { + const user = ndk.getUser({ pubkey: this.account.pubkey }); + ndk.activeUser = user; + + const contacts = await user.follows(undefined /* outbox */); + this.account.contacts = [...contacts].map((user) => user.pubkey); + } + + this.#ndk = ndk; + this.#fetcher = fetcher; + } + + public updateNostrSigner({ signer }: { signer: NDKNip46Signer | NDKPrivateKeySigner }) { + this.#ndk.signer = signer; + return this.#ndk.signer; + } + + public async getAllCacheUsers() { + const results: Array = await this.#storage.select( + 'SELECT * FROM ndk_users ORDER BY createdAt DESC;' + ); + + if (!results.length) return []; + + const users: NDKCacheUserProfile[] = results.map((item) => ({ + pubkey: item.pubkey, + ...JSON.parse(item.profile as string), + })); + return users; + } + + public async checkAccount() { + const result: Array<{ total: string }> = await this.#storage.select( + 'SELECT COUNT(*) AS "total" FROM accounts WHERE is_active = "1" ORDER BY id DESC LIMIT 1;' + ); + return parseInt(result[0].total); + } + + public async getActiveAccount() { + const results: Array = await this.#storage.select( + 'SELECT * FROM accounts WHERE is_active = "1" ORDER BY id DESC LIMIT 1;' + ); + + if (results.length) { + this.account = results[0]; + this.account.contacts = []; + } else { + console.log('no active account, please create new account'); + return null; + } + } + + public async createAccount(npub: string, pubkey: string, privkey?: string) { + const existAccounts: Array = await this.#storage.select( + 'SELECT * FROM accounts WHERE pubkey = $1 ORDER BY id DESC LIMIT 1;', + [pubkey] + ); + + if (existAccounts.length) { + await this.#storage.execute( + "UPDATE accounts SET is_active = '1' WHERE pubkey = $1;", + [pubkey] + ); + } else { + await this.#storage.execute( + 'INSERT OR IGNORE INTO accounts (id, pubkey, is_active) VALUES ($1, $2, $3);', + [npub, pubkey, 1] + ); + + if (privkey) await this.#keyring_save(pubkey, privkey); + } + + return await this.getActiveAccount(); + } + + /** + * Save private key to OS secure storage + * @deprecated this method will be marked as private in the next update + */ + public async createPrivkey(name: string, privkey: string) { + await this.#keyring_save(name, privkey); + } + + public async updateAccount(column: string, value: string) { + const insert = await this.#storage.execute( + `UPDATE accounts SET ${column} = $1 WHERE id = $2;`, + [value, this.account.id] + ); + + if (insert) { + const account = await this.getActiveAccount(); + return account; + } + } + + public async getWidgets() { + const widgets: Array = await this.#storage.select( + 'SELECT * FROM widgets WHERE account_id = $1 ORDER BY created_at DESC;', + [this.account.id] + ); + return widgets; + } + + public async createWidget(kind: number, title: string, content: string | string[]) { + const insert = await this.#storage.execute( + 'INSERT INTO widgets (account_id, kind, title, content) VALUES ($1, $2, $3, $4);', + [this.account.id, kind, title, content] + ); + + if (insert) { + const widgets: Array = await this.#storage.select( + 'SELECT * FROM widgets ORDER BY id DESC LIMIT 1;' + ); + if (widgets.length < 1) console.error('get created widget failed'); + return widgets[0]; + } else { + console.error('create widget failed'); + } + } + + public async removeWidget(id: string) { + const res = await this.#storage.execute('DELETE FROM widgets WHERE id = $1;', [id]); + if (res) return id; + } + + public async createSetting(key: string, value: string | undefined) { + if (value) { + return await this.#storage.execute( + 'INSERT OR IGNORE INTO settings (key, value) VALUES ($1, $2);', + [key, value] + ); + } + + const currentSetting = await this.checkSettingValue(key); + + if (!currentSetting) + return await this.#storage.execute( + 'INSERT OR IGNORE INTO settings (key, value) VALUES ($1, $2);', + [key, value] + ); + + const currentValue = !!parseInt(currentSetting); + + return await this.#storage.execute('UPDATE settings SET value = $1 WHERE key = $2;', [ + +!currentValue, + key, + ]); + } + + public async getAllSettings() { + const results: { key: string; value: string }[] = await this.#storage.select( + 'SELECT * FROM settings ORDER BY id DESC;' + ); + if (results.length < 1) return null; + return results; + } + + public async checkSettingValue(key: string) { + const results: { key: string; value: string }[] = await this.#storage.select( + 'SELECT * FROM settings WHERE key = $1 ORDER BY id DESC LIMIT 1;', + [key] + ); + if (!results.length) return false; + return results[0].value; + } + + public async getSettingValue(key: string) { + const results: { key: string; value: string }[] = await this.#storage.select( + 'SELECT * FROM settings WHERE key = $1 ORDER BY id DESC LIMIT 1;', + [key] + ); + if (!results.length) return '0'; + return results[0].value; + } + + public async clearCache() { + await this.#storage.execute('DELETE FROM ndk_events;'); + await this.#storage.execute('DELETE FROM ndk_eventtags;'); + await this.#storage.execute('DELETE FROM ndk_users;'); + } + + public async logout() { + await this.#storage.execute("UPDATE accounts SET is_active = '0' WHERE id = $1;", [ + this.account.id, + ]); + await this.#keyring_remove(this.account.pubkey); + await this.#keyring_remove(`${this.account.pubkey}-nsecbunker`); + + this.account = null; + this.#ndk.signer = null; + } + + public subscribe({ + filter, + closeOnEose = false, + cb, + }: { + filter: NDKFilter; + closeOnEose: boolean; + cb: (event: NDKEvent) => void; + }) { + const sub = this.#ndk.subscribe(filter, { closeOnEose }); + sub.addListener('event', (event: NDKEvent) => cb(event)); + return sub; + } + + public createNDKEvent({ event }: { event: NostrEvent | NostrEventExt }) { + return new NDKEvent(this.#ndk, event); + } + + public async createEvent({ + kind, + tags, + content, + publish, + }: { + kind: NDKKind | number; + tags: NDKTag[]; + content?: string; + publish?: boolean; + }) { + try { + const event = new NDKEvent(this.#ndk); + if (content) event.content = content; + event.kind = kind; + event.tags = tags; + + if (!publish) { + const publish = await event.publish(); + if (!publish) throw new Error('cannot publish error'); + return publish.size; + } + + return event; + } catch (e) { + throw new Error(e); + } + } + + public async getUserProfile({ pubkey }: { pubkey: string }) { + try { + const user = this.#ndk.getUser({ pubkey }); + const profile = await user.fetchProfile({ + cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST, + }); + + if (!profile) return null; + return profile; + } catch (e) { + console.error(e); + return null; + } + } + + public async getUserContacts({ + pubkey = undefined, + outbox = undefined, + }: { + pubkey?: string; + outbox?: boolean; + }) { + try { + const user = this.#ndk.getUser({ pubkey: pubkey ? pubkey : this.account.pubkey }); + const contacts = [...(await user.follows(undefined, outbox))].map( + (user) => user.pubkey + ); + + this.account.contacts = contacts; + return contacts; + } catch (e) { + console.error(e); + return []; + } + } + + public async getUserRelays({ pubkey }: { pubkey?: string }) { + try { + const user = this.#ndk.getUser({ pubkey: pubkey ? pubkey : this.account.pubkey }); + return await user.relayList(); + } catch (e) { + console.error(e); + return null; + } + } + + public async createContact({ pubkey }: { pubkey: string }) { + const user = this.#ndk.getUser({ pubkey: this.account.pubkey }); + const contacts = await user.follows(); + return await user.follow(new NDKUser({ pubkey: pubkey }), contacts); + } + + public async deleteContact({ pubkey }: { pubkey: string }) { + const user = this.#ndk.getUser({ pubkey: this.account.pubkey }); + const contacts = await user.follows(); + return await user.follow(new NDKUser({ pubkey: pubkey }), contacts); + } + + public async getAllEvents({ filter }: { filter: NDKFilter }) { + const events = await this.#ndk.fetchEvents(filter); + if (!events) return []; + return [...events]; + } + + public async getEventById({ id }: { id: string }) { + const event = await this.#ndk.fetchEvent(id, { + cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST, + }); + + if (!event) return null; + return event; + } + + public getEventThread({ tags }: { tags: NDKTag[] }) { + let rootEventId: string = null; + let replyEventId: string = null; + + const events = tags.filter((el) => el[0] === 'e'); + + if (!events.length) return null; + + if (events.length === 1) + return { + rootEventId: events[0][1], + replyEventId: null, + }; + + if (events.length > 1) { + rootEventId = events.find((el) => el[3] === 'root')?.[1]; + replyEventId = events.find((el) => el[3] === 'reply')?.[1]; + + if (!rootEventId && !replyEventId) { + rootEventId = events[0][1]; + replyEventId = events[1][1]; + } + } + + return { + rootEventId, + replyEventId, + }; + } + + public async getThreads({ id, data }: { id: string; data?: NDKEventWithReplies[] }) { + let events = data || null; + + if (!data) { + const relayUrls = [...this.#ndk.pool.relays.values()].map((item) => item.url); + const rawEvents = (await this.#fetcher.fetchAllEvents( + relayUrls, + { + kinds: [NDKKind.Text], + '#e': [id], + }, + { since: 0 }, + { sort: true } + )) as unknown as NostrEvent[]; + events = rawEvents.map( + (event) => new NDKEvent(this.#ndk, event) + ) as NDKEvent[] as NDKEventWithReplies[]; + } + + if (events.length > 0) { + const replies = new Set(); + events.forEach((event) => { + const tags = event.tags.filter((el) => el[0] === 'e' && el[1] !== id); + if (tags.length > 0) { + tags.forEach((tag) => { + const rootIndex = events.findIndex((el) => el.id === tag[1]); + if (rootIndex !== -1) { + const rootEvent = events[rootIndex]; + if (rootEvent && rootEvent.replies) { + rootEvent.replies.push(event); + } else { + rootEvent.replies = [event]; + } + replies.add(event.id); + } + }); + } + }); + const cleanEvents = events.filter((ev) => !replies.has(ev.id)); + return cleanEvents; + } + + return events; + } + + public async getInfiniteEvents({ + filter, + limit, + pageParam = 0, + signal = undefined, + }: { + filter: NDKFilter; + limit: number; + pageParam?: number; + signal?: AbortSignal; + }) { + const rootIds = new Set(); + const dedupQueue = new Set(); + + const events = await this.#fetcher.fetchLatestEvents(this.relays, filter, limit, { + asOf: pageParam === 0 ? undefined : pageParam, + abortSignal: signal, + }); + + const ndkEvents = events.map((event) => { + return new NDKEvent(this.#ndk, event); + }); + + ndkEvents.forEach((event) => { + const tags = event.tags.filter((el) => el[0] === 'e'); + if (tags && tags.length > 0) { + const rootId = tags.filter((el) => el[3] === 'root')[1] ?? tags[0][1]; + if (rootIds.has(rootId)) return dedupQueue.add(event.id); + rootIds.add(rootId); + } + }); + + return ndkEvents + .filter((event) => !dedupQueue.has(event.id)) + .sort((a, b) => b.created_at - a.created_at); + } + + /** + * Upload media file to nostr.build + * @todo support multiple backends + */ + public async upload({ fileExts }: { fileExts?: string[] }) { + const defaultExts = ['png', 'jpeg', 'jpg', 'gif'].concat(fileExts); + + const selected = await open({ + multiple: false, + filters: [ + { + name: 'Image', + extensions: defaultExts, + }, + ], + }); + + if (!selected) return null; + + const file = await readBinaryFile(selected.path); + const blob = new Blob([file]); + + const data = new FormData(); + data.append('fileToUpload', blob); + data.append('submit', 'Upload Image'); + + const res = await fetch('https://nostr.build/api/v2/upload/files', { + method: 'POST', + body: data, + }); + + if (!res.ok) return null; + + const json = await res.json(); + const content = json.data[0]; + + return content.url as string; + } + + public async validateNIP05({ + pubkey, + nip05, + signal, + }: { + pubkey: string; + nip05: string; + signal?: AbortSignal; + }) { + const localPath = nip05.split('@')[0]; + const service = nip05.split('@')[1]; + const verifyURL = `https://${service}/.well-known/nostr.json?name=${localPath}`; + + const res = await fetch(verifyURL, { + method: 'GET', + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + signal, + }); + + if (!res.ok) throw new Error(`Failed to fetch NIP-05 service: ${nip05}`); + + const data: NIP05 = await res.json(); + if (data.names) { + if (data.names[localPath.toLowerCase()] !== pubkey) return false; + if (data.names[localPath] !== pubkey) return false; + return true; + } + return false; + } +} diff --git a/src/libs/ark/cache.ts b/src/libs/ark/cache.ts new file mode 100644 index 00000000..e563e7ba --- /dev/null +++ b/src/libs/ark/cache.ts @@ -0,0 +1,539 @@ +// inspired by NDK Cache Dexie +// source: https://github.com/nostr-dev-kit/ndk/tree/master/ndk-cache-dexie +import { + Hexpubkey, + NDKCacheAdapter, + NDKEvent, + NDKFilter, + NDKRelay, + NDKSubscription, + NDKUserProfile, + profileFromEvent, +} from '@nostr-dev-kit/ndk'; +import Database from '@tauri-apps/plugin-sql'; +import { LRUCache } from 'lru-cache'; +import { NostrEvent } from 'nostr-fetch'; +import { matchFilter } from 'nostr-tools'; + +import { NDKCacheEvent, NDKCacheEventTag, NDKCacheUser } from '@utils/types'; + +export class NDKCacheAdapterTauri implements NDKCacheAdapter { + #db: Database; + private dirtyProfiles: Set = new Set(); + public profiles?: LRUCache; + readonly locking: boolean; + + constructor(db: Database) { + this.#db = db; + this.locking = true; + + this.profiles = new LRUCache({ + max: 100000, + }); + + setInterval(() => { + this.dumpProfiles(); + }, 1000 * 10); + } + + async #getCacheUser(pubkey: string) { + const results: Array = await this.#db.select( + 'SELECT * FROM ndk_users WHERE pubkey = $1 ORDER BY pubkey DESC LIMIT 1;', + [pubkey] + ); + + if (!results.length) return null; + + if (typeof results[0].profile === 'string') + results[0].profile = JSON.parse(results[0].profile); + + return results[0]; + } + + async #getCacheEvent(id: string) { + const results: Array = await this.#db.select( + 'SELECT * FROM ndk_events WHERE id = $1 ORDER BY id DESC LIMIT 1;', + [id] + ); + + if (!results.length) return null; + return results[0]; + } + + async #getCacheEvents(ids: string[]) { + const idsArr = `'${ids.join("','")}'`; + + const results: Array = await this.#db.select( + `SELECT * FROM ndk_events WHERE id IN (${idsArr}) ORDER BY id;` + ); + + if (!results.length) return []; + return results; + } + + async #getCacheEventsByPubkey(pubkey: string) { + const results: Array = await this.#db.select( + 'SELECT * FROM ndk_events WHERE pubkey = $1 ORDER BY id;', + [pubkey] + ); + + if (!results.length) return []; + return results; + } + + async #getCacheEventsByKind(kind: number) { + const results: Array = await this.#db.select( + 'SELECT * FROM ndk_events WHERE kind = $1 ORDER BY id;', + [kind] + ); + + if (!results.length) return []; + return results; + } + + async #getCacheEventsByKindAndAuthor(kind: number, pubkey: string) { + const results: Array = await this.#db.select( + 'SELECT * FROM ndk_events WHERE kind = $1 AND pubkey = $2 ORDER BY id;', + [kind, pubkey] + ); + + if (!results.length) return []; + return results; + } + + async #getCacheEventTagsByTagValue(tagValue: string) { + const results: Array = await this.#db.select( + 'SELECT * FROM ndk_eventtags WHERE tagValue = $1 ORDER BY id;', + [tagValue] + ); + + if (!results.length) return []; + return results; + } + + async #setCacheEvent({ + id, + pubkey, + content, + kind, + createdAt, + relay, + event, + }: NDKCacheEvent) { + return await this.#db.execute( + 'INSERT OR IGNORE INTO ndk_events (id, pubkey, content, kind, createdAt, relay, event) VALUES ($1, $2, $3, $4, $5, $6, $7);', + [id, pubkey, content, kind, createdAt, relay, event] + ); + } + + async #setCacheEventTag({ id, eventId, tag, value, tagValue }: NDKCacheEventTag) { + return await this.#db.execute( + 'INSERT OR IGNORE INTO ndk_eventtags (id, eventId, tag, value, tagValue) VALUES ($1, $2, $3, $4, $5);', + [id, eventId, tag, value, tagValue] + ); + } + + async #setCacheProfiles(profiles: Array) { + return await Promise.all( + profiles.map( + async (profile) => + await this.#db.execute( + 'INSERT OR IGNORE INTO ndk_users (pubkey, profile, createdAt) VALUES ($1, $2, $3);', + [profile.pubkey, profile.profile, profile.createdAt] + ) + ) + ); + } + + public async query(subscription: NDKSubscription): Promise { + Promise.allSettled( + subscription.filters.map((filter) => this.processFilter(filter, subscription)) + ); + } + + public async fetchProfile(pubkey: Hexpubkey) { + if (!this.profiles) return null; + + let profile = this.profiles.get(pubkey); + + if (!profile) { + const user = await this.#getCacheUser(pubkey); + if (user) { + profile = user.profile as NDKUserProfile; + this.profiles.set(pubkey, profile); + } + } + + return profile; + } + + public saveProfile(pubkey: Hexpubkey, profile: NDKUserProfile) { + if (!this.profiles) return; + + this.profiles.set(pubkey, profile); + + this.dirtyProfiles.add(pubkey); + } + + private async processFilter( + filter: NDKFilter, + subscription: NDKSubscription + ): Promise { + const _filter = { ...filter }; + delete _filter.limit; + const filterKeys = Object.keys(_filter || {}).sort(); + + try { + (await this.byKindAndAuthor(filterKeys, filter, subscription)) || + (await this.byAuthors(filterKeys, filter, subscription)) || + (await this.byKinds(filterKeys, filter, subscription)) || + (await this.byIdsQuery(filterKeys, filter, subscription)) || + (await this.byNip33Query(filterKeys, filter, subscription)) || + (await this.byTagsAndOptionallyKinds(filterKeys, filter, subscription)); + } catch (error) { + console.error(error); + } + } + + public async setEvent( + event: NDKEvent, + _filter: NDKFilter, + relay?: NDKRelay + ): Promise { + if (event.kind === 0) { + if (!this.profiles) return; + + const profile: NDKUserProfile = profileFromEvent(event); + this.profiles.set(event.pubkey, profile); + } else { + let addEvent = true; + + if (event.isParamReplaceable()) { + const replaceableId = `${event.kind}:${event.pubkey}:${event.tagId()}`; + const existingEvent = await this.#getCacheEvent(replaceableId); + if ( + existingEvent && + event.created_at && + existingEvent.createdAt > event.created_at + ) { + addEvent = false; + } + } + + if (addEvent) { + this.#setCacheEvent({ + id: event.tagId(), + pubkey: event.pubkey, + content: event.content, + kind: event.kind!, + createdAt: event.created_at!, + relay: relay?.url, + event: JSON.stringify(event.rawEvent()), + }); + + // Don't cache contact lists as tags since it's expensive + // and there is no use case for it + if (event.kind !== 3) { + event.tags.forEach((tag) => { + if (tag[0].length !== 1) return; + + this.#setCacheEventTag({ + id: `${event.id}:${tag[0]}:${tag[1]}`, + eventId: event.id, + tag: tag[0], + value: tag[1], + tagValue: tag[0] + tag[1], + }); + }); + } + } + } + } + + /** + * Searches by authors + */ + private async byAuthors( + filterKeys: string[], + filter: NDKFilter, + subscription: NDKSubscription + ): Promise { + const f = ['authors']; + const hasAllKeys = + filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); + + let foundEvents = false; + + if (hasAllKeys && filter.authors) { + for (const pubkey of filter.authors) { + const events = await this.#getCacheEventsByPubkey(pubkey); + for (const event of events) { + let rawEvent: NostrEvent; + try { + rawEvent = JSON.parse(event.event); + } catch (e) { + console.log('failed to parse event', e); + continue; + } + + const ndkEvent = new NDKEvent(undefined, rawEvent); + const relay = event.relay ? new NDKRelay(event.relay) : undefined; + subscription.eventReceived(ndkEvent, relay, true); + foundEvents = true; + } + } + } + return foundEvents; + } + + /** + * Searches by kinds + */ + private async byKinds( + filterKeys: string[], + filter: NDKFilter, + subscription: NDKSubscription + ): Promise { + const f = ['kinds']; + const hasAllKeys = + filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); + + let foundEvents = false; + + if (hasAllKeys && filter.kinds) { + for (const kind of filter.kinds) { + const events = await this.#getCacheEventsByKind(kind); + for (const event of events) { + let rawEvent: NostrEvent; + try { + rawEvent = JSON.parse(event.event); + } catch (e) { + console.log('failed to parse event', e); + continue; + } + + const ndkEvent = new NDKEvent(undefined, rawEvent); + const relay = event.relay ? new NDKRelay(event.relay) : undefined; + subscription.eventReceived(ndkEvent, relay, true); + foundEvents = true; + } + } + } + return foundEvents; + } + + /** + * Searches by ids + */ + private async byIdsQuery( + filterKeys: string[], + filter: NDKFilter, + subscription: NDKSubscription + ): Promise { + const f = ['ids']; + const hasAllKeys = + filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); + + if (hasAllKeys && filter.ids) { + for (const id of filter.ids) { + const event = await this.#getCacheEvent(id); + if (!event) continue; + + let rawEvent: NostrEvent; + try { + rawEvent = JSON.parse(event.event); + } catch (e) { + console.log('failed to parse event', e); + continue; + } + + const ndkEvent = new NDKEvent(undefined, rawEvent); + const relay = event.relay ? new NDKRelay(event.relay) : undefined; + subscription.eventReceived(ndkEvent, relay, true); + } + + return true; + } + + return false; + } + + /** + * Searches by NIP-33 + */ + private async byNip33Query( + filterKeys: string[], + filter: NDKFilter, + subscription: NDKSubscription + ): Promise { + const f = ['#d', 'authors', 'kinds']; + const hasAllKeys = + filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); + + if (hasAllKeys && filter.kinds && filter.authors) { + for (const kind of filter.kinds) { + const replaceableKind = kind >= 30000 && kind < 40000; + + if (!replaceableKind) continue; + + for (const author of filter.authors) { + for (const dTag of filter['#d']) { + const replaceableId = `${kind}:${author}:${dTag}`; + const event = await this.#getCacheEvent(replaceableId); + if (!event) continue; + + let rawEvent: NostrEvent; + try { + rawEvent = JSON.parse(event.event); + } catch (e) { + console.log('failed to parse event', e); + continue; + } + + const ndkEvent = new NDKEvent(undefined, rawEvent); + const relay = event.relay ? new NDKRelay(event.relay) : undefined; + subscription.eventReceived(ndkEvent, relay, true); + } + } + } + return true; + } + return false; + } + + /** + * Searches by kind & author + */ + private async byKindAndAuthor( + filterKeys: string[], + filter: NDKFilter, + subscription: NDKSubscription + ): Promise { + const f = ['authors', 'kinds']; + const hasAllKeys = + filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); + let foundEvents = false; + + if (!hasAllKeys) return false; + + if (filter.kinds && filter.authors) { + for (const kind of filter.kinds) { + for (const author of filter.authors) { + const events = await this.#getCacheEventsByKindAndAuthor(kind, author); + + for (const event of events) { + let rawEvent: NostrEvent; + try { + rawEvent = JSON.parse(event.event); + } catch (e) { + console.log('failed to parse event', e); + continue; + } + + const ndkEvent = new NDKEvent(undefined, rawEvent); + const relay = event.relay ? new NDKRelay(event.relay) : undefined; + subscription.eventReceived(ndkEvent, relay, true); + foundEvents = true; + } + } + } + } + return foundEvents; + } + + /** + * Searches by tags and optionally filters by tags + */ + private async byTagsAndOptionallyKinds( + filterKeys: string[], + filter: NDKFilter, + subscription: NDKSubscription + ): Promise { + for (const filterKey of filterKeys) { + const isKind = filterKey === 'kinds'; + const isTag = filterKey.startsWith('#') && filterKey.length === 2; + + if (!isKind && !isTag) return false; + } + + const events = await this.filterByTag(filterKeys, filter); + const kinds = filter.kinds as number[]; + + for (const event of events) { + if (!kinds?.includes(event.kind!)) continue; + + subscription.eventReceived(event, undefined, true); + } + + return false; + } + + private async filterByTag( + filterKeys: string[], + filter: NDKFilter + ): Promise { + const retEvents: NDKEvent[] = []; + + for (const filterKey of filterKeys) { + if (filterKey.length !== 2) continue; + const tag = filterKey.slice(1); + // const values = filter[filterKey] as string[]; + const values: string[] = []; + for (const [key, value] of Object.entries(filter)) { + if (key === filterKey) values.push(value as string); + } + + for (const value of values) { + const eventTags = await this.#getCacheEventTagsByTagValue(tag + value); + if (!eventTags.length) continue; + + const eventIds = eventTags.map((t) => t.eventId); + + const events = await this.#getCacheEvents(eventIds); + for (const event of events) { + let rawEvent; + try { + rawEvent = JSON.parse(event.event); + + // Make sure all passed filters match the event + if (!matchFilter(filter, rawEvent)) continue; + } catch (e) { + console.log('failed to parse event', e); + continue; + } + + const ndkEvent = new NDKEvent(undefined, rawEvent); + const relay = event.relay ? new NDKRelay(event.relay) : undefined; + ndkEvent.relay = relay; + retEvents.push(ndkEvent); + } + } + } + + return retEvents; + } + + private async dumpProfiles(): Promise { + const profiles = []; + + if (!this.profiles) return; + + for (const pubkey of this.dirtyProfiles) { + const profile = this.profiles.get(pubkey); + + if (!profile) continue; + + profiles.push({ + pubkey, + profile: JSON.stringify(profile), + createdAt: Date.now(), + }); + } + + if (profiles.length) { + await this.#setCacheProfiles(profiles); + } + + this.dirtyProfiles.clear(); + } +} diff --git a/src/libs/ark/index.ts b/src/libs/ark/index.ts new file mode 100644 index 00000000..39970415 --- /dev/null +++ b/src/libs/ark/index.ts @@ -0,0 +1,3 @@ +export * from './ark'; +export * from './cache'; +export * from './provider'; diff --git a/src/libs/ark/provider.tsx b/src/libs/ark/provider.tsx new file mode 100644 index 00000000..6f1290fa --- /dev/null +++ b/src/libs/ark/provider.tsx @@ -0,0 +1,123 @@ +import { ask } from '@tauri-apps/plugin-dialog'; +import { relaunch } from '@tauri-apps/plugin-process'; +import Database from '@tauri-apps/plugin-sql'; +import { check } from '@tauri-apps/plugin-updater'; +import Markdown from 'markdown-to-jsx'; +import { PropsWithChildren, createContext, useContext, useEffect, useState } from 'react'; + +import { Ark } from '@libs/ark'; + +import { LoaderIcon } from '@shared/icons'; + +import { QUOTES } from '@utils/constants'; + +interface ArkContext { + ark: Ark; +} + +const ArkContext = createContext({ + ark: undefined, +}); + +const ArkProvider = ({ children }: PropsWithChildren) => { + const [ark, setArk] = useState(undefined); + const [isNewVersion, setIsNewVersion] = useState(false); + + async function initArk() { + try { + const sqlite = await Database.load('sqlite:lume_v2.db'); + const _ark = new Ark({ storage: sqlite }); + + if (!_ark.account) await _ark.getActiveAccount(); + + const settings = await _ark.getAllSettings(); + let autoUpdater = false; + + if (settings) { + settings.forEach((item) => { + if (item.key === 'outbox') _ark.settings.outbox = !!parseInt(item.value); + + if (item.key === 'media') _ark.settings.media = !!parseInt(item.value); + + if (item.key === 'hashtag') _ark.settings.hashtag = !!parseInt(item.value); + + if (item.key === 'autoupdate') { + if (parseInt(item.value)) autoUpdater = true; + } + }); + } + + if (autoUpdater) { + // check update + const update = await check(); + // install new version + if (update) { + setIsNewVersion(true); + + await update.downloadAndInstall(); + await relaunch(); + } + } + + setArk(_ark); + } catch (e) { + console.error(e); + const yes = await ask(`${e}. Click "Yes" to relaunch app`, { + title: 'Lume', + type: 'error', + okLabel: 'Yes', + }); + if (yes) relaunch(); + } + } + + useEffect(() => { + if (!ark && !isNewVersion) initArk(); + }, []); + + if (!ark) { + return ( +
+
+
TIP:
+ + {QUOTES[Math.floor(Math.random() * QUOTES.length)]} + +
+
+ +

+ {isNewVersion ? 'Found a new version, updating...' : 'Starting...'} +

+
+
+ ); + } + + return {children}; +}; + +const useArk = () => { + const context = useContext(ArkContext); + if (context === undefined) { + throw new Error('Please import Ark Provider to use useArk() hook'); + } + return context; +}; + +export { ArkProvider, useArk }; diff --git a/src/main.jsx b/src/main.jsx index df781e5c..1dc6099d 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -3,8 +3,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { createRoot } from 'react-dom/client'; import { Toaster } from 'sonner'; -import { NDKProvider } from '@libs/ndk/provider'; -import { StorageProvider } from '@libs/storage/provider'; +import { ArkProvider } from '@libs/ark/provider'; import App from './app'; @@ -23,10 +22,8 @@ root.render( - - - - - + + + ); diff --git a/src/shared/accounts/logout.tsx b/src/shared/accounts/logout.tsx index 259ee1f9..9edd3d2e 100644 --- a/src/shared/accounts/logout.tsx +++ b/src/shared/accounts/logout.tsx @@ -3,26 +3,18 @@ import { useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; export function Logout() { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const navigate = useNavigate(); const queryClient = useQueryClient(); const logout = async () => { try { - ndk.signer = null; - - // remove private key - await db.secureRemove(db.account.pubkey); - await db.secureRemove(`${db.account.pubkey}-nsecbunker`); - // logout - await db.accountLogout(); + await ark.logout(); // clear cache queryClient.clear(); diff --git a/src/shared/notes/repost.tsx b/src/shared/notes/repost.tsx index 8317732c..dbab01ae 100644 --- a/src/shared/notes/repost.tsx +++ b/src/shared/notes/repost.tsx @@ -2,7 +2,7 @@ import { NDKEvent, NDKKind, NostrEvent } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; import { memo } from 'react'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { MemoizedArticleKind, @@ -14,24 +14,25 @@ import { import { User } from '@shared/user'; export function Repost({ event }: { event: NDKEvent }) { - const { ndk } = useNDK(); + const { ark } = useArk(); const { status, data: repostEvent } = useQuery({ queryKey: ['repost', event.id], queryFn: async () => { try { + let event: NDKEvent = undefined; + if (event.content.length > 50) { const embed = JSON.parse(event.content) as NostrEvent; - const embedEvent = new NDKEvent(ndk, embed); - return embedEvent; + event = ark.createNDKEvent({ event: embed }); } const id = event.tags.find((el) => el[0] === 'e')[1]; if (!id) throw new Error('Failed to get repost event id'); - const ndkEvent = await ndk.fetchEvent(id); - if (!ndkEvent) return Promise.reject(new Error('Failed to get repost event')); + event = await ark.getEventById({ id }); - return ndkEvent; + if (!event) return Promise.reject(new Error('Failed to get repost event')); + return event; } catch { throw new Error('Failed to get repost event'); } diff --git a/src/shared/widgets/other/liveUpdater.tsx b/src/shared/widgets/other/liveUpdater.tsx index 60fe1250..5fa72578 100644 --- a/src/shared/widgets/other/liveUpdater.tsx +++ b/src/shared/widgets/other/liveUpdater.tsx @@ -1,16 +1,13 @@ -import { NDKEvent, NDKFilter, NDKKind, NDKSubscription } from '@nostr-dev-kit/ndk'; +import { NDKEvent, NDKKind, NDKSubscription } from '@nostr-dev-kit/ndk'; import { QueryStatus, useQueryClient } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ChevronUpIcon } from '@shared/icons'; export function LiveUpdater({ status }: { status: QueryStatus }) { - const { db } = useStorage(); - const { ndk } = useNDK(); - + const { ark } = useArk(); const [events, setEvents] = useState([]); const queryClient = useQueryClient(); @@ -30,19 +27,16 @@ export function LiveUpdater({ status }: { status: QueryStatus }) { useEffect(() => { let sub: NDKSubscription = undefined; - if (status === 'success' && db.account && db.account?.follows?.length > 0) { - queryClient.fetchQuery({ queryKey: ['notification'] }); - - const filter: NDKFilter = { - kinds: [NDKKind.Text, NDKKind.Repost], - authors: db.account.contacts, - since: Math.floor(Date.now() / 1000), - }; - - sub = ndk.subscribe(filter, { closeOnEose: false, groupable: false }); - sub.addListener('event', (event: NDKEvent) => - setEvents((prev) => [...prev, event]) - ); + if (status === 'success' && ark.account && ark.account?.contacts?.length > 0) { + sub = ark.subscribe({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + authors: ark.account.contacts, + since: Math.floor(Date.now() / 1000), + }, + closeOnEose: false, + cb: (event: NDKEvent) => setEvents((prev) => [...prev, event]), + }); } return () => { diff --git a/src/shared/widgets/other/nostrBandUserProfile.tsx b/src/shared/widgets/other/nostrBandUserProfile.tsx index a9395f64..37b30f5c 100644 --- a/src/shared/widgets/other/nostrBandUserProfile.tsx +++ b/src/shared/widgets/other/nostrBandUserProfile.tsx @@ -1,10 +1,8 @@ -import { NDKUser } from '@nostr-dev-kit/ndk'; import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { FollowIcon } from '@shared/icons'; @@ -16,9 +14,7 @@ export interface Profile { } export function NostrBandUserProfile({ data }: { data: Profile }) { - const { db } = useStorage(); - const { ndk } = useNDK(); - + const { ark } = useArk(); const [followed, setFollowed] = useState(false); const navigate = useNavigate(); @@ -26,12 +22,10 @@ export function NostrBandUserProfile({ data }: { data: Profile }) { const follow = async (pubkey: string) => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setFollowed(true); - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const contacts = await user.follows(); - const add = await user.follow(new NDKUser({ pubkey: pubkey }), contacts); + const add = ark.createContact({ pubkey }); if (!add) { toast.success('You already follow this user'); @@ -44,7 +38,7 @@ export function NostrBandUserProfile({ data }: { data: Profile }) { }; useEffect(() => { - if (db.account.contacts.includes(data.pubkey)) { + if (ark.account.contacts.includes(data.pubkey)) { setFollowed(true); } }, []); diff --git a/src/utils/hooks/useEvent.ts b/src/utils/hooks/useEvent.ts index 7617aba3..36309c21 100644 --- a/src/utils/hooks/useEvent.ts +++ b/src/utils/hooks/useEvent.ts @@ -1,45 +1,41 @@ -import { NDKEvent, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk'; +import { NDKEvent, NostrEvent } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; import { nip19 } from 'nostr-tools'; import { AddressPointer } from 'nostr-tools/lib/types/nip19'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; export function useEvent(id: undefined | string, embed?: undefined | string) { - const { ndk } = useNDK(); + const { ark } = useArk(); const { status, isFetching, isError, data } = useQuery({ queryKey: ['event', id], queryFn: async () => { + let event: NDKEvent = undefined; + const naddr = id.startsWith('naddr') ? (nip19.decode(id).data as AddressPointer) : null; // return event refer from naddr if (naddr) { - const rEvents = await ndk.fetchEvents({ - kinds: [naddr.kind], - '#d': [naddr.identifier], - authors: [naddr.pubkey], + const events = await ark.getAllEvents({ + filter: { + kinds: [naddr.kind], + '#d': [naddr.identifier], + authors: [naddr.pubkey], + }, }); - - const rEvent = [...rEvents].slice(-1)[0]; - - if (!rEvent) throw new Error('event not found'); - return rEvent; + event = events.slice(-1)[0]; } // return embed event (nostr.band api) if (embed) { const embedEvent: NostrEvent = JSON.parse(embed); - const ndkEvent = new NDKEvent(ndk, embedEvent); - - return ndkEvent; + event = ark.createNDKEvent({ event: embedEvent }); } // get event from relay - const event = await ndk.fetchEvent(id, { - cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST, - }); + event = await ark.getEventById({ id }); if (!event) throw new Error(`Cannot get event with ${id}, will be retry after 10 seconds`); diff --git a/src/utils/hooks/useProfile.ts b/src/utils/hooks/useProfile.ts index c9b10ac9..c8e93758 100644 --- a/src/utils/hooks/useProfile.ts +++ b/src/utils/hooks/useProfile.ts @@ -1,11 +1,11 @@ -import { NDKSubscriptionCacheUsage, NDKUserProfile } from '@nostr-dev-kit/ndk'; +import { NDKUserProfile } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; import { nip19 } from 'nostr-tools'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; export function useProfile(pubkey: string, embed?: string) { - const { ndk } = useNDK(); + const { ark } = useArk(); const { isLoading, isError, @@ -29,10 +29,7 @@ export function useProfile(pubkey: string, embed?: string) { if (decoded.type === 'npub') hexstring = decoded.data; } - const user = ndk.getUser({ pubkey: hexstring }); - const profile = await user.fetchProfile({ - cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST, - }); + const profile = await ark.getUserProfile({ pubkey: hexstring }); if (!profile) throw new Error( diff --git a/src/utils/hooks/useRelay.ts b/src/utils/hooks/useRelay.ts index e95e1755..d60d0750 100644 --- a/src/utils/hooks/useRelay.ts +++ b/src/utils/hooks/useRelay.ts @@ -1,46 +1,44 @@ -import { NDKEvent, NDKKind, NDKRelayUrl, NDKTag } from '@nostr-dev-kit/ndk'; +import { NDKKind, NDKRelayUrl, NDKTag } from '@nostr-dev-kit/ndk'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; export function useRelay() { - const { db } = useStorage(); - const { ndk } = useNDK(); - + const { ark } = useArk(); const queryClient = useQueryClient(); const connectRelay = useMutation({ mutationFn: async (relay: NDKRelayUrl, purpose?: 'read' | 'write' | undefined) => { // Cancel any outgoing refetches - await queryClient.cancelQueries({ queryKey: ['relays', db.account.pubkey] }); + await queryClient.cancelQueries({ queryKey: ['relays', ark.account.pubkey] }); // Snapshot the previous value const prevRelays: NDKTag[] = queryClient.getQueryData([ 'relays', - db.account.pubkey, + ark.account.pubkey, ]); // create new relay list if not exist if (!prevRelays) { - const newListEvent = new NDKEvent(ndk); - newListEvent.kind = NDKKind.RelayList; - newListEvent.tags = [['r', relay, purpose ?? '']]; - await newListEvent.publish(); + await ark.createEvent({ + kind: NDKKind.RelayList, + tags: [['r', relay, purpose ?? '']], + publish: true, + }); } // add relay to exist list const index = prevRelays.findIndex((el) => el[1] === relay); if (index > -1) return; - const event = new NDKEvent(ndk); - event.kind = NDKKind.RelayList; - event.tags = [...prevRelays, ['r', relay, purpose ?? '']]; - - await event.publish(); + await ark.createEvent({ + kind: NDKKind.RelayList, + tags: [...prevRelays, ['r', relay, purpose ?? '']], + publish: true, + }); // Optimistically update to the new value - queryClient.setQueryData(['relays', db.account.pubkey], (prev: NDKTag[]) => [ + queryClient.setQueryData(['relays', ark.account.pubkey], (prev: NDKTag[]) => [ ...prev, ['r', relay, purpose ?? ''], ]); @@ -49,19 +47,19 @@ export function useRelay() { return { prevRelays }; }, onSettled: () => { - queryClient.invalidateQueries({ queryKey: ['relays', db.account.pubkey] }); + queryClient.invalidateQueries({ queryKey: ['relays', ark.account.pubkey] }); }, }); const removeRelay = useMutation({ mutationFn: async (relay: NDKRelayUrl) => { // Cancel any outgoing refetches - await queryClient.cancelQueries({ queryKey: ['relays', db.account.pubkey] }); + await queryClient.cancelQueries({ queryKey: ['relays', ark.account.pubkey] }); // Snapshot the previous value const prevRelays: NDKTag[] = queryClient.getQueryData([ 'relays', - db.account.pubkey, + ark.account.pubkey, ]); if (!prevRelays) return; @@ -69,19 +67,20 @@ export function useRelay() { const index = prevRelays.findIndex((el) => el[1] === relay); if (index > -1) prevRelays.splice(index, 1); - const event = new NDKEvent(ndk); - event.kind = NDKKind.RelayList; - event.tags = prevRelays; - await event.publish(); + await ark.createEvent({ + kind: NDKKind.RelayList, + tags: prevRelays, + publish: true, + }); // Optimistically update to the new value - queryClient.setQueryData(['relays', db.account.pubkey], prevRelays); + queryClient.setQueryData(['relays', ark.account.pubkey], prevRelays); // Return a context object with the snapshotted value return { prevRelays }; }, onSettled: () => { - queryClient.invalidateQueries({ queryKey: ['relays', db.account.pubkey] }); + queryClient.invalidateQueries({ queryKey: ['relays', ark.account.pubkey] }); }, }); diff --git a/src/utils/hooks/useRichContent.tsx b/src/utils/hooks/useRichContent.tsx index 4edeefd1..f407aae6 100644 --- a/src/utils/hooks/useRichContent.tsx +++ b/src/utils/hooks/useRichContent.tsx @@ -4,7 +4,7 @@ import { ReactNode } from 'react'; import { Link } from 'react-router-dom'; import reactStringReplace from 'react-string-replace'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { Hashtag, @@ -46,7 +46,7 @@ const VIDEOS = [ ]; export function useRichContent(content: string, textmode: boolean = false) { - const { db } = useStorage(); + const { ark } = useArk(); let parsedContent: string | ReactNode[] = content.replace(/\n+/g, '\n'); let linkPreview: string; @@ -58,7 +58,7 @@ export function useRichContent(content: string, textmode: boolean = false) { const words = text.split(/( |\n)/); if (!textmode) { - if (db.settings.media) { + if (ark.settings.media) { images = words.filter((word) => IMAGES.some((el) => word.endsWith(el))); videos = words.filter((word) => VIDEOS.some((el) => word.endsWith(el))); } @@ -90,7 +90,7 @@ export function useRichContent(content: string, textmode: boolean = false) { if (hashtags.length) { hashtags.forEach((hashtag) => { parsedContent = reactStringReplace(parsedContent, hashtag, (match, i) => { - if (db.settings.hashtag) return ; + if (ark.settings.hashtag) return ; return null; }); }); diff --git a/src/utils/types.d.ts b/src/utils/types.d.ts index 2b9874ac..cccaf9af 100644 --- a/src/utils/types.d.ts +++ b/src/utils/types.d.ts @@ -160,3 +160,9 @@ export interface NIP11 { payments_url: string; icon: string[]; } + +export interface NIP05 { + names: { + [key: string]: string; + }; +} From 7507cd9ba149971296aaad068126cddf58584632 Mon Sep 17 00:00:00 2001 From: reya Date: Thu, 7 Dec 2023 18:09:00 +0700 Subject: [PATCH 2/8] wip: migrate to ark --- src/app.tsx | 6 +- src/app/auth/onboarding.tsx | 14 +- src/app/chats/components/mediaUploader.tsx | 10 +- src/app/error.tsx | 10 +- src/app/home/index.tsx | 6 +- src/app/new/components/mediaUploader.tsx | 10 +- src/app/new/components/mentionPopup.tsx | 8 +- src/app/notes/text.tsx | 7 +- src/app/nwc/components/form.tsx | 6 +- src/app/nwc/index.tsx | 8 +- src/app/relays/components/relayList.tsx | 7 +- src/app/relays/components/userRelayList.tsx | 21 +- src/app/settings/advanced.tsx | 6 +- src/app/settings/backup.tsx | 8 +- src/app/settings/components/postCard.tsx | 12 +- src/app/settings/components/profileCard.tsx | 14 +- src/app/settings/components/zapCard.tsx | 10 +- src/app/settings/general.tsx | 20 +- src/app/users/components/profile.tsx | 34 +-- src/libs/ark/ark.ts | 69 ++++- src/shared/avatarUploader.tsx | 8 +- src/shared/bannerUploader.tsx | 8 +- src/shared/notes/notify.tsx | 7 +- src/shared/notes/replies/list.tsx | 22 +- src/shared/notes/text.tsx | 7 +- src/shared/titleBar.tsx | 10 +- src/shared/widgets/notification.tsx | 6 +- src/shared/widgets/thread.tsx | 7 +- src/utils/hooks/useNostr.ts | 299 -------------------- 29 files changed, 206 insertions(+), 454 deletions(-) delete mode 100644 src/utils/hooks/useNostr.ts diff --git a/src/app.tsx b/src/app.tsx index 3f75e465..f5cbc78b 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -7,7 +7,7 @@ import { ChatsScreen } from '@app/chats'; import { ErrorScreen } from '@app/error'; import { ExploreScreen } from '@app/explore'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { AppLayout } from '@shared/layouts/app'; @@ -19,12 +19,12 @@ import { SettingsLayout } from '@shared/layouts/settings'; import './app.css'; export default function App() { - const { db } = useStorage(); + const { ark } = useArk(); const accountLoader = async () => { try { // redirect to welcome screen if none user exist - const totalAccount = await db.checkAccount(); + const totalAccount = await ark.checkAccount(); if (totalAccount === 0) return redirect('/auth/welcome'); return null; diff --git a/src/app/auth/onboarding.tsx b/src/app/auth/onboarding.tsx index fdf3f462..d4e065d4 100644 --- a/src/app/auth/onboarding.tsx +++ b/src/app/auth/onboarding.tsx @@ -3,12 +3,12 @@ import { isPermissionGranted, requestPermission } from '@tauri-apps/plugin-notif import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { InfoIcon } from '@shared/icons'; export function OnboardingScreen() { - const { db } = useStorage(); + const { ark } = useArk(); const navigate = useNavigate(); const [settings, setSettings] = useState({ @@ -18,19 +18,19 @@ export function OnboardingScreen() { }); const next = () => { - if (!db.account.contacts.length) return navigate('/auth/follow'); + if (!ark.account.contacts.length) return navigate('/auth/follow'); return navigate('/auth/finish'); }; const toggleOutbox = async () => { - await db.createSetting('outbox', String(+!settings.outbox)); + await ark.createSetting('outbox', String(+!settings.outbox)); // update state setSettings((prev) => ({ ...prev, outbox: !settings.outbox })); }; const toggleAutoupdate = async () => { - await db.createSetting('autoupdate', String(+!settings.autoupdate)); - db.settings.autoupdate = !settings.autoupdate; + await ark.createSetting('autoupdate', String(+!settings.autoupdate)); + ark.settings.autoupdate = !settings.autoupdate; // update state setSettings((prev) => ({ ...prev, autoupdate: !settings.autoupdate })); }; @@ -46,7 +46,7 @@ export function OnboardingScreen() { const permissionGranted = await isPermissionGranted(); setSettings((prev) => ({ ...prev, notification: permissionGranted })); - const data = await db.getAllSettings(); + const data = await ark.getAllSettings(); if (!data) return; data.forEach((item) => { diff --git a/src/app/chats/components/mediaUploader.tsx b/src/app/chats/components/mediaUploader.tsx index 165b9470..adec461f 100644 --- a/src/app/chats/components/mediaUploader.tsx +++ b/src/app/chats/components/mediaUploader.tsx @@ -1,22 +1,24 @@ import * as Tooltip from '@radix-ui/react-tooltip'; import { Dispatch, SetStateAction, useState } from 'react'; -import { LoaderIcon, MediaIcon } from '@shared/icons'; +import { useArk } from '@libs/ark'; -import { useNostr } from '@utils/hooks/useNostr'; +import { LoaderIcon, MediaIcon } from '@shared/icons'; export function MediaUploader({ setState, }: { setState: Dispatch>; }) { - const { upload } = useNostr(); + const { ark } = useArk(); const [loading, setLoading] = useState(false); const uploadMedia = async () => { setLoading(true); - const image = await upload(['mp4', 'mp3', 'webm', 'mkv', 'avi', 'mov']); + const image = await ark.upload({ + fileExts: ['mp4', 'mp3', 'webm', 'mkv', 'avi', 'mov'], + }); if (image) { setState((prev: string) => `${prev}\n${image}`); diff --git a/src/app/error.tsx b/src/app/error.tsx index 6028c818..0a8f6fc2 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -4,7 +4,7 @@ import { writeTextFile } from '@tauri-apps/plugin-fs'; import { relaunch } from '@tauri-apps/plugin-process'; import { useRouteError } from 'react-router-dom'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; interface RouteError { statusText: string; @@ -12,7 +12,7 @@ interface RouteError { } export function ErrorScreen() { - const { db } = useStorage(); + const { ark } = useArk(); const error = useRouteError() as RouteError; const restart = async () => { @@ -26,18 +26,18 @@ export function ErrorScreen() { const filePath = await save({ defaultPath: downloadPath + '/' + fileName, }); - const nsec = await db.secureLoad(db.account.pubkey); + const nsec = await ark.loadPrivkey(ark.account.pubkey); if (filePath) { if (nsec) { await writeTextFile( filePath, - `Nostr account, generated by Lume (lume.nu)\nPublic key: ${db.account.id}\nPrivate key: ${nsec}` + `Nostr account, generated by Lume (lume.nu)\nPublic key: ${ark.account.id}\nPrivate key: ${nsec}` ); } else { await writeTextFile( filePath, - `Nostr account, generated by Lume (lume.nu)\nPublic key: ${db.account.id}` + `Nostr account, generated by Lume (lume.nu)\nPublic key: ${ark.account.id}` ); } } // else { user cancel action } diff --git a/src/app/home/index.tsx b/src/app/home/index.tsx index fcca85ed..08072a9f 100644 --- a/src/app/home/index.tsx +++ b/src/app/home/index.tsx @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query'; import { useCallback, useRef, useState } from 'react'; import { VList, VListHandle } from 'virtua'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { @@ -28,11 +28,11 @@ export function HomeScreen() { const ref = useRef(null); const [selectedIndex, setSelectedIndex] = useState(-1); - const { db } = useStorage(); + const { ark } = useArk(); const { status, data } = useQuery({ queryKey: ['widgets'], queryFn: async () => { - const dbWidgets = await db.getWidgets(); + const dbWidgets = await ark.getWidgets(); const defaultWidgets = [ { id: '9999', diff --git a/src/app/new/components/mediaUploader.tsx b/src/app/new/components/mediaUploader.tsx index 89cdba11..6c0319c7 100644 --- a/src/app/new/components/mediaUploader.tsx +++ b/src/app/new/components/mediaUploader.tsx @@ -2,12 +2,12 @@ import { message } from '@tauri-apps/plugin-dialog'; import { Editor } from '@tiptap/react'; import { useState } from 'react'; +import { useArk } from '@libs/ark'; + import { MediaIcon } from '@shared/icons'; -import { useNostr } from '@utils/hooks/useNostr'; - export function MediaUploader({ editor }: { editor: Editor }) { - const { upload } = useNostr(); + const { ark } = useArk(); const [loading, setLoading] = useState(false); const uploadToNostrBuild = async () => { @@ -15,7 +15,9 @@ export function MediaUploader({ editor }: { editor: Editor }) { // start loading setLoading(true); - const image = await upload(['mp4', 'mp3', 'webm', 'mkv', 'avi', 'mov']); + const image = await ark.upload({ + fileExts: ['mp4', 'mp3', 'webm', 'mkv', 'avi', 'mov'], + }); if (image) { editor.commands.setImage({ src: image }); diff --git a/src/app/new/components/mentionPopup.tsx b/src/app/new/components/mentionPopup.tsx index 26c07eb0..c574cdb4 100644 --- a/src/app/new/components/mentionPopup.tsx +++ b/src/app/new/components/mentionPopup.tsx @@ -4,12 +4,12 @@ import { nip19 } from 'nostr-tools'; import { MentionPopupItem } from '@app/new/components'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { MentionIcon } from '@shared/icons'; export function MentionPopup({ editor }: { editor: Editor }) { - const { db } = useStorage(); + const { ark } = useArk(); const insertMention = (pubkey: string) => { editor.commands.insertContent(`nostr:${nip19.npubEncode(pubkey)}`); @@ -32,8 +32,8 @@ export function MentionPopup({ editor }: { editor: Editor }) { className="h-full max-h-[200px] w-[250px] overflow-hidden overflow-y-auto rounded-lg border border-neutral-200 bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:bg-neutral-900" >
- {db.account.contacts.length > 0 ? ( - db.account.contacts.map((item) => ( + {ark.account.contacts.length ? ( + ark.account.contacts.map((item) => ( diff --git a/src/app/notes/text.tsx b/src/app/notes/text.tsx index 9e723251..a1c7e744 100644 --- a/src/app/notes/text.tsx +++ b/src/app/notes/text.tsx @@ -6,6 +6,8 @@ import { useRef, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { toast } from 'sonner'; +import { useArk } from '@libs/ark'; + import { ArrowLeftIcon, CheckCircleIcon, ReplyIcon, ShareIcon } from '@shared/icons'; import { ChildNote, @@ -18,15 +20,14 @@ import { ReplyList } from '@shared/notes/replies/list'; import { User } from '@shared/user'; import { useEvent } from '@utils/hooks/useEvent'; -import { useNostr } from '@utils/hooks/useNostr'; export function TextNoteScreen() { const navigate = useNavigate(); const replyRef = useRef(null); const { id } = useParams(); + const { ark } = useArk(); const { status, data } = useEvent(id); - const { getEventThread } = useNostr(); const [isCopy, setIsCopy] = useState(false); @@ -50,7 +51,7 @@ export function TextNoteScreen() { }; const renderKind = (event: NDKEvent) => { - const thread = getEventThread(event.tags); + const thread = ark.getEventThread({ tags: event.tags }); switch (event.kind) { case NDKKind.Text: return ( diff --git a/src/app/nwc/components/form.tsx b/src/app/nwc/components/form.tsx index 41a38faa..807968be 100644 --- a/src/app/nwc/components/form.tsx +++ b/src/app/nwc/components/form.tsx @@ -1,12 +1,12 @@ import { useState } from 'react'; import { toast } from 'sonner'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; export function NWCForm({ setWalletConnectURL }) { - const { db } = useStorage(); + const { ark } = useArk(); const [uri, setUri] = useState(''); const [loading, setLoading] = useState(false); @@ -27,7 +27,7 @@ export function NWCForm({ setWalletConnectURL }) { const params = new URLSearchParams(uriObj.search); if (params.has('relay') && params.has('secret')) { - await db.secureSave(`${db.account.pubkey}-nwc`, uri); + await ark.createPrivkey(`${ark.account.pubkey}-nwc`, uri); setWalletConnectURL(uri); setLoading(false); } else { diff --git a/src/app/nwc/index.tsx b/src/app/nwc/index.tsx index a970338e..9340375d 100644 --- a/src/app/nwc/index.tsx +++ b/src/app/nwc/index.tsx @@ -2,22 +2,22 @@ import { useEffect, useState } from 'react'; import { NWCForm } from '@app/nwc/components/form'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { CheckCircleIcon } from '@shared/icons'; export function NWCScreen() { - const { db } = useStorage(); + const { ark } = useArk(); const [walletConnectURL, setWalletConnectURL] = useState(null); const remove = async () => { - await db.secureRemove(`${db.account.pubkey}-nwc`); + await ark.removePrivkey(`${ark.account.pubkey}-nwc`); setWalletConnectURL(null); }; useEffect(() => { async function getNWC() { - const nwc = await db.secureLoad(`${db.account.pubkey}-nwc`); + const nwc = await ark.loadPrivkey(`${ark.account.pubkey}-nwc`); if (nwc) setWalletConnectURL(nwc); } getNWC(); diff --git a/src/app/relays/components/relayList.tsx b/src/app/relays/components/relayList.tsx index 23861733..976f0e3c 100644 --- a/src/app/relays/components/relayList.tsx +++ b/src/app/relays/components/relayList.tsx @@ -2,19 +2,20 @@ import { useQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { VList } from 'virtua'; +import { useArk } from '@libs/ark'; + import { LoaderIcon, PlusIcon, ShareIcon } from '@shared/icons'; import { User } from '@shared/user'; -import { useNostr } from '@utils/hooks/useNostr'; import { useRelay } from '@utils/hooks/useRelay'; export function RelayList() { - const { getAllRelaysByUsers } = useNostr(); + const { ark } = useArk(); const { connectRelay } = useRelay(); const { status, data } = useQuery({ queryKey: ['relays'], queryFn: async () => { - return await getAllRelaysByUsers(); + return await ark.getAllRelaysFromContacts(); }, refetchOnWindowFocus: false, refetchOnMount: false, diff --git a/src/app/relays/components/userRelayList.tsx b/src/app/relays/components/userRelayList.tsx index 3afa1235..68e0ca93 100644 --- a/src/app/relays/components/userRelayList.tsx +++ b/src/app/relays/components/userRelayList.tsx @@ -1,29 +1,26 @@ -import { NDKKind, NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk'; +import { NDKKind } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; import { RelayForm } from '@app/relays/components/relayForm'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { CancelIcon, RefreshIcon } from '@shared/icons'; import { useRelay } from '@utils/hooks/useRelay'; export function UserRelayList() { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { removeRelay } = useRelay(); const { status, data, refetch } = useQuery({ - queryKey: ['relays', db.account.pubkey], + queryKey: ['relays', ark.account.pubkey], queryFn: async () => { - const event = await ndk.fetchEvent( - { + const event = await ark.getEventByFilter({ + filter: { kinds: [NDKKind.RelayList], - authors: [db.account.pubkey], + authors: [ark.account.pubkey], }, - { cacheUsage: NDKSubscriptionCacheUsage.ONLY_RELAY } - ); + }); if (!event) return []; return event.tags; @@ -31,7 +28,7 @@ export function UserRelayList() { refetchOnWindowFocus: false, }); - const currentRelays = new Set([...ndk.pool.relays.values()].map((item) => item.url)); + const currentRelays = new Set([...ark.relays]); return (
diff --git a/src/app/settings/advanced.tsx b/src/app/settings/advanced.tsx index 456c7056..fd7100e2 100644 --- a/src/app/settings/advanced.tsx +++ b/src/app/settings/advanced.tsx @@ -1,10 +1,10 @@ -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; export function AdvancedSettingScreen() { - const { db } = useStorage(); + const { ark } = useArk(); const clearCache = async () => { - await db.clearCache(); + await ark.clearCache(); }; return ( diff --git a/src/app/settings/backup.tsx b/src/app/settings/backup.tsx index c8cd0cd1..a4b9f2c8 100644 --- a/src/app/settings/backup.tsx +++ b/src/app/settings/backup.tsx @@ -1,23 +1,23 @@ import { nip19 } from 'nostr-tools'; import { useEffect, useState } from 'react'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { EyeOffIcon } from '@shared/icons'; export function BackupSettingScreen() { - const { db } = useStorage(); + const { ark } = useArk(); const [privkey, setPrivkey] = useState(null); const [showPassword, setShowPassword] = useState(false); const removePrivkey = async () => { - await db.secureRemove(db.account.pubkey); + await ark.removePrivkey(ark.account.pubkey); }; useEffect(() => { async function loadPrivkey() { - const key = await db.secureLoad(db.account.pubkey); + const key = await ark.loadPrivkey(ark.account.pubkey); if (key) setPrivkey(key); } diff --git a/src/app/settings/components/postCard.tsx b/src/app/settings/components/postCard.tsx index 374fe14c..dd344214 100644 --- a/src/app/settings/components/postCard.tsx +++ b/src/app/settings/components/postCard.tsx @@ -2,19 +2,19 @@ import { useQuery } from '@tanstack/react-query'; import { fetch } from '@tauri-apps/plugin-http'; import { Link } from 'react-router-dom'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { compactNumber } from '@utils/number'; export function PostCard() { - const { db } = useStorage(); + const { ark } = useArk(); const { status, data } = useQuery({ - queryKey: ['user-stats', db.account.pubkey], + queryKey: ['user-stats', ark.account.pubkey], queryFn: async ({ signal }: { signal: AbortSignal }) => { const res = await fetch( - `https://api.nostr.band/v0/stats/profile/${db.account.pubkey}`, + `https://api.nostr.band/v0/stats/profile/${ark.account.pubkey}`, { signal, } @@ -41,14 +41,14 @@ export function PostCard() { ) : (

- {compactNumber.format(data.stats[db.account.pubkey].pub_note_count)} + {compactNumber.format(data.stats[ark.account.pubkey].pub_note_count)}

Posts

View diff --git a/src/app/settings/components/profileCard.tsx b/src/app/settings/components/profileCard.tsx index f8c8429c..bfec4ffd 100644 --- a/src/app/settings/components/profileCard.tsx +++ b/src/app/settings/components/profileCard.tsx @@ -2,7 +2,7 @@ import * as Avatar from '@radix-ui/react-avatar'; import { minidenticon } from 'minidenticons'; import { Link } from 'react-router-dom'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { EditIcon, LoaderIcon } from '@shared/icons'; @@ -10,12 +10,12 @@ import { useProfile } from '@utils/hooks/useProfile'; import { displayNpub } from '@utils/shortenKey'; export function ProfileCard() { - const { db } = useStorage(); - const { isLoading, user } = useProfile(db.account.pubkey); + const { ark } = useArk(); + const { isLoading, user } = useProfile(ark.account.pubkey); const svgURI = 'data:image/svg+xml;utf8,' + - encodeURIComponent(minidenticon(db.account.pubkey, 90, 50)); + encodeURIComponent(minidenticon(ark.account.pubkey, 90, 50)); return (
@@ -38,7 +38,7 @@ export function ProfileCard() { {db.account.pubkey} @@ -57,7 +57,7 @@ export function ProfileCard() { {user?.display_name || user?.name}

- {user?.nip05 || displayNpub(db.account.pubkey, 16)} + {user?.nip05 || displayNpub(ark.account.pubkey, 16)}

diff --git a/src/app/settings/components/zapCard.tsx b/src/app/settings/components/zapCard.tsx index d1e35a84..e8f4a12b 100644 --- a/src/app/settings/components/zapCard.tsx +++ b/src/app/settings/components/zapCard.tsx @@ -1,19 +1,19 @@ import { useQuery } from '@tanstack/react-query'; import { fetch } from '@tauri-apps/plugin-http'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { compactNumber } from '@utils/number'; export function ZapCard() { - const { db } = useStorage(); + const { ark } = useArk(); const { status, data } = useQuery({ - queryKey: ['user-stats', db.account.pubkey], + queryKey: ['user-stats', ark.account.pubkey], queryFn: async ({ signal }: { signal: AbortSignal }) => { const res = await fetch( - `https://api.nostr.band/v0/stats/profile/${db.account.pubkey}`, + `https://api.nostr.band/v0/stats/profile/${ark.account.pubkey}`, { signal, } @@ -41,7 +41,7 @@ export function ZapCard() {

{compactNumber.format( - data?.stats[db.account.pubkey]?.zaps_received?.msats / 1000 || 0 + data?.stats[ark.account.pubkey]?.zaps_received?.msats / 1000 || 0 )}

diff --git a/src/app/settings/general.tsx b/src/app/settings/general.tsx index a8f6437e..7ae91bde 100644 --- a/src/app/settings/general.tsx +++ b/src/app/settings/general.tsx @@ -6,12 +6,12 @@ import { isPermissionGranted, requestPermission } from '@tauri-apps/plugin-notif import { useEffect, useState } from 'react'; import { twMerge } from 'tailwind-merge'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { DarkIcon, LightIcon, SystemModeIcon } from '@shared/icons'; export function GeneralSettingScreen() { - const { db } = useStorage(); + const { ark } = useArk(); const [settings, setSettings] = useState({ autoupdate: false, autolaunch: false, @@ -41,28 +41,28 @@ export function GeneralSettingScreen() { }; const toggleOutbox = async () => { - await db.createSetting('outbox', String(+!settings.outbox)); + await ark.createSetting('outbox', String(+!settings.outbox)); // update state setSettings((prev) => ({ ...prev, outbox: !settings.outbox })); }; const toggleMedia = async () => { - await db.createSetting('media', String(+!settings.media)); - db.settings.media = !settings.media; + await ark.createSetting('media', String(+!settings.media)); + ark.settings.media = !settings.media; // update state setSettings((prev) => ({ ...prev, media: !settings.media })); }; const toggleHashtag = async () => { - await db.createSetting('hashtag', String(+!settings.hashtag)); - db.settings.hashtag = !settings.hashtag; + await ark.createSetting('hashtag', String(+!settings.hashtag)); + ark.settings.hashtag = !settings.hashtag; // update state setSettings((prev) => ({ ...prev, hashtag: !settings.hashtag })); }; const toggleAutoupdate = async () => { - await db.createSetting('autoupdate', String(+!settings.autoupdate)); - db.settings.autoupdate = !settings.autoupdate; + await ark.createSetting('autoupdate', String(+!settings.autoupdate)); + ark.settings.autoupdate = !settings.autoupdate; // update state setSettings((prev) => ({ ...prev, autoupdate: !settings.autoupdate })); }; @@ -86,7 +86,7 @@ export function GeneralSettingScreen() { const permissionGranted = await isPermissionGranted(); setSettings((prev) => ({ ...prev, notification: permissionGranted })); - const data = await db.getAllSettings(); + const data = await ark.getAllSettings(); if (!data) return; data.forEach((item) => { diff --git a/src/app/users/components/profile.tsx b/src/app/users/components/profile.tsx index 07629ed6..910c909a 100644 --- a/src/app/users/components/profile.tsx +++ b/src/app/users/components/profile.tsx @@ -1,4 +1,3 @@ -import { NDKEvent, NDKKind, NDKUser } from '@nostr-dev-kit/ndk'; import * as Avatar from '@radix-ui/react-avatar'; import { minidenticon } from 'minidenticons'; import { useEffect, useState } from 'react'; @@ -7,8 +6,7 @@ import { toast } from 'sonner'; import { UserStats } from '@app/users/components/stats'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { NIP05 } from '@shared/nip05'; @@ -16,8 +14,7 @@ import { useProfile } from '@utils/hooks/useProfile'; import { displayNpub } from '@utils/shortenKey'; export function UserProfile({ pubkey }: { pubkey: string }) { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { user } = useProfile(pubkey); const [followed, setFollowed] = useState(false); @@ -28,12 +25,10 @@ export function UserProfile({ pubkey }: { pubkey: string }) { const follow = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setFollowed(true); - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const contacts = await user.follows(); - const add = await user.follow(new NDKUser({ pubkey: pubkey }), contacts); + const add = await ark.createContact({ pubkey }); if (!add) { toast.success('You already follow this user'); @@ -47,32 +42,17 @@ export function UserProfile({ pubkey }: { pubkey: string }) { const unfollow = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setFollowed(false); - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const contacts = await user.follows(); - contacts.delete(new NDKUser({ pubkey: pubkey })); - - const list = [...contacts].map((item) => [ - 'p', - item.pubkey, - item.relayUrls?.[0] || '', - '', - ]); - const event = new NDKEvent(ndk); - event.content = ''; - event.kind = NDKKind.Contacts; - event.tags = list; - - await event.publish(); + await ark.deleteContact({ pubkey }); } catch (e) { toast.error(e); } }; useEffect(() => { - if (db.account.contacts.includes(pubkey)) { + if (ark.account.contacts.includes(pubkey)) { setFollowed(true); } }, []); diff --git a/src/libs/ark/ark.ts b/src/libs/ark/ark.ts index cad18714..33a7f6f6 100644 --- a/src/libs/ark/ark.ts +++ b/src/libs/ark/ark.ts @@ -241,10 +241,26 @@ export class Ark { /** * Save private key to OS secure storage - * @deprecated this method will be marked as private in the next update + * @deprecated this method will be remove in the next update */ public async createPrivkey(name: string, privkey: string) { - await this.#keyring_save(name, privkey); + return await this.#keyring_save(name, privkey); + } + + /** + * Load private key from OS secure storage + * @deprecated this method will be remove in the next update + */ + public async loadPrivkey(name: string) { + return await this.#keyring_load(name); + } + + /** + * Remove private key from OS secure storage + * @deprecated this method will be remove in the next update + */ + public async removePrivkey(name: string) { + return await this.#keyring_remove(name); } public async updateAccount(column: string, value: string) { @@ -458,7 +474,19 @@ export class Ark { public async deleteContact({ pubkey }: { pubkey: string }) { const user = this.#ndk.getUser({ pubkey: this.account.pubkey }); const contacts = await user.follows(); - return await user.follow(new NDKUser({ pubkey: pubkey }), contacts); + contacts.delete(new NDKUser({ pubkey: pubkey })); + + const event = new NDKEvent(this.#ndk); + event.content = ''; + event.kind = NDKKind.Contacts; + event.tags = [...contacts].map((item) => [ + 'p', + item.pubkey, + item.relayUrls?.[0] || '', + '', + ]); + + return await event.publish(); } public async getAllEvents({ filter }: { filter: NDKFilter }) { @@ -476,6 +504,15 @@ export class Ark { return event; } + public async getEventByFilter({ filter }: { filter: NDKFilter }) { + const event = await this.#ndk.fetchEvent(filter, { + cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST, + }); + + if (!event) return null; + return event; + } + public getEventThread({ tags }: { tags: NDKTag[] }) { let rootEventId: string = null; let replyEventId: string = null; @@ -551,6 +588,32 @@ export class Ark { return events; } + public async getAllRelaysFromContacts() { + const LIMIT = 1; + const relayMap = new Map(); + const relayEvents = this.#fetcher.fetchLatestEventsPerAuthor( + { + authors: this.account.contacts, + relayUrls: this.relays, + }, + { kinds: [NDKKind.RelayList] }, + LIMIT + ); + + for await (const { author, events } of relayEvents) { + if (events[0]) { + events[0].tags.forEach((tag) => { + const users = relayMap.get(tag[1]); + + if (!users) return relayMap.set(tag[1], [author]); + return users.push(author); + }); + } + } + + return relayMap; + } + public async getInfiniteEvents({ filter, limit, diff --git a/src/shared/avatarUploader.tsx b/src/shared/avatarUploader.tsx index 2f4e15d8..fd920e76 100644 --- a/src/shared/avatarUploader.tsx +++ b/src/shared/avatarUploader.tsx @@ -1,16 +1,16 @@ import { message } from '@tauri-apps/plugin-dialog'; import { Dispatch, SetStateAction, useState } from 'react'; -import { LoaderIcon } from '@shared/icons'; +import { useArk } from '@libs/ark'; -import { useNostr } from '@utils/hooks/useNostr'; +import { LoaderIcon } from '@shared/icons'; export function AvatarUploader({ setPicture, }: { setPicture: Dispatch>; }) { - const { upload } = useNostr(); + const { ark } = useArk(); const [loading, setLoading] = useState(false); const uploadAvatar = async () => { @@ -18,7 +18,7 @@ export function AvatarUploader({ // start loading setLoading(true); - const image = await upload(); + const image = await ark.upload({}); if (image) { setPicture(image); diff --git a/src/shared/bannerUploader.tsx b/src/shared/bannerUploader.tsx index b3e308be..17282caf 100644 --- a/src/shared/bannerUploader.tsx +++ b/src/shared/bannerUploader.tsx @@ -1,16 +1,16 @@ import { message } from '@tauri-apps/plugin-dialog'; import { Dispatch, SetStateAction, useState } from 'react'; -import { LoaderIcon, PlusIcon } from '@shared/icons'; +import { useArk } from '@libs/ark'; -import { useNostr } from '@utils/hooks/useNostr'; +import { LoaderIcon, PlusIcon } from '@shared/icons'; export function BannerUploader({ setBanner, }: { setBanner: Dispatch>; }) { - const { upload } = useNostr(); + const { ark } = useArk(); const [loading, setLoading] = useState(false); const uploadBanner = async () => { @@ -18,7 +18,7 @@ export function BannerUploader({ // start loading setLoading(true); - const image = await upload(); + const image = await ark.upload({}); if (image) { setBanner(image); diff --git a/src/shared/notes/notify.tsx b/src/shared/notes/notify.tsx index 606b2599..5fb9e403 100644 --- a/src/shared/notes/notify.tsx +++ b/src/shared/notes/notify.tsx @@ -1,20 +1,21 @@ import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; import { memo } from 'react'; +import { useArk } from '@libs/ark'; + import { ReplyIcon, RepostIcon } from '@shared/icons'; import { ChildNote, TextKind } from '@shared/notes'; import { User } from '@shared/user'; import { WIDGET_KIND } from '@utils/constants'; import { formatCreatedAt } from '@utils/createdAt'; -import { useNostr } from '@utils/hooks/useNostr'; import { useWidget } from '@utils/hooks/useWidget'; export function NotifyNote({ event }: { event: NDKEvent }) { - const { getEventThread } = useNostr(); + const { ark } = useArk(); const { addWidget } = useWidget(); - const thread = getEventThread(event.tags); + const thread = ark.getEventThread({ tags: event.tags }); const createdAt = formatCreatedAt(event.created_at, false); if (event.kind === NDKKind.Reaction) { diff --git a/src/shared/notes/replies/list.tsx b/src/shared/notes/replies/list.tsx index ec16edd8..2b6dfbcd 100644 --- a/src/shared/notes/replies/list.tsx +++ b/src/shared/notes/replies/list.tsx @@ -1,38 +1,42 @@ +import { NDKSubscription } from '@nostr-dev-kit/ndk'; import { useEffect, useState } from 'react'; +import { useArk } from '@libs/ark'; + import { LoaderIcon } from '@shared/icons'; import { Reply } from '@shared/notes'; -import { useNostr } from '@utils/hooks/useNostr'; import { NDKEventWithReplies } from '@utils/types'; export function ReplyList({ eventId }: { eventId: string }) { - const { fetchAllReplies, sub } = useNostr(); + const { ark } = useArk(); const [data, setData] = useState(null); useEffect(() => { + let sub: NDKSubscription; let isCancelled = false; async function fetchRepliesAndSub() { - const events = await fetchAllReplies(eventId); + const events = await ark.getThreads({ id: eventId }); if (!isCancelled) { setData(events); } // subscribe for new replies - sub( - { + sub = ark.subscribe({ + filter: { '#e': [eventId], since: Math.floor(Date.now() / 1000), }, - (event: NDKEventWithReplies) => setData((prev) => [event, ...prev]), - false - ); + closeOnEose: false, + cb: (event: NDKEventWithReplies) => setData((prev) => [event, ...prev]), + }); } fetchRepliesAndSub(); return () => { isCancelled = true; + if (sub) sub.stop(); }; }, [eventId]); @@ -59,7 +63,7 @@ export function ReplyList({ eventId }: { eventId: string }) {
) : ( - data.map((event) => ) + data.map((event) => ) )}
); diff --git a/src/shared/notes/text.tsx b/src/shared/notes/text.tsx index ddc2e473..d9b318fd 100644 --- a/src/shared/notes/text.tsx +++ b/src/shared/notes/text.tsx @@ -2,20 +2,21 @@ import { NDKEvent } from '@nostr-dev-kit/ndk'; import { memo } from 'react'; import { twMerge } from 'tailwind-merge'; +import { useArk } from '@libs/ark'; + import { ChildNote, NoteActions } from '@shared/notes'; import { User } from '@shared/user'; import { WIDGET_KIND } from '@utils/constants'; -import { useNostr } from '@utils/hooks/useNostr'; import { useRichContent } from '@utils/hooks/useRichContent'; import { useWidget } from '@utils/hooks/useWidget'; export function TextNote({ event, className }: { event: NDKEvent; className?: string }) { const { parsedContent } = useRichContent(event.content); const { addWidget } = useWidget(); - const { getEventThread } = useNostr(); + const { ark } = useArk(); - const thread = getEventThread(event.tags); + const thread = ark.getEventThread({ tags: event.tags }); return (
diff --git a/src/shared/titleBar.tsx b/src/shared/titleBar.tsx index bc76f916..46dfa48b 100644 --- a/src/shared/titleBar.tsx +++ b/src/shared/titleBar.tsx @@ -1,4 +1,4 @@ -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { CancelIcon } from '@shared/icons'; import { User } from '@shared/user'; @@ -14,7 +14,7 @@ export function TitleBar({ title?: string; isLive?: boolean; }) { - const { db } = useStorage(); + const { ark } = useArk(); const { removeWidget } = useWidget(); return ( @@ -33,13 +33,13 @@ export function TitleBar({
{id === '9999' ? (
- {db.account.contacts + {ark.account.contacts ?.slice(0, 8) .map((item) => )} - {db.account.contacts?.length > 8 ? ( + {ark.account.contacts?.length > 8 ? (
- +{db.account.contacts?.length - 8} + +{ark.account.contacts?.length - 8}
) : null} diff --git a/src/shared/widgets/notification.tsx b/src/shared/widgets/notification.tsx index e56934d9..14ef5d6e 100644 --- a/src/shared/widgets/notification.tsx +++ b/src/shared/widgets/notification.tsx @@ -3,6 +3,7 @@ import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useMemo } from 'react'; import { VList } from 'virtua'; +import { useArk } from '@libs/ark'; import { useNDK } from '@libs/ndk/provider'; import { useStorage } from '@libs/storage/provider'; @@ -12,15 +13,12 @@ import { TitleBar } from '@shared/titleBar'; import { WidgetWrapper } from '@shared/widgets'; import { FETCH_LIMIT } from '@utils/constants'; -import { useNostr } from '@utils/hooks/useNostr'; import { sendNativeNotification } from '@utils/notification'; export function NotificationWidget() { const queryClient = useQueryClient(); - const { db } = useStorage(); - const { sub } = useNostr(); - const { ndk, relayUrls, fetcher } = useNDK(); + const { ark } = useArk(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['notification'], diff --git a/src/shared/widgets/thread.tsx b/src/shared/widgets/thread.tsx index dcb70fe2..3f846655 100644 --- a/src/shared/widgets/thread.tsx +++ b/src/shared/widgets/thread.tsx @@ -2,6 +2,8 @@ import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; import { useCallback } from 'react'; import { WVList } from 'virtua'; +import { useArk } from '@libs/ark'; + import { LoaderIcon } from '@shared/icons'; import { ChildNote, @@ -17,16 +19,15 @@ import { User } from '@shared/user'; import { WidgetWrapper } from '@shared/widgets'; import { useEvent } from '@utils/hooks/useEvent'; -import { useNostr } from '@utils/hooks/useNostr'; import { Widget } from '@utils/types'; export function ThreadWidget({ widget }: { widget: Widget }) { const { isFetching, isError, data } = useEvent(widget.content); - const { getEventThread } = useNostr(); + const { ark } = useArk(); const renderKind = useCallback( (event: NDKEvent) => { - const thread = getEventThread(event.tags); + const thread = ark.getEventThread({ tags: event.tags }); switch (event.kind) { case NDKKind.Text: return ( diff --git a/src/utils/hooks/useNostr.ts b/src/utils/hooks/useNostr.ts deleted file mode 100644 index 678f1692..00000000 --- a/src/utils/hooks/useNostr.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { - NDKEvent, - NDKFilter, - NDKKind, - NDKSubscription, - NDKTag, -} from '@nostr-dev-kit/ndk'; -import { open } from '@tauri-apps/plugin-dialog'; -import { readBinaryFile } from '@tauri-apps/plugin-fs'; -import { fetch } from '@tauri-apps/plugin-http'; -import { LRUCache } from 'lru-cache'; -import { NostrEventExt } from 'nostr-fetch'; -import { useMemo } from 'react'; - -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; - -import { nHoursAgo } from '@utils/date'; -import { getMultipleRandom } from '@utils/transform'; -import { NDKEventWithReplies } from '@utils/types'; - -export function useNostr() { - const { db } = useStorage(); - const { ndk, relayUrls, fetcher } = useNDK(); - - const subManager = useMemo( - () => - new LRUCache({ - max: 4, - dispose: (sub) => sub.stop(), - }), - [] - ); - - const sub = async ( - filter: NDKFilter, - callback: (event: NDKEvent) => void, - groupable?: boolean, - subKey?: string - ) => { - if (!ndk) throw new Error('NDK instance not found'); - - const key = subKey ?? JSON.stringify(filter); - if (!subManager.get(key)) { - const subEvent = ndk.subscribe(filter, { - closeOnEose: false, - groupable: groupable ?? true, - }); - - subEvent.addListener('event', (event: NDKEvent) => { - callback(event); - }); - - subManager.set(JSON.stringify(filter), subEvent); - console.log('sub: ', key); - } - }; - - const getEventThread = (tags: NDKTag[]) => { - let rootEventId: string = null; - let replyEventId: string = null; - - const events = tags.filter((el) => el[0] === 'e'); - - if (!events.length) return null; - - if (events.length === 1) - return { - rootEventId: events[0][1], - replyEventId: null, - }; - - if (events.length > 1) { - rootEventId = events.find((el) => el[3] === 'root')?.[1]; - replyEventId = events.find((el) => el[3] === 'reply')?.[1]; - - if (!rootEventId && !replyEventId) { - rootEventId = events[0][1]; - replyEventId = events[1][1]; - } - } - - return { - rootEventId, - replyEventId, - }; - }; - - const getAllActivities = async (limit?: number) => { - try { - const events = await ndk.fetchEvents({ - kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], - '#p': [db.account.pubkey], - limit: limit ?? 50, - }); - - return [...events]; - } catch (e) { - console.error('Error fetching activities', e); - } - }; - - const fetchNIP04Messages = async (sender: string) => { - let senderMessages: NostrEventExt[] = []; - - if (sender !== db.account.pubkey) { - senderMessages = await fetcher.fetchAllEvents( - relayUrls, - { - kinds: [NDKKind.EncryptedDirectMessage], - authors: [sender], - '#p': [db.account.pubkey], - }, - { since: 0 } - ); - } - - const userMessages = await fetcher.fetchAllEvents( - relayUrls, - { - kinds: [NDKKind.EncryptedDirectMessage], - authors: [db.account.pubkey], - '#p': [sender], - }, - { since: 0 } - ); - - const all = [...senderMessages, ...userMessages].sort( - (a, b) => a.created_at - b.created_at - ); - - return all as unknown as NDKEvent[]; - }; - - const fetchAllReplies = async (id: string, data?: NDKEventWithReplies[]) => { - let events = data || null; - - if (!data) { - events = (await fetcher.fetchAllEvents( - relayUrls, - { - kinds: [NDKKind.Text], - '#e': [id], - }, - { since: 0 }, - { sort: true } - )) as unknown as NDKEventWithReplies[]; - } - - if (events.length > 0) { - const replies = new Set(); - events.forEach((event) => { - const tags = event.tags.filter((el) => el[0] === 'e' && el[1] !== id); - if (tags.length > 0) { - tags.forEach((tag) => { - const rootIndex = events.findIndex((el) => el.id === tag[1]); - if (rootIndex !== -1) { - const rootEvent = events[rootIndex]; - if (rootEvent && rootEvent.replies) { - rootEvent.replies.push(event); - } else { - rootEvent.replies = [event]; - } - replies.add(event.id); - } - }); - } - }); - const cleanEvents = events.filter((ev) => !replies.has(ev.id)); - return cleanEvents; - } - - return events; - }; - - const getAllNIP04Chats = async () => { - const events = await fetcher.fetchAllEvents( - relayUrls, - { - kinds: [NDKKind.EncryptedDirectMessage], - '#p': [db.account.pubkey], - }, - { since: 0 } - ); - - const dedup: NDKEvent[] = Object.values( - events.reduce((ev, { id, content, pubkey, created_at, tags }) => { - if (ev[pubkey]) { - if (ev[pubkey].created_at < created_at) { - ev[pubkey] = { id, content, pubkey, created_at, tags }; - } - } else { - ev[pubkey] = { id, content, pubkey, created_at, tags }; - } - return ev; - }, {}) - ); - - return dedup; - }; - - const getContactsByPubkey = async (pubkey: string) => { - const user = ndk.getUser({ pubkey: pubkey }); - const follows = [...(await user.follows())].map((user) => user.hexpubkey); - return getMultipleRandom([...follows], 10); - }; - - const getEventsByPubkey = async (pubkey: string) => { - const events = await fetcher.fetchAllEvents( - relayUrls, - { authors: [pubkey], kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Article] }, - { since: nHoursAgo(24) }, - { sort: true } - ); - return events as unknown as NDKEvent[]; - }; - - const getAllRelaysByUsers = async () => { - const relayMap = new Map(); - const relayEvents = fetcher.fetchLatestEventsPerAuthor( - { - authors: db.account.contacts, - relayUrls: relayUrls, - }, - { kinds: [NDKKind.RelayList] }, - 5 - ); - - for await (const { author, events } of relayEvents) { - if (events[0]) { - events[0].tags.forEach((tag) => { - const users = relayMap.get(tag[1]); - - if (!users) return relayMap.set(tag[1], [author]); - return users.push(author); - }); - } - } - - return relayMap; - }; - - const createZap = async (event: NDKEvent, amount: number, message?: string) => { - // @ts-expect-error, NostrEvent to NDKEvent - const ndkEvent = new NDKEvent(ndk, event); - const res = await ndkEvent.zap(amount, message ?? 'zap from lume'); - - return res; - }; - - const upload = async (ext: string[] = []) => { - const defaultExts = ['png', 'jpeg', 'jpg', 'gif'].concat(ext); - - const selected = await open({ - multiple: false, - filters: [ - { - name: 'Image', - extensions: defaultExts, - }, - ], - }); - - if (!selected) return null; - - const file = await readBinaryFile(selected.path); - const blob = new Blob([file]); - - const data = new FormData(); - data.append('fileToUpload', blob); - data.append('submit', 'Upload Image'); - - const res = await fetch('https://nostr.build/api/v2/upload/files', { - method: 'POST', - body: data, - }); - - if (!res.ok) return null; - - const json = await res.json(); - const content = json.data[0]; - - return content.url as string; - }; - - return { - sub, - getEventThread, - getAllNIP04Chats, - getContactsByPubkey, - getEventsByPubkey, - getAllRelaysByUsers, - getAllActivities, - fetchNIP04Messages, - fetchAllReplies, - createZap, - upload, - }; -} From 5f90bd0d22d71a280254b4d5dbb795294c34df61 Mon Sep 17 00:00:00 2001 From: reya Date: Fri, 8 Dec 2023 08:18:47 +0700 Subject: [PATCH 3/8] update dependencies --- package.json | 24 +- pnpm-lock.yaml | 851 +++++++++++++++++++++---------------------- src-tauri/Cargo.lock | 204 +++++------ 3 files changed, 528 insertions(+), 551 deletions(-) diff --git a/package.json b/package.json index 285211c2..7e198b0d 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", "@tanstack/react-query": "^5.12.2", - "@tanstack/react-query-devtools": "^5.12.2", + "@tanstack/react-query-devtools": "^5.13.3", "@tauri-apps/api": "2.0.0-alpha.11", "@tauri-apps/cli": "2.0.0-alpha.17", "@tauri-apps/plugin-autostart": "2.0.0-alpha.3", @@ -62,7 +62,7 @@ "@tiptap/suggestion": "^2.1.13", "@vidstack/react": "^1.8.3", "dayjs": "^1.11.10", - "framer-motion": "^10.16.12", + "framer-motion": "^10.16.15", "html-to-text": "^9.0.5", "light-bolt11-decoder": "^3.0.0", "lru-cache": "^10.1.0", @@ -85,7 +85,7 @@ "tauri-controls": "github:reyamir/tauri-controls", "tippy.js": "^6.3.7", "tiptap-markdown": "^0.8.8", - "virtua": "^0.17.4", + "virtua": "^0.17.5", "zustand": "^4.4.7" }, "devDependencies": { @@ -93,11 +93,11 @@ "@tailwindcss/typography": "^0.5.10", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/html-to-text": "^9.0.4", - "@types/node": "^20.10.3", - "@types/react": "^18.2.41", + "@types/node": "^20.10.4", + "@types/react": "^18.2.42", "@types/react-dom": "^18.2.17", - "@typescript-eslint/eslint-plugin": "^6.13.1", - "@typescript-eslint/parser": "^6.13.1", + "@typescript-eslint/eslint-plugin": "^6.13.2", + "@typescript-eslint/parser": "^6.13.2", "@vitejs/plugin-react-swc": "^3.5.0", "autoprefixer": "^10.4.16", "clsx": "^2.0.0", @@ -112,14 +112,14 @@ "lint-staged": "^15.2.0", "postcss": "^8.4.32", "prettier": "^3.1.0", - "prettier-plugin-tailwindcss": "^0.5.7", + "prettier-plugin-tailwindcss": "^0.5.9", "prop-types": "^15.8.1", "tailwind-merge": "^1.14.0", "tailwind-scrollbar": "^3.0.5", - "tailwindcss": "^3.3.5", - "typescript": "^5.3.2", - "vite": "^4.5.0", + "tailwindcss": "^3.3.6", + "typescript": "^5.3.3", + "vite": "^4.5.1", "vite-plugin-top-level-await": "^1.3.1", - "vite-tsconfig-paths": "^4.2.1" + "vite-tsconfig-paths": "^4.2.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed1f6b85..ffebbf17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,52 +10,52 @@ dependencies: version: 1.2.0 '@getalby/sdk': specifier: ^2.7.0 - version: 2.7.0(typescript@5.3.2) + version: 2.7.0(typescript@5.3.3) '@nostr-dev-kit/ndk': specifier: ^2.2.0 - version: 2.2.0(typescript@5.3.2) + version: 2.2.0(typescript@5.3.3) '@nostr-fetch/adapter-ndk': specifier: ^0.13.1 version: 0.13.1(@nostr-dev-kit/ndk@2.2.0)(nostr-fetch@0.13.1) '@radix-ui/react-accordion': specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-alert-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-avatar': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-collapsible': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dropdown-menu': specifier: ^2.0.6 - version: 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-hover-card': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-popover': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-switch': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-toolbar': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-tooltip': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@tanstack/react-query': specifier: ^5.12.2 version: 5.12.2(react@18.2.0) '@tanstack/react-query-devtools': - specifier: ^5.12.2 - version: 5.12.2(@tanstack/react-query@5.12.2)(react@18.2.0) + specifier: ^5.13.3 + version: 5.13.3(@tanstack/react-query@5.12.2)(react@18.2.0) '@tauri-apps/api': specifier: 2.0.0-alpha.11 version: 2.0.0-alpha.11 @@ -133,13 +133,13 @@ dependencies: version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13) '@vidstack/react': specifier: ^1.8.3 - version: 1.8.3(@types/react@18.2.41)(react@18.2.0) + version: 1.8.3(@types/react@18.2.42)(react@18.2.0) dayjs: specifier: ^1.11.10 version: 1.11.10 framer-motion: - specifier: ^10.16.12 - version: 10.16.12(react-dom@18.2.0)(react@18.2.0) + specifier: ^10.16.15 + version: 10.16.15(react-dom@18.2.0)(react@18.2.0) html-to-text: specifier: ^9.0.5 version: 9.0.5 @@ -163,7 +163,7 @@ dependencies: version: 0.13.1 nostr-tools: specifier: ^1.17.0 - version: 1.17.0(typescript@5.3.2) + version: 1.17.0(typescript@5.3.3) qrcode.react: specifier: ^3.1.0 version: 3.1.0(react@18.2.0) @@ -193,7 +193,7 @@ dependencies: version: 1.1.1 reactflow: specifier: ^11.10.1 - version: 11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + version: 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) sonner: specifier: ^1.2.4 version: 1.2.4(react-dom@18.2.0)(react@18.2.0) @@ -207,19 +207,19 @@ dependencies: specifier: ^0.8.8 version: 0.8.8(@tiptap/core@2.1.13) virtua: - specifier: ^0.17.4 - version: 0.17.4(react-dom@18.2.0)(react@18.2.0) + specifier: ^0.17.5 + version: 0.17.5(react-dom@18.2.0)(react@18.2.0) zustand: specifier: ^4.4.7 - version: 4.4.7(@types/react@18.2.41)(react@18.2.0) + version: 4.4.7(@types/react@18.2.42)(react@18.2.0) devDependencies: '@tailwindcss/forms': specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.3.5) + version: 0.5.7(tailwindcss@3.3.6) '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.10(tailwindcss@3.3.5) + version: 0.5.10(tailwindcss@3.3.6) '@trivago/prettier-plugin-sort-imports': specifier: ^4.3.0 version: 4.3.0(prettier@3.1.0) @@ -227,23 +227,23 @@ devDependencies: specifier: ^9.0.4 version: 9.0.4 '@types/node': - specifier: ^20.10.3 - version: 20.10.3 + specifier: ^20.10.4 + version: 20.10.4 '@types/react': - specifier: ^18.2.41 - version: 18.2.41 + specifier: ^18.2.42 + version: 18.2.42 '@types/react-dom': specifier: ^18.2.17 version: 18.2.17 '@typescript-eslint/eslint-plugin': - specifier: ^6.13.1 - version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2) + specifier: ^6.13.2 + version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: ^6.13.1 - version: 6.13.1(eslint@8.55.0)(typescript@5.3.2) + specifier: ^6.13.2 + version: 6.13.2(eslint@8.55.0)(typescript@5.3.3) '@vitejs/plugin-react-swc': specifier: ^3.5.0 - version: 3.5.0(vite@4.5.0) + version: 3.5.0(vite@4.5.1) autoprefixer: specifier: ^10.4.16 version: 10.4.16(postcss@8.4.32) @@ -284,8 +284,8 @@ devDependencies: specifier: ^3.1.0 version: 3.1.0 prettier-plugin-tailwindcss: - specifier: ^0.5.7 - version: 0.5.7(@trivago/prettier-plugin-sort-imports@4.3.0)(prettier@3.1.0) + specifier: ^0.5.9 + version: 0.5.9(@trivago/prettier-plugin-sort-imports@4.3.0)(prettier@3.1.0) prop-types: specifier: ^15.8.1 version: 15.8.1 @@ -294,22 +294,22 @@ devDependencies: version: 1.14.0 tailwind-scrollbar: specifier: ^3.0.5 - version: 3.0.5(tailwindcss@3.3.5) + version: 3.0.5(tailwindcss@3.3.6) tailwindcss: - specifier: ^3.3.5 - version: 3.3.5 + specifier: ^3.3.6 + version: 3.3.6 typescript: - specifier: ^5.3.2 - version: 5.3.2 + specifier: ^5.3.3 + version: 5.3.3 vite: - specifier: ^4.5.0 - version: 4.5.0(@types/node@20.10.3) + specifier: ^4.5.1 + version: 4.5.1(@types/node@20.10.4) vite-plugin-top-level-await: specifier: ^1.3.1 - version: 1.3.1(vite@4.5.0) + version: 1.3.1(vite@4.5.1) vite-tsconfig-paths: - specifier: ^4.2.1 - version: 4.2.1(typescript@5.3.2)(vite@4.5.0) + specifier: ^4.2.2 + version: 4.2.2(typescript@5.3.3)(vite@4.5.1) packages: @@ -707,8 +707,8 @@ packages: resolution: {integrity: sha512-Ua8gpC+28Eo9D2/xynTrrZIrSawgtobwtRLLYq4wH8N19qoMspWZ1vqfsDDVPgQFa+iHsVAk/SbdmoPAj6OH1g==} dev: false - /@floating-ui/core@1.5.1: - resolution: {integrity: sha512-QgcKYwzcc8vvZ4n/5uklchy8KVdjJwcOeI+HnnTNclJjs2nYsy23DOCf+sSV1kBwD9yDAoVKCkv/gEPzgQU3Pw==} + /@floating-ui/core@1.5.2: + resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==} dependencies: '@floating-ui/utils': 0.1.6 dev: false @@ -716,7 +716,7 @@ packages: /@floating-ui/dom@1.5.3: resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} dependencies: - '@floating-ui/core': 1.5.1 + '@floating-ui/core': 1.5.2 '@floating-ui/utils': 0.1.6 dev: false @@ -735,13 +735,13 @@ packages: resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==} dev: false - /@getalby/sdk@2.7.0(typescript@5.3.2): + /@getalby/sdk@2.7.0(typescript@5.3.3): resolution: {integrity: sha512-4NoEgdjx0R8SYDmJfCAsgvuBs0w3d8wsOMGI4m0h2MVsSeCcWW93lrzCl8bRmHTF5N7EfleHwnieYwn5j9KZTA==} engines: {node: '>=14'} dependencies: crypto-js: 4.2.0 events: 3.3.0 - nostr-tools: 1.17.0(typescript@5.3.2) + nostr-tools: 1.17.0(typescript@5.3.3) transitivePeerDependencies: - typescript dev: false @@ -847,7 +847,7 @@ packages: fastq: 1.15.0 dev: true - /@nostr-dev-kit/ndk@2.2.0(typescript@5.3.2): + /@nostr-dev-kit/ndk@2.2.0(typescript@5.3.3): resolution: {integrity: sha512-NdnErX8em9Y/qC4CVYTHYE0bvtLV2ZQh56+JOiarjeJd+J7ZdJX1P10ba463iEOodppvKZqFlYbuDU6CprehUA==} dependencies: '@noble/hashes': 1.3.2 @@ -856,7 +856,7 @@ packages: debug: 4.3.4 light-bolt11-decoder: 3.0.0 node-fetch: 3.3.2 - nostr-tools: 1.17.0(typescript@5.3.2) + nostr-tools: 1.17.0(typescript@5.3.3) tseep: 1.1.3 typescript-lru-cache: 2.0.0 utf8-buffer: 1.0.0 @@ -872,7 +872,7 @@ packages: '@nostr-dev-kit/ndk': ^0.8.4 nostr-fetch: ^0.13.1 dependencies: - '@nostr-dev-kit/ndk': 2.2.0(typescript@5.3.2) + '@nostr-dev-kit/ndk': 2.2.0(typescript@5.3.3) '@nostr-fetch/kernel': 0.13.1 nostr-fetch: 0.13.1 dev: false @@ -894,7 +894,7 @@ packages: '@babel/runtime': 7.23.5 dev: false - /@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==} peerDependencies: '@types/react': '*' @@ -909,21 +909,21 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==} peerDependencies: '@types/react': '*' @@ -938,18 +938,18 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: '@types/react': '*' @@ -963,14 +963,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==} peerDependencies: '@types/react': '*' @@ -984,17 +984,17 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} peerDependencies: '@types/react': '*' @@ -1009,20 +1009,20 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: '@types/react': '*' @@ -1036,17 +1036,17 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: '@types/react': '*' @@ -1056,11 +1056,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-context@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-context@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: '@types/react': '*' @@ -1070,11 +1070,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} peerDependencies: '@types/react': '*' @@ -1089,26 +1089,26 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.41)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) dev: false - /@radix-ui/react-direction@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-direction@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} peerDependencies: '@types/react': '*' @@ -1118,11 +1118,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} peerDependencies: '@types/react': '*' @@ -1137,17 +1137,17 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} peerDependencies: '@types/react': '*' @@ -1162,19 +1162,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} peerDependencies: '@types/react': '*' @@ -1184,11 +1184,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} peerDependencies: '@types/react': '*' @@ -1202,16 +1202,16 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==} peerDependencies: '@types/react': '*' @@ -1226,21 +1226,21 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-id@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-id@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} peerDependencies: '@types/react': '*' @@ -1250,12 +1250,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} peerDependencies: '@types/react': '*' @@ -1270,30 +1270,30 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.41)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) dev: false - /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} peerDependencies: '@types/react': '*' @@ -1308,27 +1308,27 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.41)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) dev: false - /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} peerDependencies: '@types/react': '*' @@ -1343,22 +1343,22 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.41)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.42)(react@18.2.0) '@radix-ui/rect': 1.0.1 - '@types/react': 18.2.41 + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} peerDependencies: '@types/react': '*' @@ -1372,14 +1372,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} peerDependencies: '@types/react': '*' @@ -1393,15 +1393,15 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: '@types/react': '*' @@ -1415,14 +1415,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: '@types/react': '*' @@ -1437,21 +1437,21 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} peerDependencies: '@types/react': '*' @@ -1465,14 +1465,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-slot@1.0.2(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-slot@1.0.2(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: '@types/react': '*' @@ -1482,12 +1482,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==} peerDependencies: '@types/react': '*' @@ -1502,19 +1502,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==} peerDependencies: '@types/react': '*' @@ -1529,19 +1529,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toggle': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-toggle': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-toggle@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-toggle@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==} peerDependencies: '@types/react': '*' @@ -1556,15 +1556,15 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-tBgmM/O7a07xbaEkYJWYTXkIdU/1pW4/KZORR43toC/4XWyBCURK0ei9kMUdp+gTPPKBgYLxXmRSH1EVcIDp8Q==} peerDependencies: '@types/react': '*' @@ -1579,19 +1579,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-separator': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-separator': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==} peerDependencies: '@types/react': '*' @@ -1606,24 +1606,24 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} peerDependencies: '@types/react': '*' @@ -1633,11 +1633,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} peerDependencies: '@types/react': '*' @@ -1647,12 +1647,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: '@types/react': '*' @@ -1662,12 +1662,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} peerDependencies: '@types/react': '*' @@ -1677,11 +1677,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} peerDependencies: '@types/react': '*' @@ -1691,11 +1691,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: '@types/react': '*' @@ -1706,11 +1706,11 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/rect': 1.0.1 - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-use-size@1.0.1(@types/react@18.2.41)(react@18.2.0): + /@radix-ui/react-use-size@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: '@types/react': '*' @@ -1720,12 +1720,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.41)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 react: 18.2.0 dev: false - /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} peerDependencies: '@types/react': '*' @@ -1739,8 +1739,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.41 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.42 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1752,39 +1752,39 @@ packages: '@babel/runtime': 7.23.5 dev: false - /@reactflow/background@11.3.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/background@11.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-06FPlSUOOMALEEs+2PqPAbpqmL7WDjrkbG2UsDr2d6mbcDDhHiV4tu9FYoz44SQvXo7ma9VRotlsaR4OiRcYsg==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.41)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/controls@11.2.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/controls@11.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-4QHT92/ACVlZkvV+Hq44bAPV8WbMhkJl+/J0EbXcqQ1+an7cWJsF84eeelJw7R5J76RoaSSpKdsWsL2v7HAVlw==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.41)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/core@11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/core@11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GIh3usY1W3eVobx//OO9+Cwm+5evQBBdPGxDaeXwm25UqPMWRI240nXQA5F/5gL5Mwpf0DUC7DR2EmrKNQy+Rw==} peerDependencies: react: '>=17' @@ -1800,19 +1800,19 @@ packages: d3-zoom: 3.0.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.41)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/minimap@11.7.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/minimap@11.7.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-kJEtyeQkTZYViLGebVWHVUJROMAGcvejvT+iX4DqKnFb5yK8E8LWlXQpRx2FrL9gDy80mJJaciy7IxnnQKE1bg==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@types/d3-selection': 3.0.10 '@types/d3-zoom': 3.0.8 classcat: 5.0.4 @@ -1820,41 +1820,41 @@ packages: d3-zoom: 3.0.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.41)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/node-resizer@2.2.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/node-resizer@2.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1Xb6q97uP7hRBLpog9sRCNfnsHdDgFRGEiU+lQqGgPEAeYwl4nRjWa/sXwH6ajniKxBhGEvrdzOgEFn6CRMcpQ==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 d3-drag: 3.0.0 d3-selection: 3.0.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.41)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/node-toolbar@1.3.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/node-toolbar@1.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-JXDEuZ0wKjZ8z7qK2bIst0eZPzNyVEsiHL0e93EyuqT4fA9icoyE0fLq2ryNOOp7MXgId1h7LusnH6ta45F0yQ==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.41)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer @@ -2046,16 +2046,16 @@ packages: resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} dev: true - /@tailwindcss/forms@0.5.7(tailwindcss@3.3.5): + /@tailwindcss/forms@0.5.7(tailwindcss@3.3.6): resolution: {integrity: sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==} peerDependencies: tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' dependencies: mini-svg-data-uri: 1.4.4 - tailwindcss: 3.3.5 + tailwindcss: 3.3.6 dev: true - /@tailwindcss/typography@0.5.10(tailwindcss@3.3.5): + /@tailwindcss/typography@0.5.10(tailwindcss@3.3.6): resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' @@ -2064,24 +2064,24 @@ packages: lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.3.5 + tailwindcss: 3.3.6 dev: true /@tanstack/query-core@5.12.1: resolution: {integrity: sha512-WbZztNmKq0t6QjdNmHzezbi/uifYo9j6e2GLJkodsYaYUlzMbAp91RDyeHkIZrm7EfO4wa6Sm5sxJZm5SPlh6w==} dev: false - /@tanstack/query-devtools@5.12.1: - resolution: {integrity: sha512-AUWLgdZEYq/ckMZrtKpofOwqxuFiEIdy3gZOh/ouIBVra9ijXVTxYt5cZFSjXJT4q/o0DMii6xMZuIJx2qXZ6g==} + /@tanstack/query-devtools@5.13.3: + resolution: {integrity: sha512-1acztPKZexvM9Ns2T0aq4rMVSDA3VGdB73KF7zT/KNVl6VfnBvs24wuIRVSPZKqyZznZTzT3/DzcpntYqg9hmw==} dev: false - /@tanstack/react-query-devtools@5.12.2(@tanstack/react-query@5.12.2)(react@18.2.0): - resolution: {integrity: sha512-EpjYxwUBj+CuzAiB++FMeHzNey4TynuudfWVMsZg0uzLrnR5I0j1CWHJkNsTutb/Db9TNG1Ae66n8ywVvPd1gQ==} + /@tanstack/react-query-devtools@5.13.3(@tanstack/react-query@5.12.2)(react@18.2.0): + resolution: {integrity: sha512-ct58CMRrcjANRWCQ6cxzSUtme2jlX5au63+ckhMONob8bIk5VRfUEi4R49AWNJFL5haTBKe0InC0AV4bWi75VQ==} peerDependencies: '@tanstack/react-query': ^5.12.2 react: ^18.0.0 dependencies: - '@tanstack/query-devtools': 5.12.1 + '@tanstack/query-devtools': 5.13.3 '@tanstack/react-query': 5.12.2(react@18.2.0) react: 18.2.0 dev: false @@ -2521,9 +2521,9 @@ packages: prosemirror-schema-list: 1.3.0 prosemirror-state: 1.4.3 prosemirror-tables: 1.3.5 - prosemirror-trailing-node: 2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.4) + prosemirror-trailing-node: 2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.5) prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.4 + prosemirror-view: 1.32.5 dev: false /@tiptap/react@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)(react-dom@18.2.0)(react@18.2.0): @@ -2804,8 +2804,8 @@ packages: resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} dev: false - /@types/node@20.10.3: - resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==} + /@types/node@20.10.4: + resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==} dependencies: undici-types: 5.26.5 dev: true @@ -2824,14 +2824,14 @@ packages: /@types/react-dom@18.2.17: resolution: {integrity: sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==} dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 - /@types/react@18.2.41: - resolution: {integrity: sha512-CwOGr/PiLiNBxEBqpJ7fO3kocP/2SSuC9fpH5K7tusrg4xPSRT/193rzolYwQnTN02We/ATXKnb6GqA5w4fRxw==} + /@types/react@18.2.42: + resolution: {integrity: sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==} dependencies: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 - csstype: 3.1.2 + csstype: 3.1.3 /@types/scheduler@0.16.8: resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} @@ -2844,8 +2844,8 @@ packages: resolution: {integrity: sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==} dev: false - /@typescript-eslint/eslint-plugin@6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2): - resolution: {integrity: sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==} + /@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -2856,25 +2856,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.2) - '@typescript-eslint/scope-manager': 6.13.1 - '@typescript-eslint/type-utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2) - '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2) - '@typescript-eslint/visitor-keys': 6.13.1 + '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.13.2 + '@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.13.2 debug: 4.3.4 eslint: 8.55.0 graphemer: 1.4.0 ignore: 5.3.0 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.3.2) - typescript: 5.3.2 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.13.1(eslint@8.55.0)(typescript@5.3.2): - resolution: {integrity: sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==} + /@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2883,27 +2883,27 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.13.1 - '@typescript-eslint/types': 6.13.1 - '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2) - '@typescript-eslint/visitor-keys': 6.13.1 + '@typescript-eslint/scope-manager': 6.13.2 + '@typescript-eslint/types': 6.13.2 + '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.13.2 debug: 4.3.4 eslint: 8.55.0 - typescript: 5.3.2 + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.13.1: - resolution: {integrity: sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==} + /@typescript-eslint/scope-manager@6.13.2: + resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.13.1 - '@typescript-eslint/visitor-keys': 6.13.1 + '@typescript-eslint/types': 6.13.2 + '@typescript-eslint/visitor-keys': 6.13.2 dev: true - /@typescript-eslint/type-utils@6.13.1(eslint@8.55.0)(typescript@5.3.2): - resolution: {integrity: sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==} + /@typescript-eslint/type-utils@6.13.2(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2912,23 +2912,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2) - '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2) + '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3) + '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3) debug: 4.3.4 eslint: 8.55.0 - ts-api-utils: 1.0.3(typescript@5.3.2) - typescript: 5.3.2 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.13.1: - resolution: {integrity: sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==} + /@typescript-eslint/types@6.13.2: + resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.13.1(typescript@5.3.2): - resolution: {integrity: sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==} + /@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.3): + resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -2936,20 +2936,20 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.13.1 - '@typescript-eslint/visitor-keys': 6.13.1 + '@typescript-eslint/types': 6.13.2 + '@typescript-eslint/visitor-keys': 6.13.2 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.3.2) - typescript: 5.3.2 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.13.1(eslint@8.55.0)(typescript@5.3.2): - resolution: {integrity: sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==} + /@typescript-eslint/utils@6.13.2(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2957,9 +2957,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.13.1 - '@typescript-eslint/types': 6.13.1 - '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2) + '@typescript-eslint/scope-manager': 6.13.2 + '@typescript-eslint/types': 6.13.2 + '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3) eslint: 8.55.0 semver: 7.5.4 transitivePeerDependencies: @@ -2967,11 +2967,11 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys@6.13.1: - resolution: {integrity: sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==} + /@typescript-eslint/visitor-keys@6.13.2: + resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.13.1 + '@typescript-eslint/types': 6.13.2 eslint-visitor-keys: 3.4.3 dev: true @@ -2979,25 +2979,25 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vidstack/react@1.8.3(@types/react@18.2.41)(react@18.2.0): + /@vidstack/react@1.8.3(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-QCyHy6e3LpzfajtjrhJPXzGYbBrBCUE5qYAatKXX+nxWqRvspa0fJPlnGeWb+tg6DlDsgwDLFjGNWj8qUeUVXQ==} engines: {node: '>=18'} peerDependencies: '@types/react': ^18.0.0 react: ^18.0.0 dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 media-captions: 1.0.2 react: 18.2.0 dev: false - /@vitejs/plugin-react-swc@3.5.0(vite@4.5.0): + /@vitejs/plugin-react-swc@3.5.0(vite@4.5.1): resolution: {integrity: sha512-1PrOvAaDpqlCV+Up8RkAh9qaiUjoDUcjtttyhXDKw53XA6Ve16SOp6cCOpRs8Dj8DqUQs6eTW5YkLcLJjrXAig==} peerDependencies: vite: ^4 || ^5 dependencies: '@swc/core': 1.3.100 - vite: 4.5.0(@types/node@20.10.3) + vite: 4.5.1(@types/node@20.10.4) transitivePeerDependencies: - '@swc/helpers' dev: true @@ -3230,7 +3230,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001566 - electron-to-chromium: 1.4.601 + electron-to-chromium: 1.4.608 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true @@ -3400,8 +3400,8 @@ packages: hasBin: true dev: true - /csstype@3.1.2: - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} /d3-color@3.1.0: resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} @@ -3606,8 +3606,8 @@ packages: domhandler: 5.0.3 dev: false - /electron-to-chromium@1.4.601: - resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==} + /electron-to-chromium@1.4.608: + resolution: {integrity: sha512-J2f/3iIIm3Mo0npneITZ2UPe4B1bg8fTNrFjD8715F/k1BvbviRuqYGkET1PgprrczXYTHFvotbBOmUp6KE0uA==} dev: true /emoji-regex@10.3.0: @@ -4069,8 +4069,8 @@ packages: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true - /framer-motion@10.16.12(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-w7Yzx0OzQ5Uh6uNkxaX+4TuAPuOKz3haSbjmHpdrqDpGuCJCpq6YP9Dy7JJWdZ6mJjndrg3Ao3vUwDajKNikCA==} + /framer-motion@10.16.15(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-BapW1EKc/Sgph1rKEwL9bFt2B79z7bu7jN4Up1jGUaTE78WBsVO+6eCmoUZsQH0hHtRUDnnTqjjBbM7dk9Kp8A==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 @@ -4914,7 +4914,7 @@ packages: '@nostr-fetch/kernel': 0.13.1 dev: false - /nostr-tools@1.17.0(typescript@5.3.2): + /nostr-tools@1.17.0(typescript@5.3.3): resolution: {integrity: sha512-LZmR8GEWKZeElbFV5Xte75dOeE9EFUW/QLI1Ncn3JKn0kFddDKEfBbFN8Mu4TMs+L4HR/WTPha2l+PPuRnJcMw==} peerDependencies: typescript: '>=5.0.0' @@ -4928,7 +4928,7 @@ packages: '@scure/base': 1.1.1 '@scure/bip32': 1.3.1 '@scure/bip39': 1.2.1 - typescript: 5.3.2 + typescript: 5.3.3 dev: false /npm-run-path@5.1.0: @@ -5220,14 +5220,13 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.5.7(@trivago/prettier-plugin-sort-imports@4.3.0)(prettier@3.1.0): - resolution: {integrity: sha512-4v6uESAgwCni6YF6DwJlRaDjg9Z+al5zM4JfngcazMy4WEf/XkPS5TEQjbD+DZ5iNuG6RrKQLa/HuX2SYzC3kQ==} + /prettier-plugin-tailwindcss@0.5.9(@trivago/prettier-plugin-sort-imports@4.3.0)(prettier@3.1.0): + resolution: {integrity: sha512-9x3t1s2Cjbut2QiP+O0mDqV3gLXTe2CgRlQDgucopVkUdw26sQi53p/q4qvGxMLBDfk/dcTV57Aa/zYwz9l8Ew==} engines: {node: '>=14.21.3'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' '@prettier/plugin-pug': '*' '@shopify/prettier-plugin-liquid': '*' - '@shufo/prettier-plugin-blade': '*' '@trivago/prettier-plugin-sort-imports': '*' prettier: ^3.0 prettier-plugin-astro: '*' @@ -5247,8 +5246,6 @@ packages: optional: true '@shopify/prettier-plugin-liquid': optional: true - '@shufo/prettier-plugin-blade': - optional: true '@trivago/prettier-plugin-sort-imports': optional: true prettier-plugin-astro: @@ -5315,7 +5312,7 @@ packages: dependencies: prosemirror-state: 1.4.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.4 + prosemirror-view: 1.32.5 dev: false /prosemirror-gapcursor@1.3.2: @@ -5324,7 +5321,7 @@ packages: prosemirror-keymap: 1.2.2 prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 - prosemirror-view: 1.32.4 + prosemirror-view: 1.32.5 dev: false /prosemirror-history@1.3.2: @@ -5332,7 +5329,7 @@ packages: dependencies: prosemirror-state: 1.4.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.4 + prosemirror-view: 1.32.5 rope-sequence: 1.3.4 dev: false @@ -5391,7 +5388,7 @@ packages: dependencies: prosemirror-model: 1.19.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.4 + prosemirror-view: 1.32.5 dev: false /prosemirror-tables@1.3.5: @@ -5401,10 +5398,10 @@ packages: prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.4 + prosemirror-view: 1.32.5 dev: false - /prosemirror-trailing-node@2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.4): + /prosemirror-trailing-node@2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.5): resolution: {integrity: sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==} peerDependencies: prosemirror-model: ^1.19.0 @@ -5416,7 +5413,7 @@ packages: escape-string-regexp: 4.0.0 prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 - prosemirror-view: 1.32.4 + prosemirror-view: 1.32.5 dev: false /prosemirror-transform@1.8.0: @@ -5425,8 +5422,8 @@ packages: prosemirror-model: 1.19.3 dev: false - /prosemirror-view@1.32.4: - resolution: {integrity: sha512-WoT+ZYePp0WQvp5coABAysheZg9WttW3TSEUNgsfDQXmVOJlnjkbFbXicKPvWFLiC0ZjKt1ykbyoVKqhVnCiSQ==} + /prosemirror-view@1.32.5: + resolution: {integrity: sha512-OZ8JSytiOr1mlxu8XSPJSNMn3sQrycbP12Peubwsnq9xiyQk4F58u9wOPJYSXXG/uOXZQNSAITRnScZPdjEh+A==} dependencies: prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 @@ -5501,7 +5498,7 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true - /react-remove-scroll-bar@2.3.4(@types/react@18.2.41)(react@18.2.0): + /react-remove-scroll-bar@2.3.4(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} engines: {node: '>=10'} peerDependencies: @@ -5511,13 +5508,13 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 - react-style-singleton: 2.2.1(@types/react@18.2.41)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.42)(react@18.2.0) tslib: 2.6.2 dev: false - /react-remove-scroll@2.5.5(@types/react@18.2.41)(react@18.2.0): + /react-remove-scroll@2.5.5(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} engines: {node: '>=10'} peerDependencies: @@ -5527,13 +5524,13 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 - react-remove-scroll-bar: 2.3.4(@types/react@18.2.41)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.2.41)(react@18.2.0) + react-remove-scroll-bar: 2.3.4(@types/react@18.2.42)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.42)(react@18.2.0) tslib: 2.6.2 - use-callback-ref: 1.3.0(@types/react@18.2.41)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.41)(react@18.2.0) + use-callback-ref: 1.3.0(@types/react@18.2.42)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.42)(react@18.2.0) dev: false /react-router-dom@6.20.1(react-dom@18.2.0)(react@18.2.0): @@ -5564,7 +5561,7 @@ packages: engines: {node: '>=0.12.0'} dev: false - /react-style-singleton@2.2.1(@types/react@18.2.41)(react@18.2.0): + /react-style-singleton@2.2.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} peerDependencies: @@ -5574,7 +5571,7 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 get-nonce: 1.0.1 invariant: 2.2.4 react: 18.2.0 @@ -5588,18 +5585,18 @@ packages: loose-envify: 1.4.0 dev: false - /reactflow@11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0): + /reactflow@11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Q616fElAc5/N37tMwjuRkkgm/VgmnLLTNNCj61z5mvJxae+/VXZQMfot1K6a5LLz9G3SVKqU97PMb9Ga1PRXew==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/background': 11.3.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/controls': 11.2.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/core': 11.10.1(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/minimap': 11.7.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-resizer': 2.2.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-toolbar': 1.3.6(@types/react@18.2.41)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/background': 11.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/controls': 11.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/minimap': 11.7.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/node-resizer': 2.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/node-toolbar': 1.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: @@ -5958,17 +5955,17 @@ packages: /tailwind-merge@1.14.0: resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==} - /tailwind-scrollbar@3.0.5(tailwindcss@3.3.5): + /tailwind-scrollbar@3.0.5(tailwindcss@3.3.6): resolution: {integrity: sha512-0ZwxTivevqq9BY9fRP9zDjHl7Tu+J5giBGbln+0O1R/7nHtBUKnjQcA1aTIhK7Oyjp6Uc/Dj6/dn8Dq58k5Uww==} engines: {node: '>=12.13.0'} peerDependencies: tailwindcss: 3.x dependencies: - tailwindcss: 3.3.5 + tailwindcss: 3.3.6 dev: true - /tailwindcss@3.3.5: - resolution: {integrity: sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==} + /tailwindcss@3.3.6: + resolution: {integrity: sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -6050,20 +6047,20 @@ packages: is-number: 7.0.0 dev: true - /ts-api-utils@1.0.3(typescript@5.3.2): + /ts-api-utils@1.0.3(typescript@5.3.3): resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.3.2 + typescript: 5.3.3 dev: true /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /tsconfck@2.1.2(typescript@5.3.2): + /tsconfck@2.1.2(typescript@5.3.3): resolution: {integrity: sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==} engines: {node: ^14.13.1 || ^16 || >=18} hasBin: true @@ -6073,7 +6070,7 @@ packages: typescript: optional: true dependencies: - typescript: 5.3.2 + typescript: 5.3.3 dev: true /tseep@1.1.3: @@ -6166,8 +6163,8 @@ packages: resolution: {integrity: sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA==} dev: false - /typescript@5.3.2: - resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==} + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} hasBin: true @@ -6205,7 +6202,7 @@ packages: punycode: 2.3.1 dev: true - /use-callback-ref@1.3.0(@types/react@18.2.41)(react@18.2.0): + /use-callback-ref@1.3.0(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} engines: {node: '>=10'} peerDependencies: @@ -6215,12 +6212,12 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 tslib: 2.6.2 dev: false - /use-sidecar@1.1.2(@types/react@18.2.41)(react@18.2.0): + /use-sidecar@1.1.2(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} peerDependencies: @@ -6230,7 +6227,7 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 detect-node-es: 1.1.0 react: 18.2.0 tslib: 2.6.2 @@ -6266,8 +6263,8 @@ packages: hasBin: true dev: true - /virtua@0.17.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-969hSYRIpRkiAp39RWD5i1CneeIVl7qZ4NSn0M6tnQcc6Z5dfNTW713MCrtPMef3xVSOVJAHCKCP9xR/3ALFaA==} + /virtua@0.17.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-xTlxQ2/mDNSpU1tsdmpZT3kgoxSnjodTfs+OUeahkTSlBEWXnBujXKWXdl+hIRKOF1Igq8/gKPYltGSQV9p2eA==} peerDependencies: react: '>=16.14.0' react-dom: '>=16.14.0' @@ -6276,7 +6273,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /vite-plugin-top-level-await@1.3.1(vite@4.5.0): + /vite-plugin-top-level-await@1.3.1(vite@4.5.1): resolution: {integrity: sha512-55M1h4NAwkrpxPNOJIBzKZFihqLUzIgnElLSmPNPMR2Fn9+JHKaNg3sVX1Fq+VgvuBksQYxiD3OnwQAUu7kaPQ==} peerDependencies: vite: '>=2.8' @@ -6284,14 +6281,14 @@ packages: '@rollup/plugin-virtual': 3.0.2 '@swc/core': 1.3.100 uuid: 9.0.1 - vite: 4.5.0(@types/node@20.10.3) + vite: 4.5.1(@types/node@20.10.4) transitivePeerDependencies: - '@swc/helpers' - rollup dev: true - /vite-tsconfig-paths@4.2.1(typescript@5.3.2)(vite@4.5.0): - resolution: {integrity: sha512-GNUI6ZgPqT3oervkvzU+qtys83+75N/OuDaQl7HmOqFTb0pjZsuARrRipsyJhJ3enqV8beI1xhGbToR4o78nSQ==} + /vite-tsconfig-paths@4.2.2(typescript@5.3.3)(vite@4.5.1): + resolution: {integrity: sha512-dq0FjyxHHDnp0uS3P12WEOX2W7NeuLzX9AWP38D7Zw2CTbFErapwQVlCiT5DMJcVWKQ1MMdTe92PZl/rBQ7qcw==} peerDependencies: vite: '*' peerDependenciesMeta: @@ -6300,15 +6297,15 @@ packages: dependencies: debug: 4.3.4 globrex: 0.1.2 - tsconfck: 2.1.2(typescript@5.3.2) - vite: 4.5.0(@types/node@20.10.3) + tsconfck: 2.1.2(typescript@5.3.3) + vite: 4.5.1(@types/node@20.10.4) transitivePeerDependencies: - supports-color - typescript dev: true - /vite@4.5.0(@types/node@20.10.3): - resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} + /vite@4.5.1(@types/node@20.10.4): + resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -6335,7 +6332,7 @@ packages: terser: optional: true dependencies: - '@types/node': 20.10.3 + '@types/node': 20.10.4 esbuild: 0.18.20 postcss: 8.4.32 rollup: 3.29.4 @@ -6463,7 +6460,7 @@ packages: engines: {node: '>=10'} dev: true - /zustand@4.4.7(@types/react@18.2.41)(react@18.2.0): + /zustand@4.4.7(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==} engines: {node: '>=12.7.0'} peerDependencies: @@ -6478,7 +6475,7 @@ packages: react: optional: true dependencies: - '@types/react': 18.2.41 + '@types/react': 18.2.42 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) dev: false diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f8b35123..751aa4d1 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -120,30 +120,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -200,11 +200,11 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock 3.1.2", + "async-lock 3.2.0", "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "slab", ] @@ -246,11 +246,11 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" dependencies = [ - "async-lock 3.1.2", + "async-lock 3.2.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "parking", "polling 3.3.1", "rustix 0.38.26", @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.1.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea8b3453dd7cc96711834b75400d671b73e3656975fa68d9f277163b7f7e316" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ "event-listener 4.0.0", "event-listener-strategy", @@ -510,11 +510,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel", - "async-lock 3.1.2", + "async-lock 3.2.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "piper", "tracing", ] @@ -651,7 +651,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d1ece59890e746567b467253aea0adbe8a21784d0b025d8a306f66c391c2957" dependencies = [ "serde", - "toml 0.8.8", + "toml 0.8.2", ] [[package]] @@ -731,9 +731,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.10" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", @@ -741,9 +741,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.9" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", @@ -850,9 +850,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -1067,9 +1067,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.68+curl-8.4.0" +version = "0.4.70+curl-8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f" +checksum = "3c0333d8849afe78a4c8102a429a446bfdd055832af071945520e835ae2d841e" dependencies = [ "cc", "libc", @@ -1134,9 +1134,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" dependencies = [ "powerfmt", "serde", @@ -1269,7 +1269,7 @@ checksum = "f54cc3e827ee1c3812239a9a41dede7b4d7d5d5464faa32d71bd7cba28ce2cb2" dependencies = [ "cc", "rustc_version", - "toml 0.8.8", + "toml 0.8.2", "vswhom", "winreg 0.51.0", ] @@ -1444,14 +1444,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] @@ -1624,14 +1624,13 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ "fastrand 2.0.1", "futures-core", "futures-io", - "memchr", "parking", "pin-project-lite", ] @@ -1954,7 +1953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5" dependencies = [ "heck", - "proc-macro-crate 2.0.0", + "proc-macro-crate 2.0.1", "proc-macro-error", "proc-macro2", "quote", @@ -2389,9 +2388,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" dependencies = [ "either", ] @@ -2502,9 +2501,9 @@ dependencies = [ [[package]] name = "keyring" -version = "2.0.5" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9549a129bd08149e0a71b2d1ce2729780d47127991bfd0a78cc1df697ec72492" +checksum = "ec6488afbd1d8202dbd6e2dd38c0753d8c0adba9ac9985fc6f732a0d551f75e1" dependencies = [ "byteorder", "lazy_static", @@ -2590,7 +2589,7 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.4.1", "libc", - "redox_syscall 0.4.1", + "redox_syscall", ] [[package]] @@ -2849,9 +2848,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -3188,9 +3187,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -3211,9 +3210,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.60" +version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -3252,9 +3251,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" dependencies = [ "cc", "libc", @@ -3349,7 +3348,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "windows-targets 0.48.5", ] @@ -3675,11 +3674,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" dependencies = [ - "toml_edit 0.20.7", + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -3865,15 +3865,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -3980,9 +3971,9 @@ dependencies = [ [[package]] name = "rfd" -version = "0.12.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +checksum = "241a0deb168c88050d872294f7b3106c1dfa8740942bcc97bc91b98e97b5c501" dependencies = [ "block", "dispatch", @@ -4490,9 +4481,9 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" dependencies = [ "itertools", "nom", @@ -4873,7 +4864,7 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 0.8.8", + "toml 0.8.2", "version-compare", ] @@ -5066,7 +5057,7 @@ dependencies = [ [[package]] name = "tauri-plugin-autostart" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "auto-launch", "log", @@ -5079,7 +5070,7 @@ dependencies = [ [[package]] name = "tauri-plugin-cli" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "clap", "log", @@ -5092,7 +5083,7 @@ dependencies = [ [[package]] name = "tauri-plugin-clipboard-manager" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "arboard", "log", @@ -5106,7 +5097,7 @@ dependencies = [ [[package]] name = "tauri-plugin-dialog" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "glib 0.16.9", "log", @@ -5123,7 +5114,7 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "anyhow", "glob", @@ -5136,7 +5127,7 @@ dependencies = [ [[package]] name = "tauri-plugin-http" version = "2.0.0-alpha.5" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "data-url", "glob", @@ -5153,7 +5144,7 @@ dependencies = [ [[package]] name = "tauri-plugin-notification" version = "2.0.0-alpha.5" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "log", "notify-rust", @@ -5171,7 +5162,7 @@ dependencies = [ [[package]] name = "tauri-plugin-os" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "gethostname 0.4.3", "log", @@ -5187,7 +5178,7 @@ dependencies = [ [[package]] name = "tauri-plugin-process" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "tauri", ] @@ -5195,7 +5186,7 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "encoding_rs", "log", @@ -5212,7 +5203,7 @@ dependencies = [ [[package]] name = "tauri-plugin-sql" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "futures-core", "log", @@ -5228,7 +5219,7 @@ dependencies = [ [[package]] name = "tauri-plugin-store" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "log", "serde", @@ -5244,7 +5235,7 @@ source = "git+https://github.com/wyhaya/tauri-plugin-theme#cccc9b3fbc308a475ef87 dependencies = [ "cocoa 0.25.0", "dirs-next", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "gtk", "once_cell", "serde", @@ -5256,7 +5247,7 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "base64", "dirs-next", @@ -5282,7 +5273,7 @@ dependencies = [ [[package]] name = "tauri-plugin-upload" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "futures-util", "log", @@ -5299,7 +5290,7 @@ dependencies = [ [[package]] name = "tauri-plugin-window-state" version = "2.0.0-alpha.4" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#e7c72c9816d14a218e702dd233a6cfec957c2ee6" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#a848e1870a71b7460a0c56ba44cc1af61d818483" dependencies = [ "bincode", "bitflags 2.4.1", @@ -5406,7 +5397,7 @@ checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall 0.4.1", + "redox_syscall", "rustix 0.38.26", "windows-sys 0.48.0", ] @@ -5600,21 +5591,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.20.2", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] @@ -5634,20 +5625,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap 2.1.0", "serde", @@ -5755,9 +5735,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" @@ -5777,9 +5757,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -6442,9 +6422,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" dependencies = [ "memchr", ] @@ -6655,18 +6635,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.28" +version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" +checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.28" +version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" +checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2" dependencies = [ "proc-macro2", "quote", From 68886ad5842e33bf7f66567fe8650691af19f13f Mon Sep 17 00:00:00 2001 From: reya Date: Fri, 8 Dec 2023 09:32:48 +0700 Subject: [PATCH 4/8] wip: migrate to ark --- src/app/chats/components/chatForm.tsx | 16 +-- src/app/chats/hooks/useDecryptMessage.tsx | 23 +--- src/app/relays/components/relayEventList.tsx | 102 ++++++++++----- src/app/users/index.tsx | 97 ++++++++++---- src/libs/ark/ark.ts | 128 ++++++++++++++++--- src/shared/accounts/active.tsx | 12 +- src/shared/layouts/app.tsx | 10 +- src/shared/layouts/auth.tsx | 6 +- src/shared/layouts/new.tsx | 6 +- src/shared/layouts/note.tsx | 6 +- src/shared/layouts/settings.tsx | 6 +- src/shared/notes/actions/reaction.tsx | 6 +- src/shared/notes/actions/repost.tsx | 6 +- src/shared/notes/actions/zap.tsx | 16 +-- src/shared/notes/replies/form.tsx | 22 +--- src/shared/userProfile.tsx | 33 +---- src/shared/widgets/article.tsx | 25 ++-- src/shared/widgets/file.tsx | 24 ++-- src/shared/widgets/group.tsx | 21 ++- src/shared/widgets/hashtag.tsx | 21 ++- src/shared/widgets/newsfeed.tsx | 38 ++---- src/shared/widgets/other/addGroupFeeds.tsx | 6 +- src/shared/widgets/topic.tsx | 37 ++---- src/shared/widgets/user.tsx | 105 ++++++++------- src/utils/hooks/useSuggestion.ts | 6 +- src/utils/hooks/useWidget.ts | 12 +- 26 files changed, 441 insertions(+), 349 deletions(-) diff --git a/src/app/chats/components/chatForm.tsx b/src/app/chats/components/chatForm.tsx index 27b62455..62389df7 100644 --- a/src/app/chats/components/chatForm.tsx +++ b/src/app/chats/components/chatForm.tsx @@ -1,29 +1,19 @@ -import { NDKEvent, NDKKind, NDKUser } from '@nostr-dev-kit/ndk'; import { useState } from 'react'; import { toast } from 'sonner'; import { MediaUploader } from '@app/chats/components/mediaUploader'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { EnterIcon } from '@shared/icons'; export function ChatForm({ receiverPubkey }: { receiverPubkey: string }) { - const { ndk } = useNDK(); + const { ark } = useArk(); const [value, setValue] = useState(''); const submit = async () => { try { - const recipient = new NDKUser({ pubkey: receiverPubkey }); - const message = await ndk.signer.encrypt(recipient, value); - - const event = new NDKEvent(ndk); - event.content = message; - event.kind = NDKKind.EncryptedDirectMessage; - event.tag(recipient); - - const publish = await event.publish(); - + const publish = await ark.nip04Encrypt({ content: value, pubkey: receiverPubkey }); if (publish) setValue(''); } catch (e) { toast.error(e); diff --git a/src/app/chats/hooks/useDecryptMessage.tsx b/src/app/chats/hooks/useDecryptMessage.tsx index 88e3d00e..b1320130 100644 --- a/src/app/chats/hooks/useDecryptMessage.tsx +++ b/src/app/chats/hooks/useDecryptMessage.tsx @@ -1,26 +1,17 @@ -import { NDKEvent, NDKUser } from '@nostr-dev-kit/ndk'; +import { NDKEvent } from '@nostr-dev-kit/ndk'; import { useEffect, useState } from 'react'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; -export function useDecryptMessage(message: NDKEvent) { - const { db } = useStorage(); - const { ndk } = useNDK(); - - const [content, setContent] = useState(message.content); +export function useDecryptMessage(event: NDKEvent) { + const { ark } = useArk(); + const [content, setContent] = useState(event.content); useEffect(() => { async function decryptContent() { try { - const sender = new NDKUser({ - pubkey: - db.account.pubkey === message.pubkey - ? message.tags.find((el) => el[0] === 'p')[1] - : message.pubkey, - }); - const result = await ndk.signer.decrypt(sender, message.content); - setContent(result); + const message = await ark.nip04Decrypt({ event }); + setContent(message); } catch (e) { console.error(e); } diff --git a/src/app/relays/components/relayEventList.tsx b/src/app/relays/components/relayEventList.tsx index e9249a04..ce60f9f3 100644 --- a/src/app/relays/components/relayEventList.tsx +++ b/src/app/relays/components/relayEventList.tsx @@ -1,33 +1,58 @@ import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; -import { useQuery } from '@tanstack/react-query'; -import { normalizeRelayUrl } from 'nostr-fetch'; -import { useCallback } from 'react'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import { useCallback, useMemo } from 'react'; import { VList } from 'virtua'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; -import { LoaderIcon } from '@shared/icons'; -import { MemoizedRepost, MemoizedTextNote, UnknownNote } from '@shared/notes'; +import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; +import { + MemoizedRepost, + MemoizedTextNote, + NoteSkeleton, + UnknownNote, +} from '@shared/notes'; + +import { FETCH_LIMIT } from '@utils/constants'; export function RelayEventList({ relayUrl }: { relayUrl: string }) { - const { fetcher } = useNDK(); - const { status, data } = useQuery({ - queryKey: ['relay-events', relayUrl], - queryFn: async () => { - const url = 'wss://' + relayUrl; - const events = await fetcher.fetchLatestEvents( - [normalizeRelayUrl(url)], - { - kinds: [NDKKind.Text, NDKKind.Repost], - }, - 20 - ); - return events as unknown as NDKEvent[]; - }, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - }); + const { ark } = useArk(); + const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery({ + queryKey: ['relay-events', relayUrl], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + const url = 'wss://' + relayUrl; + const events = await ark.getRelayEvents({ + relayUrl: url, + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + + return events; + }, + getNextPageParam: (lastPage) => { + const lastEvent = lastPage.at(-1); + if (!lastEvent) return; + return lastEvent.created_at - 1; + }, + refetchOnWindowFocus: false, + }); + + const allEvents = useMemo( + () => (data ? data.pages.flatMap((page) => page) : []), + [data] + ); const renderItem = useCallback( (event: NDKEvent) => { @@ -46,16 +71,33 @@ export function RelayEventList({ relayUrl }: { relayUrl: string }) { return ( {status === 'pending' ? ( -
-
- -

Loading newsfeed...

+
+
+
) : ( - data.map((item) => renderItem(item)) + allEvents.map((item) => renderItem(item)) )} -
+
+ {hasNextPage ? ( + + ) : null} +
); } diff --git a/src/app/users/index.tsx b/src/app/users/index.tsx index f34c57cf..491edd76 100644 --- a/src/app/users/index.tsx +++ b/src/app/users/index.tsx @@ -1,30 +1,60 @@ import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; -import { useQuery } from '@tanstack/react-query'; -import { useCallback } from 'react'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import { useCallback, useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { UserProfile } from '@app/users/components/profile'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; -import { MemoizedRepost, MemoizedTextNote, UnknownNote } from '@shared/notes'; +import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; +import { + MemoizedRepost, + MemoizedTextNote, + NoteSkeleton, + UnknownNote, +} from '@shared/notes'; + +import { FETCH_LIMIT } from '@utils/constants'; export function UserScreen() { const { pubkey } = useParams(); - const { ndk } = useNDK(); - const { status, data } = useQuery({ - queryKey: ['user-feed', pubkey], - queryFn: async () => { - const events = await ndk.fetchEvents({ - kinds: [NDKKind.Text, NDKKind.Repost], - authors: [pubkey], - limit: 20, - }); - const sorted = [...events].sort((a, b) => b.created_at - a.created_at); - return sorted; - }, - refetchOnWindowFocus: false, - }); + const { ark } = useArk(); + const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery({ + queryKey: ['user-posts', pubkey], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + const events = await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + authors: [pubkey], + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + + return events; + }, + getNextPageParam: (lastPage) => { + const lastEvent = lastPage.at(-1); + if (!lastEvent) return; + return lastEvent.created_at - 1; + }, + refetchOnWindowFocus: false, + }); + + const allEvents = useMemo( + () => (data ? data.pages.flatMap((page) => page) : []), + [data] + ); // render event match event kind const renderItem = useCallback( @@ -50,20 +80,33 @@ export function UserScreen() {
{status === 'pending' ? ( -
Loading...
- ) : data.length === 0 ? (
-
-
-

- User doesn't have any posts in the last 48 hours. -

-
+
+
) : ( - data.map((item) => renderItem(item)) + allEvents.map((item) => renderItem(item)) )} +
+ {hasNextPage ? ( + + ) : null} +
diff --git a/src/libs/ark/ark.ts b/src/libs/ark/ark.ts index 33a7f6f6..49290dd7 100644 --- a/src/libs/ark/ark.ts +++ b/src/libs/ark/ark.ts @@ -13,9 +13,15 @@ import { ndkAdapter } from '@nostr-fetch/adapter-ndk'; import { invoke } from '@tauri-apps/api/primitives'; import { open } from '@tauri-apps/plugin-dialog'; import { readBinaryFile } from '@tauri-apps/plugin-fs'; +import { fetch } from '@tauri-apps/plugin-http'; import { Platform } from '@tauri-apps/plugin-os'; import Database from '@tauri-apps/plugin-sql'; -import { NostrEventExt, NostrFetcher, normalizeRelayUrlSet } from 'nostr-fetch'; +import { + NostrEventExt, + NostrFetcher, + normalizeRelayUrl, + normalizeRelayUrlSet, +} from 'nostr-fetch'; import { toast } from 'sonner'; import { NDKCacheAdapterTauri } from '@libs/ark'; @@ -77,7 +83,7 @@ export class Ark { // NIP-46 Signer if (nsecbunker) { const localSignerPrivkey = await this.#keyring_load( - `${this.account.pubkey}-nsecbunker` + `${this.account.id}-nsecbunker` ); if (!localSignerPrivkey) { @@ -89,9 +95,9 @@ export class Ark { const bunker = new NDK({ explicitRelayUrls: ['wss://relay.nsecbunker.com', 'wss://nostr.vulpem.com'], }); - bunker.connect(); + await bunker.connect(); - const remoteSigner = new NDKNip46Signer(bunker, this.account.id, localSigner); + const remoteSigner = new NDKNip46Signer(bunker, this.account.pubkey, localSigner); await remoteSigner.blockUntilReady(); this.readyToSign = true; @@ -619,11 +625,13 @@ export class Ark { limit, pageParam = 0, signal = undefined, + dedup = true, }: { filter: NDKFilter; limit: number; pageParam?: number; signal?: AbortSignal; + dedup?: boolean; }) { const rootIds = new Set(); const dedupQueue = new Set(); @@ -637,18 +645,53 @@ export class Ark { return new NDKEvent(this.#ndk, event); }); - ndkEvents.forEach((event) => { - const tags = event.tags.filter((el) => el[0] === 'e'); - if (tags && tags.length > 0) { - const rootId = tags.filter((el) => el[3] === 'root')[1] ?? tags[0][1]; - if (rootIds.has(rootId)) return dedupQueue.add(event.id); - rootIds.add(rootId); + if (dedup) { + ndkEvents.forEach((event) => { + const tags = event.tags.filter((el) => el[0] === 'e'); + if (tags && tags.length > 0) { + const rootId = tags.filter((el) => el[3] === 'root')[1] ?? tags[0][1]; + if (rootIds.has(rootId)) return dedupQueue.add(event.id); + rootIds.add(rootId); + } + }); + + return ndkEvents + .filter((event) => !dedupQueue.has(event.id)) + .sort((a, b) => b.created_at - a.created_at); + } + + return ndkEvents.sort((a, b) => b.created_at - a.created_at); + } + + public async getRelayEvents({ + relayUrl, + filter, + limit, + pageParam = 0, + signal = undefined, + }: { + relayUrl: string; + filter: NDKFilter; + limit: number; + pageParam?: number; + signal?: AbortSignal; + dedup?: boolean; + }) { + const events = await this.#fetcher.fetchLatestEvents( + [normalizeRelayUrl(relayUrl)], + filter, + limit, + { + asOf: pageParam === 0 ? undefined : pageParam, + abortSignal: signal, } + ); + + const ndkEvents = events.map((event) => { + return new NDKEvent(this.#ndk, event); }); - return ndkEvents - .filter((event) => !dedupQueue.has(event.id)) - .sort((a, b) => b.created_at - a.created_at); + return ndkEvents.sort((a, b) => b.created_at - a.created_at); } /** @@ -714,11 +757,60 @@ export class Ark { if (!res.ok) throw new Error(`Failed to fetch NIP-05 service: ${nip05}`); const data: NIP05 = await res.json(); - if (data.names) { - if (data.names[localPath.toLowerCase()] !== pubkey) return false; - if (data.names[localPath] !== pubkey) return false; - return true; - } + + if (!data.names) return false; + + if (data.names[localPath.toLowerCase()] === pubkey) return true; + if (data.names[localPath] === pubkey) return true; + return false; } + + public async nip04Decrypt({ event }: { event: NDKEvent }) { + try { + const sender = new NDKUser({ + pubkey: + this.account.pubkey === event.pubkey + ? event.tags.find((el) => el[0] === 'p')[1] + : event.pubkey, + }); + const content = await this.#ndk.signer.decrypt(sender, event.content); + + return content; + } catch (e) { + console.error(e); + } + } + + public async nip04Encrypt({ content, pubkey }: { content: string; pubkey: string }) { + try { + const recipient = new NDKUser({ pubkey }); + const message = await this.#ndk.signer.encrypt(recipient, content); + + const event = new NDKEvent(this.#ndk); + event.content = message; + event.kind = NDKKind.EncryptedDirectMessage; + event.tag(recipient); + + const publish = await event.publish(); + + if (!publish) throw new Error('Failed to send NIP-04 encrypted message'); + return publish; + } catch (e) { + console.error(e); + } + } + + public async replyTo({ content, event }: { content: string; event: NDKEvent }) { + try { + const replyEvent = new NDKEvent(this.#ndk); + event.content = content; + event.kind = NDKKind.Text; + event.tag(event, 'reply'); + + return await replyEvent.publish(); + } catch (e) { + console.error(e); + } + } } diff --git a/src/shared/accounts/active.tsx b/src/shared/accounts/active.tsx index 1d3c6512..23e95665 100644 --- a/src/shared/accounts/active.tsx +++ b/src/shared/accounts/active.tsx @@ -2,7 +2,7 @@ import * as Avatar from '@radix-ui/react-avatar'; import { minidenticon } from 'minidenticons'; import { Link } from 'react-router-dom'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { AccountMoreActions } from '@shared/accounts/more'; import { NetworkStatusIndicator } from '@shared/networkStatusIndicator'; @@ -10,12 +10,12 @@ import { NetworkStatusIndicator } from '@shared/networkStatusIndicator'; import { useProfile } from '@utils/hooks/useProfile'; export function ActiveAccount() { - const { db } = useStorage(); - const { user } = useProfile(db.account.pubkey); + const { ark } = useArk(); + const { user } = useProfile(ark.account.pubkey); const svgURI = 'data:image/svg+xml;utf8,' + - encodeURIComponent(minidenticon(db.account.pubkey, 90, 50)); + encodeURIComponent(minidenticon(ark.account.pubkey, 90, 50)); return (
@@ -23,7 +23,7 @@ export function ActiveAccount() { {db.account.pubkey} diff --git a/src/shared/layouts/app.tsx b/src/shared/layouts/app.tsx index 659196e6..b98d09a4 100644 --- a/src/shared/layouts/app.tsx +++ b/src/shared/layouts/app.tsx @@ -2,21 +2,21 @@ import { Outlet, ScrollRestoration } from 'react-router-dom'; import { twMerge } from 'tailwind-merge'; import { WindowTitlebar } from 'tauri-controls'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { Navigation } from '@shared/navigation'; export function AppLayout() { - const { db } = useStorage(); + const { ark } = useArk(); return (
- {db.platform !== 'macos' ? ( + {ark.platform !== 'macos' ? ( ) : (
@@ -26,7 +26,7 @@ export function AppLayout() { data-tauri-drag-region className={twMerge( 'h-full w-[64px] shrink-0', - db.platform !== 'macos' ? 'pt-2' : 'pt-0' + ark.platform !== 'macos' ? 'pt-2' : 'pt-0' )} > diff --git a/src/shared/layouts/auth.tsx b/src/shared/layouts/auth.tsx index 4296eca1..52629947 100644 --- a/src/shared/layouts/auth.tsx +++ b/src/shared/layouts/auth.tsx @@ -1,14 +1,14 @@ import { Outlet, ScrollRestoration } from 'react-router-dom'; import { WindowTitlebar } from 'tauri-controls'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; export function AuthLayout() { - const { db } = useStorage(); + const { ark } = useArk(); return (
- {db.platform !== 'macos' ? ( + {ark.platform !== 'macos' ? ( ) : (
diff --git a/src/shared/layouts/new.tsx b/src/shared/layouts/new.tsx index c92a2799..877da035 100644 --- a/src/shared/layouts/new.tsx +++ b/src/shared/layouts/new.tsx @@ -2,17 +2,17 @@ import { Link, NavLink, Outlet, useLocation } from 'react-router-dom'; import { twMerge } from 'tailwind-merge'; import { WindowTitlebar } from 'tauri-controls'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ArrowLeftIcon } from '@shared/icons'; export function NewLayout() { - const { db } = useStorage(); + const { ark } = useArk(); const location = useLocation(); return (
- {db.platform !== 'macos' ? ( + {ark.platform !== 'macos' ? ( ) : (
diff --git a/src/shared/layouts/note.tsx b/src/shared/layouts/note.tsx index 4b3106a1..c9eaea3e 100644 --- a/src/shared/layouts/note.tsx +++ b/src/shared/layouts/note.tsx @@ -1,14 +1,14 @@ import { Outlet, ScrollRestoration } from 'react-router-dom'; import { WindowTitlebar } from 'tauri-controls'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; export function NoteLayout() { - const { db } = useStorage(); + const { ark } = useArk(); return (
- {db.platform !== 'macos' ? ( + {ark.platform !== 'macos' ? ( ) : (
diff --git a/src/shared/layouts/settings.tsx b/src/shared/layouts/settings.tsx index 5ccb689f..cbcbbdea 100644 --- a/src/shared/layouts/settings.tsx +++ b/src/shared/layouts/settings.tsx @@ -2,7 +2,7 @@ import { NavLink, Outlet, ScrollRestoration, useNavigate } from 'react-router-do import { twMerge } from 'tailwind-merge'; import { WindowTitlebar } from 'tauri-controls'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { AdvancedSettingsIcon, @@ -14,12 +14,12 @@ import { } from '@shared/icons'; export function SettingsLayout() { - const { db } = useStorage(); + const { ark } = useArk(); const navigate = useNavigate(); return (
- {db.platform !== 'macos' ? ( + {ark.platform !== 'macos' ? ( ) : (
diff --git a/src/shared/notes/actions/reaction.tsx b/src/shared/notes/actions/reaction.tsx index 30143541..e7395c78 100644 --- a/src/shared/notes/actions/reaction.tsx +++ b/src/shared/notes/actions/reaction.tsx @@ -4,7 +4,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { ReactionIcon } from '@shared/icons'; @@ -35,7 +35,7 @@ export function NoteReaction({ event }: { event: NDKEvent }) { const [open, setOpen] = useState(false); const [reaction, setReaction] = useState(null); - const { ndk } = useNDK(); + const { ark } = useArk(); const navigate = useNavigate(); const getReactionImage = (content: string) => { @@ -45,7 +45,7 @@ export function NoteReaction({ event }: { event: NDKEvent }) { const react = async (content: string) => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setReaction(content); diff --git a/src/shared/notes/actions/repost.tsx b/src/shared/notes/actions/repost.tsx index 4a3d4a66..d6a98324 100644 --- a/src/shared/notes/actions/repost.tsx +++ b/src/shared/notes/actions/repost.tsx @@ -6,7 +6,7 @@ import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { twMerge } from 'tailwind-merge'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon, RepostIcon } from '@shared/icons'; @@ -15,12 +15,12 @@ export function NoteRepost({ event }: { event: NDKEvent }) { const [isLoading, setIsLoading] = useState(false); const [isRepost, setIsRepost] = useState(false); - const { ndk } = useNDK(); + const { ark } = useArk(); const navigate = useNavigate(); const submit = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); setIsLoading(true); diff --git a/src/shared/notes/actions/zap.tsx b/src/shared/notes/actions/zap.tsx index 6c45abc3..04d3a5bb 100644 --- a/src/shared/notes/actions/zap.tsx +++ b/src/shared/notes/actions/zap.tsx @@ -9,8 +9,7 @@ import { useEffect, useRef, useState } from 'react'; import CurrencyInput from 'react-currency-input-field'; import { useNavigate } from 'react-router-dom'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { CancelIcon, ZapIcon } from '@shared/icons'; @@ -20,11 +19,7 @@ import { compactNumber } from '@utils/number'; import { displayNpub } from '@utils/shortenKey'; export function NoteZap({ event }: { event: NDKEvent }) { - const nwc = useRef(null); - const navigate = useNavigate(); - - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { user } = useProfile(event.pubkey); const [walletConnectURL, setWalletConnectURL] = useState(null); @@ -35,9 +30,12 @@ export function NoteZap({ event }: { event: NDKEvent }) { const [isCompleted, setIsCompleted] = useState(false); const [isLoading, setIsLoading] = useState(false); + const nwc = useRef(null); + const navigate = useNavigate(); + const createZapRequest = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); + if (!ark.readyToSign) return navigate('/new/privkey'); const zapAmount = parseInt(amount) * 1000; const res = await event.zap(zapAmount, zapMessage); @@ -88,7 +86,7 @@ export function NoteZap({ event }: { event: NDKEvent }) { useEffect(() => { async function getWalletConnectURL() { const uri: string = await invoke('secure_load', { - key: `${db.account.pubkey}-nwc`, + key: `${ark.account.pubkey}-nwc`, }); if (uri) setWalletConnectURL(uri); } diff --git a/src/shared/notes/replies/form.tsx b/src/shared/notes/replies/form.tsx index f84dfcf4..6b2fa9e8 100644 --- a/src/shared/notes/replies/form.tsx +++ b/src/shared/notes/replies/form.tsx @@ -1,15 +1,15 @@ -import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; +import { NDKEvent } from '@nostr-dev-kit/ndk'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { ReplyMediaUploader } from '@shared/notes'; export function NoteReplyForm({ rootEvent }: { rootEvent: NDKEvent }) { - const { ndk } = useNDK(); + const { ark } = useArk(); const navigate = useNavigate(); const [value, setValue] = useState(''); @@ -17,22 +17,14 @@ export function NoteReplyForm({ rootEvent }: { rootEvent: NDKEvent }) { const submit = async () => { try { - if (!ndk.signer) return navigate('/new/privkey'); - + if (!ark.readyToSign) return navigate('/new/privkey'); setLoading(true); - const event = new NDKEvent(ndk); - event.content = value; - event.kind = NDKKind.Text; - - // tag root event - event.tag(rootEvent, 'reply'); - // publish event - const publishedRelays = await event.publish(); + const publish = await ark.replyTo({ content: value, event: rootEvent }); - if (publishedRelays) { - toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`); + if (publish) { + toast.success(`Broadcasted to ${publish.size} relays successfully.`); // reset state setValue(''); diff --git a/src/shared/userProfile.tsx b/src/shared/userProfile.tsx index 8e0f84f7..c66799ac 100644 --- a/src/shared/userProfile.tsx +++ b/src/shared/userProfile.tsx @@ -1,10 +1,8 @@ -import { NDKEvent, NDKKind, NDKUser } from '@nostr-dev-kit/ndk'; import { useEffect, useState } from 'react'; -import { Link, useNavigate } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import { toast } from 'sonner'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { NIP05 } from '@shared/nip05'; @@ -12,18 +10,14 @@ import { useProfile } from '@utils/hooks/useProfile'; import { displayNpub } from '@utils/shortenKey'; export function UserProfile({ pubkey }: { pubkey: string }) { - const { db } = useStorage(); - const { ndk } = useNDK(); + const { ark } = useArk(); const { user } = useProfile(pubkey); const [followed, setFollowed] = useState(false); - const navigate = useNavigate(); const follow = async (pubkey: string) => { try { - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const contacts = await user.follows(); - const add = await user.follow(new NDKUser({ pubkey: pubkey }), contacts); + const add = await ark.createContact({ pubkey }); if (add) { setFollowed(true); @@ -37,22 +31,9 @@ export function UserProfile({ pubkey }: { pubkey: string }) { const unfollow = async (pubkey: string) => { try { - if (!ndk.signer) return navigate('/new/privkey'); + const remove = await ark.deleteContact({ pubkey }); - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const contacts = await user.follows(); - contacts.delete(new NDKUser({ pubkey: pubkey })); - - let list: string[][]; - contacts.forEach((el) => list.push(['p', el.pubkey, el.relayUrls?.[0] || '', ''])); - - const event = new NDKEvent(ndk); - event.content = ''; - event.kind = NDKKind.Contacts; - event.tags = list; - - const publishedRelays = await event.publish(); - if (publishedRelays) { + if (remove) { setFollowed(false); } } catch (error) { @@ -61,7 +42,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) { }; useEffect(() => { - if (db.account.contacts.includes(pubkey)) { + if (ark.account.contacts.includes(pubkey)) { setFollowed(true); } }, []); diff --git a/src/shared/widgets/article.tsx b/src/shared/widgets/article.tsx index 7a29799b..7c90092b 100644 --- a/src/shared/widgets/article.tsx +++ b/src/shared/widgets/article.tsx @@ -1,11 +1,10 @@ -import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; +import { NDKKind } from '@nostr-dev-kit/ndk'; import { useInfiniteQuery } from '@tanstack/react-query'; import { FetchFilter } from 'nostr-fetch'; import { useMemo } from 'react'; import { VList } from 'virtua'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { MemoizedArticleNote } from '@shared/notes'; @@ -16,8 +15,7 @@ import { FETCH_LIMIT } from '@utils/constants'; import { Widget } from '@utils/types'; export function ArticleWidget({ widget }: { widget: Widget }) { - const { db } = useStorage(); - const { ndk, relayUrls, fetcher } = useNDK(); + const { ark } = useArk(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['article', widget.id], @@ -39,20 +37,19 @@ export function ArticleWidget({ widget }: { widget: Widget }) { } else { filter = { kinds: [NDKKind.Article], - authors: db.account.contacts, + authors: ark.account.contacts, }; } - const events = await fetcher.fetchLatestEvents(relayUrls, filter, FETCH_LIMIT, { - asOf: pageParam === 0 ? undefined : pageParam, - abortSignal: signal, + const events = await ark.getInfiniteEvents({ + filter, + limit: FETCH_LIMIT, + pageParam, + signal, + dedup: false, }); - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); - }); - - return ndkEvents.sort((a, b) => b.created_at - a.created_at); + return events; }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); diff --git a/src/shared/widgets/file.tsx b/src/shared/widgets/file.tsx index 2fe07523..d1f95604 100644 --- a/src/shared/widgets/file.tsx +++ b/src/shared/widgets/file.tsx @@ -1,11 +1,9 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; import { useInfiniteQuery } from '@tanstack/react-query'; import { FetchFilter } from 'nostr-fetch'; import { useMemo } from 'react'; import { VList } from 'virtua'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { MemoizedFileNote } from '@shared/notes'; @@ -16,8 +14,7 @@ import { FETCH_LIMIT } from '@utils/constants'; import { Widget } from '@utils/types'; export function FileWidget({ widget }: { widget: Widget }) { - const { db } = useStorage(); - const { ndk, relayUrls, fetcher } = useNDK(); + const { ark } = useArk(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['media', widget.id], @@ -39,20 +36,19 @@ export function FileWidget({ widget }: { widget: Widget }) { } else { filter = { kinds: [1063], - authors: db.account.contacts, + authors: ark.account.contacts, }; } - const events = await fetcher.fetchLatestEvents(relayUrls, filter, FETCH_LIMIT, { - asOf: pageParam === 0 ? undefined : pageParam, - abortSignal: signal, + const events = await ark.getInfiniteEvents({ + filter, + limit: FETCH_LIMIT, + pageParam, + signal, + dedup: false, }); - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); - }); - - return ndkEvents.sort((a, b) => b.created_at - a.created_at); + return events; }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); diff --git a/src/shared/widgets/group.tsx b/src/shared/widgets/group.tsx index 41be9b0c..dd9aeaec 100644 --- a/src/shared/widgets/group.tsx +++ b/src/shared/widgets/group.tsx @@ -3,7 +3,7 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import { useCallback, useMemo } from 'react'; import { VList } from 'virtua'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { @@ -19,7 +19,7 @@ import { FETCH_LIMIT } from '@utils/constants'; import { Widget } from '@utils/types'; export function GroupWidget({ widget }: { widget: Widget }) { - const { relayUrls, ndk, fetcher } = useNDK(); + const { ark } = useArk(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['groupfeeds', widget.id], @@ -32,21 +32,18 @@ export function GroupWidget({ widget }: { widget: Widget }) { pageParam: number; }) => { const authors = JSON.parse(widget.content); - const events = await fetcher.fetchLatestEvents( - relayUrls, - { + const events = await ark.getInfiniteEvents({ + filter: { kinds: [NDKKind.Text, NDKKind.Repost], authors: authors, }, - FETCH_LIMIT, - { asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal } - ); - - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); + limit: FETCH_LIMIT, + pageParam, + signal, + dedup: false, }); - return ndkEvents.sort((a, b) => b.created_at - a.created_at); + return events; }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); diff --git a/src/shared/widgets/hashtag.tsx b/src/shared/widgets/hashtag.tsx index 178de71f..567f39f5 100644 --- a/src/shared/widgets/hashtag.tsx +++ b/src/shared/widgets/hashtag.tsx @@ -3,7 +3,7 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import { useCallback, useMemo } from 'react'; import { VList } from 'virtua'; -import { useNDK } from '@libs/ndk/provider'; +import { useArk } from '@libs/ark'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { MemoizedRepost, MemoizedTextNote, UnknownNote } from '@shared/notes'; @@ -14,7 +14,7 @@ import { FETCH_LIMIT } from '@utils/constants'; import { Widget } from '@utils/types'; export function HashtagWidget({ widget }: { widget: Widget }) { - const { ndk, relayUrls, fetcher } = useNDK(); + const { ark } = useArk(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['hashtag', widget.id], @@ -26,21 +26,18 @@ export function HashtagWidget({ widget }: { widget: Widget }) { signal: AbortSignal; pageParam: number; }) => { - const events = await fetcher.fetchLatestEvents( - relayUrls, - { + const events = await ark.getInfiniteEvents({ + filter: { kinds: [NDKKind.Text, NDKKind.Repost], '#t': [widget.content], }, - FETCH_LIMIT, - { asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal } - ); - - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); + limit: FETCH_LIMIT, + pageParam, + signal, + dedup: false, }); - return ndkEvents.sort((a, b) => b.created_at - a.created_at); + return events; }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); diff --git a/src/shared/widgets/newsfeed.tsx b/src/shared/widgets/newsfeed.tsx index b2e8bf16..5ed78a79 100644 --- a/src/shared/widgets/newsfeed.tsx +++ b/src/shared/widgets/newsfeed.tsx @@ -3,8 +3,7 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import { useCallback, useMemo, useRef } from 'react'; import { VList, VListHandle } from 'virtua'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { @@ -19,8 +18,7 @@ import { LiveUpdater, WidgetWrapper } from '@shared/widgets'; import { FETCH_LIMIT } from '@utils/constants'; export function NewsfeedWidget() { - const { db } = useStorage(); - const { relayUrls, ndk, fetcher } = useNDK(); + const { ark } = useArk(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['newsfeed'], @@ -32,35 +30,17 @@ export function NewsfeedWidget() { signal: AbortSignal; pageParam: number; }) => { - const rootIds = new Set(); - const dedupQueue = new Set(); - - const events = await fetcher.fetchLatestEvents( - relayUrls, - { + const events = await ark.getInfiniteEvents({ + filter: { kinds: [NDKKind.Text, NDKKind.Repost], - authors: db.account.contacts, + authors: ark.account.contacts, }, - FETCH_LIMIT, - { asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal } - ); - - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); + limit: FETCH_LIMIT, + pageParam, + signal, }); - ndkEvents.forEach((event) => { - const tags = event.tags.filter((el) => el[0] === 'e'); - if (tags && tags.length > 0) { - const rootId = tags.filter((el) => el[3] === 'root')[1] ?? tags[0][1]; - if (rootIds.has(rootId)) return dedupQueue.add(event.id); - rootIds.add(rootId); - } - }); - - return ndkEvents - .filter((event) => !dedupQueue.has(event.id)) - .sort((a, b) => b.created_at - a.created_at); + return events; }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); diff --git a/src/shared/widgets/other/addGroupFeeds.tsx b/src/shared/widgets/other/addGroupFeeds.tsx index 4d2f9b9c..6713e441 100644 --- a/src/shared/widgets/other/addGroupFeeds.tsx +++ b/src/shared/widgets/other/addGroupFeeds.tsx @@ -1,7 +1,7 @@ import * as Dialog from '@radix-ui/react-dialog'; import { useState } from 'react'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { ArrowRightCircleIcon, @@ -16,7 +16,7 @@ import { WIDGET_KIND } from '@utils/constants'; import { useWidget } from '@utils/hooks/useWidget'; export function AddGroupFeeds({ currentWidgetId }: { currentWidgetId: string }) { - const { db } = useStorage(); + const { ark } = useArk(); const { replaceWidget } = useWidget(); const [title, setTitle] = useState(''); @@ -95,7 +95,7 @@ export function AddGroupFeeds({ currentWidgetId }: { currentWidgetId: string }) Users
- {db.account.contacts.map((item: string) => ( + {ark.account?.contacts?.map((item: string) => (
- ) : data.length === 0 ? ( -
-
-
-

- No new post from 24 hours ago -

-
-
-
) : ( - data.map((item) => renderItem(item)) + allEvents.map((item) => renderItem(item)) )} +
+ {hasNextPage ? ( + + ) : null} +
diff --git a/src/utils/hooks/useSuggestion.ts b/src/utils/hooks/useSuggestion.ts index e8317d80..5307d878 100644 --- a/src/utils/hooks/useSuggestion.ts +++ b/src/utils/hooks/useSuggestion.ts @@ -4,14 +4,14 @@ import tippy from 'tippy.js'; import { MentionList } from '@app/new/components'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; export function useSuggestion() { - const { db } = useStorage(); + const { ark } = useArk(); const suggestion: MentionOptions['suggestion'] = { items: async ({ query }) => { - const users = await db.getAllCacheUsers(); + const users = await ark.getAllCacheUsers(); return users .filter((item) => { if (item.name) return item.name.toLowerCase().startsWith(query.toLowerCase()); diff --git a/src/utils/hooks/useWidget.ts b/src/utils/hooks/useWidget.ts index e8c902a8..c64d5519 100644 --- a/src/utils/hooks/useWidget.ts +++ b/src/utils/hooks/useWidget.ts @@ -1,16 +1,16 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useStorage } from '@libs/storage/provider'; +import { useArk } from '@libs/ark'; import { Widget } from '@utils/types'; export function useWidget() { - const { db } = useStorage(); + const { ark } = useArk(); const queryClient = useQueryClient(); const addWidget = useMutation({ mutationFn: async (widget: Widget) => { - return await db.createWidget(widget.kind, widget.title, widget.content); + return await ark.createWidget(widget.kind, widget.title, widget.content); }, onSuccess: (data) => { queryClient.setQueryData(['widgets'], (old: Widget[]) => [...old, data]); @@ -26,8 +26,8 @@ export function useWidget() { const prevWidgets = queryClient.getQueryData(['widgets']); // create new widget - await db.removeWidget(currentId); - const newWidget = await db.createWidget(widget.kind, widget.title, widget.content); + await ark.removeWidget(currentId); + const newWidget = await ark.createWidget(widget.kind, widget.title, widget.content); // Optimistically update to the new value queryClient.setQueryData(['widgets'], (prev: Widget[]) => [ @@ -57,7 +57,7 @@ export function useWidget() { ); // Update in database - await db.removeWidget(id); + await ark.removeWidget(id); // Return a context object with the snapshotted value return { prevWidgets }; From e5071870444d41942e9356ae9c97efef467c5fc5 Mon Sep 17 00:00:00 2001 From: reya Date: Fri, 8 Dec 2023 12:39:15 +0700 Subject: [PATCH 5/8] wip: fully migrate to ark --- package.json | 1 - pnpm-lock.yaml | 382 -------------- src/app.css | 2 - src/app.tsx | 11 - src/app/chats/chat.tsx | 5 +- src/app/chats/index.tsx | 8 +- src/app/explore/components/edge.tsx | 29 -- src/app/explore/components/groupTitle.tsx | 17 - src/app/explore/components/line.tsx | 14 - src/app/explore/components/userGroupNode.tsx | 34 -- .../explore/components/userLatestPosts.tsx | 59 --- src/app/explore/components/userNode.tsx | 21 - src/app/explore/components/userWithDrawer.tsx | 155 ------ src/app/explore/index.tsx | 116 ----- src/libs/ark/ark.ts | 89 +++- src/libs/ark/provider.tsx | 6 +- src/libs/ndk/cache.ts | 426 --------------- src/libs/ndk/instance.ts | 214 -------- src/libs/ndk/provider.tsx | 80 --- src/libs/storage/instance.ts | 486 ------------------ src/libs/storage/provider.tsx | 128 ----- src/shared/notes/repost.tsx | 34 +- src/shared/widgets/newsfeed.tsx | 4 +- src/shared/widgets/notification.tsx | 60 +-- 24 files changed, 141 insertions(+), 2240 deletions(-) delete mode 100644 src/app/explore/components/edge.tsx delete mode 100644 src/app/explore/components/groupTitle.tsx delete mode 100644 src/app/explore/components/line.tsx delete mode 100644 src/app/explore/components/userGroupNode.tsx delete mode 100644 src/app/explore/components/userLatestPosts.tsx delete mode 100644 src/app/explore/components/userNode.tsx delete mode 100644 src/app/explore/components/userWithDrawer.tsx delete mode 100644 src/app/explore/index.tsx delete mode 100644 src/libs/ndk/cache.ts delete mode 100644 src/libs/ndk/instance.ts delete mode 100644 src/libs/ndk/provider.tsx delete mode 100644 src/libs/storage/instance.ts delete mode 100644 src/libs/storage/provider.tsx diff --git a/package.json b/package.json index ed70e4d2..b310f471 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,6 @@ "react-hotkeys-hook": "^4.4.1", "react-router-dom": "^6.20.1", "react-string-replace": "^1.1.1", - "reactflow": "^11.10.1", "sonner": "^1.2.4", "tauri-controls": "github:reyamir/tauri-controls", "tippy.js": "^6.3.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffebbf17..aa7e38ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,9 +191,6 @@ dependencies: react-string-replace: specifier: ^1.1.1 version: 1.1.1 - reactflow: - specifier: ^11.10.1 - version: 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) sonner: specifier: ^1.2.4 version: 1.2.4(react-dom@18.2.0)(react@18.2.0) @@ -1752,114 +1749,6 @@ packages: '@babel/runtime': 7.23.5 dev: false - /@reactflow/background@11.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-06FPlSUOOMALEEs+2PqPAbpqmL7WDjrkbG2UsDr2d6mbcDDhHiV4tu9FYoz44SQvXo7ma9VRotlsaR4OiRcYsg==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/controls@11.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-4QHT92/ACVlZkvV+Hq44bAPV8WbMhkJl+/J0EbXcqQ1+an7cWJsF84eeelJw7R5J76RoaSSpKdsWsL2v7HAVlw==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/core@11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-GIh3usY1W3eVobx//OO9+Cwm+5evQBBdPGxDaeXwm25UqPMWRI240nXQA5F/5gL5Mwpf0DUC7DR2EmrKNQy+Rw==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@types/d3': 7.4.3 - '@types/d3-drag': 3.0.7 - '@types/d3-selection': 3.0.10 - '@types/d3-zoom': 3.0.8 - classcat: 5.0.4 - d3-drag: 3.0.0 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/minimap@11.7.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-kJEtyeQkTZYViLGebVWHVUJROMAGcvejvT+iX4DqKnFb5yK8E8LWlXQpRx2FrL9gDy80mJJaciy7IxnnQKE1bg==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/d3-selection': 3.0.10 - '@types/d3-zoom': 3.0.8 - classcat: 5.0.4 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/node-resizer@2.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-1Xb6q97uP7hRBLpog9sRCNfnsHdDgFRGEiU+lQqGgPEAeYwl4nRjWa/sXwH6ajniKxBhGEvrdzOgEFn6CRMcpQ==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 - d3-drag: 3.0.0 - d3-selection: 3.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/node-toolbar@1.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-JXDEuZ0wKjZ8z7qK2bIst0eZPzNyVEsiHL0e93EyuqT4fA9icoyE0fLq2ryNOOp7MXgId1h7LusnH6ta45F0yQ==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - /@remirror/core-constants@2.0.2: resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==} dev: false @@ -2598,189 +2487,6 @@ packages: - supports-color dev: true - /@types/d3-array@3.2.1: - resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} - dev: false - - /@types/d3-axis@3.0.6: - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} - dependencies: - '@types/d3-selection': 3.0.10 - dev: false - - /@types/d3-brush@3.0.6: - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} - dependencies: - '@types/d3-selection': 3.0.10 - dev: false - - /@types/d3-chord@3.0.6: - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} - dev: false - - /@types/d3-color@3.1.3: - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - dev: false - - /@types/d3-contour@3.0.6: - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} - dependencies: - '@types/d3-array': 3.2.1 - '@types/geojson': 7946.0.13 - dev: false - - /@types/d3-delaunay@6.0.4: - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} - dev: false - - /@types/d3-dispatch@3.0.6: - resolution: {integrity: sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==} - dev: false - - /@types/d3-drag@3.0.7: - resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} - dependencies: - '@types/d3-selection': 3.0.10 - dev: false - - /@types/d3-dsv@3.0.7: - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} - dev: false - - /@types/d3-ease@3.0.2: - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - dev: false - - /@types/d3-fetch@3.0.7: - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - dependencies: - '@types/d3-dsv': 3.0.7 - dev: false - - /@types/d3-force@3.0.9: - resolution: {integrity: sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==} - dev: false - - /@types/d3-format@3.0.4: - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} - dev: false - - /@types/d3-geo@3.1.0: - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - dependencies: - '@types/geojson': 7946.0.13 - dev: false - - /@types/d3-hierarchy@3.1.6: - resolution: {integrity: sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==} - dev: false - - /@types/d3-interpolate@3.0.4: - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - dependencies: - '@types/d3-color': 3.1.3 - dev: false - - /@types/d3-path@3.0.2: - resolution: {integrity: sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==} - dev: false - - /@types/d3-polygon@3.0.2: - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} - dev: false - - /@types/d3-quadtree@3.0.6: - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - dev: false - - /@types/d3-random@3.0.3: - resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} - dev: false - - /@types/d3-scale-chromatic@3.0.3: - resolution: {integrity: sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==} - dev: false - - /@types/d3-scale@4.0.8: - resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==} - dependencies: - '@types/d3-time': 3.0.3 - dev: false - - /@types/d3-selection@3.0.10: - resolution: {integrity: sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==} - dev: false - - /@types/d3-shape@3.1.6: - resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==} - dependencies: - '@types/d3-path': 3.0.2 - dev: false - - /@types/d3-time-format@4.0.3: - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} - dev: false - - /@types/d3-time@3.0.3: - resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==} - dev: false - - /@types/d3-timer@3.0.2: - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - dev: false - - /@types/d3-transition@3.0.8: - resolution: {integrity: sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==} - dependencies: - '@types/d3-selection': 3.0.10 - dev: false - - /@types/d3-zoom@3.0.8: - resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} - dependencies: - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.10 - dev: false - - /@types/d3@7.4.3: - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} - dependencies: - '@types/d3-array': 3.2.1 - '@types/d3-axis': 3.0.6 - '@types/d3-brush': 3.0.6 - '@types/d3-chord': 3.0.6 - '@types/d3-color': 3.1.3 - '@types/d3-contour': 3.0.6 - '@types/d3-delaunay': 6.0.4 - '@types/d3-dispatch': 3.0.6 - '@types/d3-drag': 3.0.7 - '@types/d3-dsv': 3.0.7 - '@types/d3-ease': 3.0.2 - '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.9 - '@types/d3-format': 3.0.4 - '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.6 - '@types/d3-interpolate': 3.0.4 - '@types/d3-path': 3.0.2 - '@types/d3-polygon': 3.0.2 - '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.3 - '@types/d3-scale': 4.0.8 - '@types/d3-scale-chromatic': 3.0.3 - '@types/d3-selection': 3.0.10 - '@types/d3-shape': 3.1.6 - '@types/d3-time': 3.0.3 - '@types/d3-time-format': 4.0.3 - '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.8 - '@types/d3-zoom': 3.0.8 - dev: false - - /@types/geojson@7946.0.13: - resolution: {integrity: sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==} - dev: false - /@types/html-to-text@9.0.4: resolution: {integrity: sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==} dev: true @@ -3307,10 +3013,6 @@ packages: fsevents: 2.3.3 dev: true - /classcat@5.0.4: - resolution: {integrity: sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==} - dev: false - /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3403,71 +3105,6 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - /d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - dev: false - - /d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} - engines: {node: '>=12'} - dev: false - - /d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} - engines: {node: '>=12'} - dependencies: - d3-dispatch: 3.0.1 - d3-selection: 3.0.0 - dev: false - - /d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - dev: false - - /d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - dependencies: - d3-color: 3.1.0 - dev: false - - /d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} - engines: {node: '>=12'} - dev: false - - /d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - dev: false - - /d3-transition@3.0.1(d3-selection@3.0.0): - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} - engines: {node: '>=12'} - peerDependencies: - d3-selection: 2 - 3 - dependencies: - d3-color: 3.1.0 - d3-dispatch: 3.0.1 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-timer: 3.0.1 - dev: false - - /d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} - engines: {node: '>=12'} - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - dev: false - /d@1.0.1: resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} dependencies: @@ -5585,25 +5222,6 @@ packages: loose-envify: 1.4.0 dev: false - /reactflow@11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Q616fElAc5/N37tMwjuRkkgm/VgmnLLTNNCj61z5mvJxae+/VXZQMfot1K6a5LLz9G3SVKqU97PMb9Ga1PRXew==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/background': 11.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/controls': 11.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/minimap': 11.7.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-resizer': 2.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-toolbar': 1.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - /read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} dependencies: diff --git a/src/app.css b/src/app.css index 69801706..fac2b4d3 100644 --- a/src/app.css +++ b/src/app.css @@ -1,5 +1,3 @@ -/* @import 'reactflow/dist/style.css'; */ - /* Vidstack */ @import '@vidstack/react/player/styles/default/theme.css'; @import '@vidstack/react/player/styles/default/layouts/video.css'; diff --git a/src/app.tsx b/src/app.tsx index f5cbc78b..3165aa5b 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,11 +1,9 @@ import { message } from '@tauri-apps/plugin-dialog'; import { fetch } from '@tauri-apps/plugin-http'; import { RouterProvider, createBrowserRouter, defer, redirect } from 'react-router-dom'; -import { ReactFlowProvider } from 'reactflow'; import { ChatsScreen } from '@app/chats'; import { ErrorScreen } from '@app/error'; -import { ExploreScreen } from '@app/explore'; import { useArk } from '@libs/ark'; @@ -87,15 +85,6 @@ export default function App() { return { Component: RelayScreen }; }, }, - { - path: 'explore', - element: ( - - - - ), - errorElement: , - }, { path: 'chats', element: , diff --git a/src/app/chats/chat.tsx b/src/app/chats/chat.tsx index 5f66f3d8..a6bccbbb 100644 --- a/src/app/chats/chat.tsx +++ b/src/app/chats/chat.tsx @@ -12,16 +12,13 @@ import { useArk } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; import { User } from '@shared/user'; -import { useNostr } from '@utils/hooks/useNostr'; - export function ChatScreen() { const { ark } = useArk(); const { pubkey } = useParams(); - const { fetchNIP04Messages } = useNostr(); const { status, data } = useQuery({ queryKey: ['nip04-dm', pubkey], queryFn: async () => { - return await fetchNIP04Messages(pubkey); + return await ark.getAllMessagesByPubkey({ pubkey }); }, refetchOnWindowFocus: false, }); diff --git a/src/app/chats/index.tsx b/src/app/chats/index.tsx index 67698c9a..700360c8 100644 --- a/src/app/chats/index.tsx +++ b/src/app/chats/index.tsx @@ -5,16 +5,16 @@ import { Outlet } from 'react-router-dom'; import { ChatListItem } from '@app/chats/components/chatListItem'; +import { useArk } from '@libs/ark'; + import { LoaderIcon } from '@shared/icons'; -import { useNostr } from '@utils/hooks/useNostr'; - export function ChatsScreen() { - const { getAllNIP04Chats } = useNostr(); + const { ark } = useArk(); const { status, data } = useQuery({ queryKey: ['nip04-chats'], queryFn: async () => { - return await getAllNIP04Chats(); + return await ark.getAllChats(); }, refetchOnWindowFocus: false, refetchOnMount: false, diff --git a/src/app/explore/components/edge.tsx b/src/app/explore/components/edge.tsx deleted file mode 100644 index 296996b1..00000000 --- a/src/app/explore/components/edge.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { BaseEdge, EdgeProps, getBezierPath } from 'reactflow'; - -export function Edge({ - sourceX, - sourceY, - targetX, - targetY, - sourcePosition, - targetPosition, - style = {}, - markerEnd, -}: EdgeProps) { - const [edgePath] = getBezierPath({ - sourceX, - sourceY, - sourcePosition, - targetX, - targetY, - targetPosition, - }); - - return ( - - ); -} diff --git a/src/app/explore/components/groupTitle.tsx b/src/app/explore/components/groupTitle.tsx deleted file mode 100644 index 14828d7f..00000000 --- a/src/app/explore/components/groupTitle.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { memo } from 'react'; - -import { useProfile } from '@utils/hooks/useProfile'; - -export const GroupTitle = memo(function GroupTitle({ pubkey }: { pubkey: string }) { - const { isLoading, user } = useProfile(pubkey); - - if (isLoading) { - return
; - } - - return ( -

{`${ - user.name || user.display_name - }'s network`}

- ); -}); diff --git a/src/app/explore/components/line.tsx b/src/app/explore/components/line.tsx deleted file mode 100644 index 993667c7..00000000 --- a/src/app/explore/components/line.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export function Line({ fromX, fromY, toX, toY }) { - return ( - - - - - ); -} diff --git a/src/app/explore/components/userGroupNode.tsx b/src/app/explore/components/userGroupNode.tsx deleted file mode 100644 index 0356d291..00000000 --- a/src/app/explore/components/userGroupNode.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Handle, Position } from 'reactflow'; - -import { UserWithDrawer } from '@app/explore/components/userWithDrawer'; - -import { GroupTitle } from './groupTitle'; - -export function UserGroupNode({ data }) { - return ( - <> - -
- {data.title ? ( -

{data.title}

- ) : ( - - )} -
- {data.list.map((user: string) => ( - - ))} -
-
- - - ); -} diff --git a/src/app/explore/components/userLatestPosts.tsx b/src/app/explore/components/userLatestPosts.tsx deleted file mode 100644 index 05a0d9fb..00000000 --- a/src/app/explore/components/userLatestPosts.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; -import { useQuery } from '@tanstack/react-query'; -import { useCallback } from 'react'; - -import { LoaderIcon } from '@shared/icons'; -import { MemoizedRepost, MemoizedTextNote, UnknownNote } from '@shared/notes'; - -import { useNostr } from '@utils/hooks/useNostr'; - -export function UserLatestPosts({ pubkey }: { pubkey: string }) { - const { getEventsByPubkey } = useNostr(); - const { status, data } = useQuery({ - queryKey: ['user-posts', pubkey], - queryFn: async () => { - return await getEventsByPubkey(pubkey); - }, - refetchOnWindowFocus: false, - }); - - const renderItem = useCallback( - (event: NDKEvent) => { - switch (event.kind) { - case NDKKind.Text: - return ; - case NDKKind.Repost: - return ; - default: - return ; - } - }, - [data] - ); - - return ( -
-

- Latest post -

-
- {status === 'pending' ? ( -
-
- - Loading latest posts... -
-
- ) : data.length < 1 ? ( -
-
- No posts from 24 hours ago -
-
- ) : ( - data.map((event) => renderItem(event)) - )} -
-
- ); -} diff --git a/src/app/explore/components/userNode.tsx b/src/app/explore/components/userNode.tsx deleted file mode 100644 index 0f5af72e..00000000 --- a/src/app/explore/components/userNode.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Handle, Position } from 'reactflow'; - -import { User } from '@shared/user'; - -export function UserNode({ data }) { - return ( - <> -
- -
- -
-
- - - ); -} diff --git a/src/app/explore/components/userWithDrawer.tsx b/src/app/explore/components/userWithDrawer.tsx deleted file mode 100644 index f498b70b..00000000 --- a/src/app/explore/components/userWithDrawer.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { NDKEvent, NDKKind, NDKUser } from '@nostr-dev-kit/ndk'; -import * as Dialog from '@radix-ui/react-dialog'; -import { memo, useEffect, useState } from 'react'; -import { Link } from 'react-router-dom'; -import { toast } from 'sonner'; - -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; - -import { NIP05 } from '@shared/nip05'; -import { TextNote } from '@shared/notes'; -import { User } from '@shared/user'; - -import { useProfile } from '@utils/hooks/useProfile'; -import { displayNpub } from '@utils/shortenKey'; - -import { UserLatestPosts } from './userLatestPosts'; - -export const UserWithDrawer = memo(function UserWithDrawer({ - pubkey, -}: { - pubkey: string; -}) { - const { db } = useStorage(); - const { ndk } = useNDK(); - const { isLoading, user } = useProfile(pubkey); - - const [followed, setFollowed] = useState(false); - - const follow = async (pubkey: string) => { - try { - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const contacts = await user.follows(); - const add = await user.follow(new NDKUser({ pubkey: pubkey }), contacts); - - if (add) { - setFollowed(true); - } else { - toast('You already follow this user'); - } - } catch (error) { - console.log(error); - } - }; - - const unfollow = async (pubkey: string) => { - try { - const user = ndk.getUser({ pubkey: db.account.pubkey }); - const contacts = await user.follows(); - contacts.delete(new NDKUser({ pubkey: pubkey })); - - let list: string[][]; - contacts.forEach((el) => list.push(['p', el.pubkey, el.relayUrls?.[0] || '', ''])); - - const event = new NDKEvent(ndk); - event.content = ''; - event.kind = NDKKind.Contacts; - event.tags = list; - - const publishedRelays = await event.publish(); - if (publishedRelays) { - setFollowed(false); - } - } catch (error) { - console.log(error); - } - }; - - useEffect(() => { - if (db.account.contacts.includes(pubkey)) { - setFollowed(true); - } - }, []); - - return ( - - - - - - -
- {isLoading ? ( -
-

Loading...

-
- ) : ( - <> -
- {pubkey} -
-
-
-
- {user?.name || user?.display_name || user?.displayName} -
- {user?.nip05 ? ( - - ) : ( - - {displayNpub(pubkey, 16)} - - )} -
- {user?.about ? : null} -
-
- {followed ? ( - - ) : ( - - )} - - Message - -
-
-
- - - )} -
-
-
-
- ); -}); diff --git a/src/app/explore/index.tsx b/src/app/explore/index.tsx deleted file mode 100644 index 2ec9400d..00000000 --- a/src/app/explore/index.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { useCallback, useMemo, useRef } from 'react'; -import ReactFlow, { - Background, - ConnectionMode, - addEdge, - useEdgesState, - useNodesState, - useReactFlow, -} from 'reactflow'; - -import { Edge } from '@app/explore/components/edge'; -import { Line } from '@app/explore/components/line'; -import { UserGroupNode } from '@app/explore/components/userGroupNode'; -import { UserNode } from '@app/explore/components/userNode'; - -import { useStorage } from '@libs/storage/provider'; - -import { useNostr } from '@utils/hooks/useNostr'; -import { getMultipleRandom } from '@utils/transform'; - -let id = 2; -const getId = () => `${id++}`; -const nodeTypes = { user: UserNode, userGroup: UserGroupNode }; -const edgeTypes = { buttonedge: Edge }; - -export function ExploreScreen() { - const { db } = useStorage(); - const { getContactsByPubkey } = useNostr(); - const { project } = useReactFlow(); - - const defaultContacts = useMemo(() => getMultipleRandom(db.account.contacts, 10), []); - const reactFlowWrapper = useRef(null); - const connectingNodeId = useRef(null); - - const initialNodes = [ - { - id: '0', - type: 'user', - position: { x: 141, y: 0 }, - data: { list: [], title: '', pubkey: db.account.pubkey }, - }, - { - id: '1', - type: 'userGroup', - position: { x: 0, y: 200 }, - data: { list: defaultContacts, title: 'Starting Point', pubkey: '' }, - }, - ]; - const initialEdges = [{ id: 'e0-1', type: 'buttonedge', source: '0', target: '1' }]; - - const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); - const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); - - const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []); - - const onConnectStart = useCallback((_, { nodeId }) => { - connectingNodeId.current = nodeId; - }, []); - - const onConnectEnd = useCallback( - async (event) => { - const targetIsPane = event.target.classList.contains('react-flow__pane'); - - if (targetIsPane) { - const { top, left } = reactFlowWrapper.current.getBoundingClientRect(); - - const id = getId(); - const prevData = nodes.slice(-1)[0]; - const randomPubkey = getMultipleRandom(prevData.data.list, 1)[0]; - - const newContactList = await getContactsByPubkey(randomPubkey); - const newNode = { - id, - type: 'userGroup', - position: project({ x: event.clientX - left, y: event.clientY - top }), - data: { list: newContactList, title: null, pubkey: randomPubkey }, - }; - - setNodes((nds) => nds.concat(newNode)); - setEdges((eds) => - eds.concat({ - id, - type: 'buttonedge', - source: connectingNodeId.current, - target: id, - }) - ); - } - }, - [project] - ); - - return ( -
- - - -
- ); -} diff --git a/src/libs/ark/ark.ts b/src/libs/ark/ark.ts index 49290dd7..27d316cb 100644 --- a/src/libs/ark/ark.ts +++ b/src/libs/ark/ark.ts @@ -50,9 +50,15 @@ export class Ark { hashtag: boolean; }; - constructor({ storage }: { storage: Database }) { + constructor({ storage, platform }: { storage: Database; platform: Platform }) { this.#storage = storage; - this.#init(); + this.platform = platform; + this.settings = { + autoupdate: false, + outbox: false, + media: true, + hashtag: true, + }; } async #keyring_save(key: string, value: string) { @@ -74,10 +80,8 @@ export class Ark { } async #initNostrSigner({ nsecbunker }: { nsecbunker?: boolean }) { - if (!this.account) { - this.readyToSign = false; - return null; - } + const account = await this.getActiveAccount(); + this.account = account; try { // NIP-46 Signer @@ -128,7 +132,7 @@ export class Ark { } } - async #init() { + public async init() { const outboxSetting = await this.getSettingValue('outbox'); const bunkerSetting = await this.getSettingValue('nsecbunker'); @@ -178,6 +182,7 @@ export class Ark { this.account.contacts = [...contacts].map((user) => user.pubkey); } + this.relays = [...ndk.pool.relays.values()].map((relay) => relay.url); this.#ndk = ndk; this.#fetcher = fetcher; } @@ -214,10 +219,8 @@ export class Ark { ); if (results.length) { - this.account = results[0]; - this.account.contacts = []; + return results[0]; } else { - console.log('no active account, please create new account'); return null; } } @@ -766,6 +769,72 @@ export class Ark { return false; } + /** + * Return all NIP-04 messages + * @deprecated NIP-04 will be replace by NIP-44 in the next update + */ + public async getAllChats() { + const events = await this.#fetcher.fetchAllEvents( + this.relays, + { + kinds: [NDKKind.EncryptedDirectMessage], + '#p': [this.account.pubkey], + }, + { since: 0 } + ); + + const dedup: NDKEvent[] = Object.values( + events.reduce((ev, { id, content, pubkey, created_at, tags }) => { + if (ev[pubkey]) { + if (ev[pubkey].created_at < created_at) { + ev[pubkey] = { id, content, pubkey, created_at, tags }; + } + } else { + ev[pubkey] = { id, content, pubkey, created_at, tags }; + } + return ev; + }, {}) + ); + + return dedup; + } + + /** + * Return all NIP-04 messages by pubkey + * @deprecated NIP-04 will be replace by NIP-44 in the next update + */ + public async getAllMessagesByPubkey({ pubkey }: { pubkey: string }) { + let senderMessages: NostrEventExt[] = []; + + if (pubkey !== this.account.pubkey) { + senderMessages = await this.#fetcher.fetchAllEvents( + this.relays, + { + kinds: [NDKKind.EncryptedDirectMessage], + authors: [pubkey], + '#p': [this.account.pubkey], + }, + { since: 0 } + ); + } + + const userMessages = await this.#fetcher.fetchAllEvents( + this.relays, + { + kinds: [NDKKind.EncryptedDirectMessage], + authors: [this.account.pubkey], + '#p': [pubkey], + }, + { since: 0 } + ); + + const all = [...senderMessages, ...userMessages].sort( + (a, b) => a.created_at - b.created_at + ); + + return all as unknown as NDKEvent[]; + } + public async nip04Decrypt({ event }: { event: NDKEvent }) { try { const sender = new NDKUser({ diff --git a/src/libs/ark/provider.tsx b/src/libs/ark/provider.tsx index 6f1290fa..4e3ecc4a 100644 --- a/src/libs/ark/provider.tsx +++ b/src/libs/ark/provider.tsx @@ -1,4 +1,5 @@ import { ask } from '@tauri-apps/plugin-dialog'; +import { platform } from '@tauri-apps/plugin-os'; import { relaunch } from '@tauri-apps/plugin-process'; import Database from '@tauri-apps/plugin-sql'; import { check } from '@tauri-apps/plugin-updater'; @@ -26,9 +27,10 @@ const ArkProvider = ({ children }: PropsWithChildren) => { async function initArk() { try { const sqlite = await Database.load('sqlite:lume_v2.db'); - const _ark = new Ark({ storage: sqlite }); + const platformName = await platform(); - if (!_ark.account) await _ark.getActiveAccount(); + const _ark = new Ark({ storage: sqlite, platform: platformName }); + await _ark.init(); const settings = await _ark.getAllSettings(); let autoUpdater = false; diff --git a/src/libs/ndk/cache.ts b/src/libs/ndk/cache.ts deleted file mode 100644 index 53abe434..00000000 --- a/src/libs/ndk/cache.ts +++ /dev/null @@ -1,426 +0,0 @@ -// inspired by: https://github.com/nostr-dev-kit/ndk/tree/master/ndk-cache-dexie -import { NDKEvent, NDKRelay, profileFromEvent } from '@nostr-dev-kit/ndk'; -import type { - Hexpubkey, - NDKCacheAdapter, - NDKFilter, - NDKSubscription, - NDKUserProfile, - NostrEvent, -} from '@nostr-dev-kit/ndk'; -import { LRUCache } from 'lru-cache'; -import { matchFilter } from 'nostr-tools'; - -import { LumeStorage } from '@libs/storage/instance'; - -export default class NDKCacheAdapterTauri implements NDKCacheAdapter { - public db: LumeStorage; - public profiles?: LRUCache; - private dirtyProfiles: Set = new Set(); - readonly locking: boolean; - - constructor(db: LumeStorage) { - this.db = db; - this.locking = true; - - this.profiles = new LRUCache({ - max: 100000, - }); - - setInterval(() => { - this.dumpProfiles(); - }, 1000 * 10); - } - - public async query(subscription: NDKSubscription): Promise { - Promise.allSettled( - subscription.filters.map((filter) => this.processFilter(filter, subscription)) - ); - } - - public async fetchProfile(pubkey: Hexpubkey) { - if (!this.profiles) return null; - - let profile = this.profiles.get(pubkey); - - if (!profile) { - const user = await this.db.getCacheUser(pubkey); - if (user) { - profile = user.profile as NDKUserProfile; - this.profiles.set(pubkey, profile); - } - } - - return profile; - } - - public saveProfile(pubkey: Hexpubkey, profile: NDKUserProfile) { - if (!this.profiles) return; - - this.profiles.set(pubkey, profile); - - this.dirtyProfiles.add(pubkey); - } - - private async processFilter( - filter: NDKFilter, - subscription: NDKSubscription - ): Promise { - const _filter = { ...filter }; - delete _filter.limit; - const filterKeys = Object.keys(_filter || {}).sort(); - - try { - (await this.byKindAndAuthor(filterKeys, filter, subscription)) || - (await this.byAuthors(filterKeys, filter, subscription)) || - (await this.byKinds(filterKeys, filter, subscription)) || - (await this.byIdsQuery(filterKeys, filter, subscription)) || - (await this.byNip33Query(filterKeys, filter, subscription)) || - (await this.byTagsAndOptionallyKinds(filterKeys, filter, subscription)); - } catch (error) { - console.error(error); - } - } - - public async setEvent( - event: NDKEvent, - _filter: NDKFilter, - relay?: NDKRelay - ): Promise { - if (event.kind === 0) { - if (!this.profiles) return; - - const profile: NDKUserProfile = profileFromEvent(event); - this.profiles.set(event.pubkey, profile); - } else { - let addEvent = true; - - if (event.isParamReplaceable()) { - const replaceableId = `${event.kind}:${event.pubkey}:${event.tagId()}`; - const existingEvent = await this.db.getCacheEvent(replaceableId); - if ( - existingEvent && - event.created_at && - existingEvent.createdAt > event.created_at - ) { - addEvent = false; - } - } - - if (addEvent) { - this.db.setCacheEvent({ - id: event.tagId(), - pubkey: event.pubkey, - content: event.content, - kind: event.kind!, - createdAt: event.created_at!, - relay: relay?.url, - event: JSON.stringify(event.rawEvent()), - }); - - // Don't cache contact lists as tags since it's expensive - // and there is no use case for it - if (event.kind !== 3) { - event.tags.forEach((tag) => { - if (tag[0].length !== 1) return; - - this.db.setCacheEventTag({ - id: `${event.id}:${tag[0]}:${tag[1]}`, - eventId: event.id, - tag: tag[0], - value: tag[1], - tagValue: tag[0] + tag[1], - }); - }); - } - } - } - } - - /** - * Searches by authors - */ - private async byAuthors( - filterKeys: string[], - filter: NDKFilter, - subscription: NDKSubscription - ): Promise { - const f = ['authors']; - const hasAllKeys = - filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); - - let foundEvents = false; - - if (hasAllKeys && filter.authors) { - for (const pubkey of filter.authors) { - const events = await this.db.getCacheEventsByPubkey(pubkey); - for (const event of events) { - let rawEvent: NostrEvent; - try { - rawEvent = JSON.parse(event.event); - } catch (e) { - console.log('failed to parse event', e); - continue; - } - - const ndkEvent = new NDKEvent(undefined, rawEvent); - const relay = event.relay ? new NDKRelay(event.relay) : undefined; - subscription.eventReceived(ndkEvent, relay, true); - foundEvents = true; - } - } - } - return foundEvents; - } - - /** - * Searches by kinds - */ - private async byKinds( - filterKeys: string[], - filter: NDKFilter, - subscription: NDKSubscription - ): Promise { - const f = ['kinds']; - const hasAllKeys = - filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); - - let foundEvents = false; - - if (hasAllKeys && filter.kinds) { - for (const kind of filter.kinds) { - const events = await this.db.getCacheEventsByKind(kind); - for (const event of events) { - let rawEvent: NostrEvent; - try { - rawEvent = JSON.parse(event.event); - } catch (e) { - console.log('failed to parse event', e); - continue; - } - - const ndkEvent = new NDKEvent(undefined, rawEvent); - const relay = event.relay ? new NDKRelay(event.relay) : undefined; - subscription.eventReceived(ndkEvent, relay, true); - foundEvents = true; - } - } - } - return foundEvents; - } - - /** - * Searches by ids - */ - private async byIdsQuery( - filterKeys: string[], - filter: NDKFilter, - subscription: NDKSubscription - ): Promise { - const f = ['ids']; - const hasAllKeys = - filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); - - if (hasAllKeys && filter.ids) { - for (const id of filter.ids) { - const event = await this.db.getCacheEvent(id); - if (!event) continue; - - let rawEvent: NostrEvent; - try { - rawEvent = JSON.parse(event.event); - } catch (e) { - console.log('failed to parse event', e); - continue; - } - - const ndkEvent = new NDKEvent(undefined, rawEvent); - const relay = event.relay ? new NDKRelay(event.relay) : undefined; - subscription.eventReceived(ndkEvent, relay, true); - } - - return true; - } - - return false; - } - - /** - * Searches by NIP-33 - */ - private async byNip33Query( - filterKeys: string[], - filter: NDKFilter, - subscription: NDKSubscription - ): Promise { - const f = ['#d', 'authors', 'kinds']; - const hasAllKeys = - filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); - - if (hasAllKeys && filter.kinds && filter.authors) { - for (const kind of filter.kinds) { - const replaceableKind = kind >= 30000 && kind < 40000; - - if (!replaceableKind) continue; - - for (const author of filter.authors) { - for (const dTag of filter['#d']) { - const replaceableId = `${kind}:${author}:${dTag}`; - const event = await this.db.getCacheEvent(replaceableId); - if (!event) continue; - - let rawEvent: NostrEvent; - try { - rawEvent = JSON.parse(event.event); - } catch (e) { - console.log('failed to parse event', e); - continue; - } - - const ndkEvent = new NDKEvent(undefined, rawEvent); - const relay = event.relay ? new NDKRelay(event.relay) : undefined; - subscription.eventReceived(ndkEvent, relay, true); - } - } - } - return true; - } - return false; - } - - /** - * Searches by kind & author - */ - private async byKindAndAuthor( - filterKeys: string[], - filter: NDKFilter, - subscription: NDKSubscription - ): Promise { - const f = ['authors', 'kinds']; - const hasAllKeys = - filterKeys.length === f.length && f.every((k) => filterKeys.includes(k)); - let foundEvents = false; - - if (!hasAllKeys) return false; - - if (filter.kinds && filter.authors) { - for (const kind of filter.kinds) { - for (const author of filter.authors) { - const events = await this.db.getCacheEventsByKindAndAuthor(kind, author); - - for (const event of events) { - let rawEvent: NostrEvent; - try { - rawEvent = JSON.parse(event.event); - } catch (e) { - console.log('failed to parse event', e); - continue; - } - - const ndkEvent = new NDKEvent(undefined, rawEvent); - const relay = event.relay ? new NDKRelay(event.relay) : undefined; - subscription.eventReceived(ndkEvent, relay, true); - foundEvents = true; - } - } - } - } - return foundEvents; - } - - /** - * Searches by tags and optionally filters by tags - */ - private async byTagsAndOptionallyKinds( - filterKeys: string[], - filter: NDKFilter, - subscription: NDKSubscription - ): Promise { - for (const filterKey of filterKeys) { - const isKind = filterKey === 'kinds'; - const isTag = filterKey.startsWith('#') && filterKey.length === 2; - - if (!isKind && !isTag) return false; - } - - const events = await this.filterByTag(filterKeys, filter); - const kinds = filter.kinds as number[]; - - for (const event of events) { - if (!kinds?.includes(event.kind!)) continue; - - subscription.eventReceived(event, undefined, true); - } - - return false; - } - - private async filterByTag( - filterKeys: string[], - filter: NDKFilter - ): Promise { - const retEvents: NDKEvent[] = []; - - for (const filterKey of filterKeys) { - if (filterKey.length !== 2) continue; - const tag = filterKey.slice(1); - // const values = filter[filterKey] as string[]; - const values: string[] = []; - for (const [key, value] of Object.entries(filter)) { - if (key === filterKey) values.push(value as string); - } - - for (const value of values) { - const eventTags = await this.db.getCacheEventTagsByTagValue(tag + value); - if (!eventTags.length) continue; - - const eventIds = eventTags.map((t) => t.eventId); - - const events = await this.db.getCacheEvents(eventIds); - for (const event of events) { - let rawEvent; - try { - rawEvent = JSON.parse(event.event); - - // Make sure all passed filters match the event - if (!matchFilter(filter, rawEvent)) continue; - } catch (e) { - console.log('failed to parse event', e); - continue; - } - - const ndkEvent = new NDKEvent(undefined, rawEvent); - const relay = event.relay ? new NDKRelay(event.relay) : undefined; - ndkEvent.relay = relay; - retEvents.push(ndkEvent); - } - } - } - - return retEvents; - } - - private async dumpProfiles(): Promise { - const profiles = []; - - if (!this.profiles) return; - - for (const pubkey of this.dirtyProfiles) { - const profile = this.profiles.get(pubkey); - - if (!profile) continue; - - profiles.push({ - pubkey, - profile: JSON.stringify(profile), - createdAt: Date.now(), - }); - } - - if (profiles.length) { - await this.db.setCacheProfiles(profiles); - } - - this.dirtyProfiles.clear(); - } -} diff --git a/src/libs/ndk/instance.ts b/src/libs/ndk/instance.ts deleted file mode 100644 index 6f95c5aa..00000000 --- a/src/libs/ndk/instance.ts +++ /dev/null @@ -1,214 +0,0 @@ -import NDK, { - NDKEvent, - NDKKind, - NDKNip46Signer, - NDKPrivateKeySigner, -} from '@nostr-dev-kit/ndk'; -import { ndkAdapter } from '@nostr-fetch/adapter-ndk'; -import { useQueryClient } from '@tanstack/react-query'; -import { ask } from '@tauri-apps/plugin-dialog'; -import { relaunch } from '@tauri-apps/plugin-process'; -import { NostrFetcher, normalizeRelayUrlSet } from 'nostr-fetch'; -import { useEffect, useState } from 'react'; -import { toast } from 'sonner'; - -import NDKCacheAdapterTauri from '@libs/ndk/cache'; -import { useStorage } from '@libs/storage/provider'; - -import { FETCH_LIMIT } from '@utils/constants'; - -export const NDKInstance = () => { - const { db } = useStorage(); - const queryClient = useQueryClient(); - - const [ndk, setNDK] = useState(undefined); - const [fetcher, setFetcher] = useState(undefined); - const [relayUrls, setRelayUrls] = useState([]); - - async function getSigner(nsecbunker?: boolean) { - if (!db.account) return; - - try { - // NIP-46 Signer - if (nsecbunker) { - const localSignerPrivkey = await db.secureLoad(`${db.account.id}-nsecbunker`); - if (!localSignerPrivkey) return null; - - const localSigner = new NDKPrivateKeySigner(localSignerPrivkey); - const bunker = new NDK({ - explicitRelayUrls: ['wss://relay.nsecbunker.com', 'wss://nostr.vulpem.com'], - }); - await bunker.connect(); - - const remoteSigner = new NDKNip46Signer(bunker, db.account.pubkey, localSigner); - await remoteSigner.blockUntilReady(); - - return remoteSigner; - } - - // Privkey Signer - const userPrivkey = await db.secureLoad(db.account.pubkey); - if (!userPrivkey) return null; - return new NDKPrivateKeySigner(userPrivkey); - } catch (e) { - console.log(e); - if (e === 'Token already redeemed') { - toast.info( - 'nsecbunker token already redeemed. You need to re-login with another token.' - ); - - await db.secureRemove(`${db.account.pubkey}-nsecbunker`); - await db.accountLogout(); - } - - return null; - } - } - - async function initNDK() { - const outboxSetting = await db.getSettingValue('outbox'); - const bunkerSetting = await db.getSettingValue('nsecbunker'); - - const bunker = !!parseInt(bunkerSetting); - const outbox = !!parseInt(outboxSetting); - - const explicitRelayUrls = normalizeRelayUrlSet([ - 'wss://relay.damus.io', - 'wss://relay.nostr.band', - 'wss://nos.lol', - 'wss://nostr.mutinywallet.com', - ]); - - // #TODO: user should config outbox relays - const outboxRelayUrls = normalizeRelayUrlSet(['wss://purplepag.es']); - - // #TODO: user should config blacklist relays - const blacklistRelayUrls = normalizeRelayUrlSet(['wss://brb.io']); - - try { - const tauriAdapter = new NDKCacheAdapterTauri(db); - const instance = new NDK({ - explicitRelayUrls, - outboxRelayUrls, - blacklistRelayUrls, - enableOutboxModel: outbox, - autoConnectUserRelays: true, - autoFetchUserMutelist: true, - cacheAdapter: tauriAdapter, - // clientName: 'Lume', - // clientNip89: '', - }); - - // add signer if exist - const signer = await getSigner(bunker); - if (signer) instance.signer = signer; - - // connect - await instance.connect(); - const _fetcher = NostrFetcher.withCustomPool(ndkAdapter(instance)); - - // update account's metadata - if (db.account) { - const user = instance.getUser({ pubkey: db.account.pubkey }); - instance.activeUser = user; - - const contacts = await user.follows(undefined /* outbox */); - db.account.contacts = [...contacts].map((user) => user.pubkey); - - // prefetch newsfeed - await queryClient.prefetchInfiniteQuery({ - queryKey: ['newsfeed'], - initialPageParam: 0, - queryFn: async ({ - signal, - pageParam, - }: { - signal: AbortSignal; - pageParam: number; - }) => { - const rootIds = new Set(); - const dedupQueue = new Set(); - - const events = await _fetcher.fetchLatestEvents( - explicitRelayUrls, - { - kinds: [NDKKind.Text, NDKKind.Repost], - authors: db.account.contacts, - }, - FETCH_LIMIT, - { asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal } - ); - - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); - }); - - ndkEvents.forEach((event) => { - const tags = event.tags.filter((el) => el[0] === 'e'); - if (tags && tags.length > 0) { - const rootId = tags.filter((el) => el[3] === 'root')[1] ?? tags[0][1]; - if (rootIds.has(rootId)) return dedupQueue.add(event.id); - rootIds.add(rootId); - } - }); - - return ndkEvents - .filter((event) => !dedupQueue.has(event.id)) - .sort((a, b) => b.created_at - a.created_at); - }, - }); - - // prefetch notification - await queryClient.prefetchInfiniteQuery({ - queryKey: ['notification'], - initialPageParam: 0, - queryFn: async ({ - signal, - pageParam, - }: { - signal: AbortSignal; - pageParam: number; - }) => { - const events = await _fetcher.fetchLatestEvents( - explicitRelayUrls, - { - kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], - '#p': [db.account.pubkey], - }, - FETCH_LIMIT, - { asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal } - ); - - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); - }); - - return ndkEvents.sort((a, b) => b.created_at - a.created_at); - }, - }); - } - - setNDK(instance); - setFetcher(_fetcher); - setRelayUrls(explicitRelayUrls); - } catch (e) { - console.error(e); - const yes = await ask(e, { - title: 'Lume', - type: 'error', - okLabel: 'Yes', - }); - if (yes) relaunch(); - } - } - - useEffect(() => { - if (!ndk) initNDK(); - }, []); - - return { - ndk, - fetcher, - relayUrls, - }; -}; diff --git a/src/libs/ndk/provider.tsx b/src/libs/ndk/provider.tsx deleted file mode 100644 index 7ad2fe56..00000000 --- a/src/libs/ndk/provider.tsx +++ /dev/null @@ -1,80 +0,0 @@ -// source: https://github.com/nostr-dev-kit/ndk-react/ -import NDK from '@nostr-dev-kit/ndk'; -import Markdown from 'markdown-to-jsx'; -import { NostrFetcher } from 'nostr-fetch'; -import { PropsWithChildren, createContext, useContext } from 'react'; - -import { NDKInstance } from '@libs/ndk/instance'; - -import { LoaderIcon } from '@shared/icons'; - -import { QUOTES } from '@utils/constants'; - -interface NDKContext { - ndk: undefined | NDK; - fetcher: undefined | NostrFetcher; - relayUrls: string[]; -} - -const NDKContext = createContext({ - ndk: undefined, - fetcher: undefined, - relayUrls: [], -}); - -const NDKProvider = ({ children }: PropsWithChildren) => { - const { ndk, relayUrls, fetcher } = NDKInstance(); - - if (!ndk) - return ( -
-
-
TIP:
- - {QUOTES[Math.floor(Math.random() * QUOTES.length)]} - -
-
- -

Connecting to relays...

-
-
- ); - - return ( - - {children} - - ); -}; - -const useNDK = () => { - const context = useContext(NDKContext); - if (context === undefined) { - throw new Error('import NDKProvider to use useNDK'); - } - return context; -}; - -export { NDKProvider, useNDK }; diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts deleted file mode 100644 index 7a91ff9e..00000000 --- a/src/libs/storage/instance.ts +++ /dev/null @@ -1,486 +0,0 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; -import { invoke } from '@tauri-apps/api/primitives'; -import { Platform } from '@tauri-apps/plugin-os'; -import Database from '@tauri-apps/plugin-sql'; - -import { rawEvent } from '@utils/transform'; -import type { - Account, - DBEvent, - NDKCacheEvent, - NDKCacheEventTag, - NDKCacheUser, - NDKCacheUserProfile, - Relays, - Widget, -} from '@utils/types'; - -export class LumeStorage { - public db: Database; - public account: Account | null; - public platform: Platform | null; - public settings: { - autoupdate: boolean; - outbox: boolean; - media: boolean; - hashtag: boolean; - }; - - constructor(sqlite: Database, platform: Platform) { - this.db = sqlite; - this.account = null; - this.platform = platform; - this.settings = { autoupdate: false, outbox: false, media: true, hashtag: true }; - } - - public async secureSave(key: string, value: string) { - return await invoke('secure_save', { key, value }); - } - - public async secureLoad(key: string) { - try { - const value: string = await invoke('secure_load', { key }); - if (!value) return null; - return value; - } catch { - return null; - } - } - - public async secureRemove(key: string) { - return await invoke('secure_remove', { key }); - } - - public async getAllCacheUsers() { - const results: Array = await this.db.select( - 'SELECT * FROM ndk_users ORDER BY createdAt DESC;' - ); - - if (!results.length) return []; - - const users: NDKCacheUserProfile[] = results.map((item) => ({ - pubkey: item.pubkey, - ...JSON.parse(item.profile as string), - })); - return users; - } - - public async getCacheUser(pubkey: string) { - const results: Array = await this.db.select( - 'SELECT * FROM ndk_users WHERE pubkey = $1 ORDER BY pubkey DESC LIMIT 1;', - [pubkey] - ); - - if (!results.length) return null; - - if (typeof results[0].profile === 'string') - results[0].profile = JSON.parse(results[0].profile); - - return results[0]; - } - - public async getCacheEvent(id: string) { - const results: Array = await this.db.select( - 'SELECT * FROM ndk_events WHERE id = $1 ORDER BY id DESC LIMIT 1;', - [id] - ); - - if (!results.length) return null; - return results[0]; - } - - public async getCacheEvents(ids: string[]) { - const idsArr = `'${ids.join("','")}'`; - - const results: Array = await this.db.select( - `SELECT * FROM ndk_events WHERE id IN (${idsArr}) ORDER BY id;` - ); - - if (!results.length) return []; - return results; - } - - public async getCacheEventsByPubkey(pubkey: string) { - const results: Array = await this.db.select( - 'SELECT * FROM ndk_events WHERE pubkey = $1 ORDER BY id;', - [pubkey] - ); - - if (!results.length) return []; - return results; - } - - public async getCacheEventsByKind(kind: number) { - const results: Array = await this.db.select( - 'SELECT * FROM ndk_events WHERE kind = $1 ORDER BY id;', - [kind] - ); - - if (!results.length) return []; - return results; - } - - public async getCacheEventsByKindAndAuthor(kind: number, pubkey: string) { - const results: Array = await this.db.select( - 'SELECT * FROM ndk_events WHERE kind = $1 AND pubkey = $2 ORDER BY id;', - [kind, pubkey] - ); - - if (!results.length) return []; - return results; - } - - public async getCacheEventTagsByTagValue(tagValue: string) { - const results: Array = await this.db.select( - 'SELECT * FROM ndk_eventtags WHERE tagValue = $1 ORDER BY id;', - [tagValue] - ); - - if (!results.length) return []; - return results; - } - - public async setCacheEvent({ - id, - pubkey, - content, - kind, - createdAt, - relay, - event, - }: NDKCacheEvent) { - return await this.db.execute( - 'INSERT OR IGNORE INTO ndk_events (id, pubkey, content, kind, createdAt, relay, event) VALUES ($1, $2, $3, $4, $5, $6, $7);', - [id, pubkey, content, kind, createdAt, relay, event] - ); - } - - public async setCacheEventTag({ id, eventId, tag, value, tagValue }: NDKCacheEventTag) { - return await this.db.execute( - 'INSERT OR IGNORE INTO ndk_eventtags (id, eventId, tag, value, tagValue) VALUES ($1, $2, $3, $4, $5);', - [id, eventId, tag, value, tagValue] - ); - } - - public async setCacheProfiles(profiles: Array) { - return await Promise.all( - profiles.map( - async (profile) => - await this.db.execute( - 'INSERT OR IGNORE INTO ndk_users (pubkey, profile, createdAt) VALUES ($1, $2, $3);', - [profile.pubkey, profile.profile, profile.createdAt] - ) - ) - ); - } - - public async checkAccount() { - const result: Array<{ total: string }> = await this.db.select( - 'SELECT COUNT(*) AS "total" FROM accounts WHERE is_active = "1" ORDER BY id DESC LIMIT 1;' - ); - return parseInt(result[0].total); - } - - public async getActiveAccount() { - const results: Array = await this.db.select( - 'SELECT * FROM accounts WHERE is_active = "1" ORDER BY id DESC LIMIT 1;' - ); - - if (results.length) { - this.account = results[0]; - this.account.contacts = []; - } else { - console.log('no active account, please create new account'); - return null; - } - } - - public async createAccount(npub: string, pubkey: string) { - const existAccounts: Array = await this.db.select( - 'SELECT * FROM accounts WHERE pubkey = $1 ORDER BY id DESC LIMIT 1;', - [pubkey] - ); - - if (existAccounts.length) { - await this.db.execute("UPDATE accounts SET is_active = '1' WHERE pubkey = $1;", [ - pubkey, - ]); - } else { - await this.db.execute( - 'INSERT OR IGNORE INTO accounts (id, pubkey, is_active) VALUES ($1, $2, $3);', - [npub, pubkey, 1] - ); - } - - return await this.getActiveAccount(); - } - - public async updateAccount(column: string, value: string) { - const insert = await this.db.execute( - `UPDATE accounts SET ${column} = $1 WHERE id = $2;`, - [value, this.account.id] - ); - - if (insert) { - const account = await this.getActiveAccount(); - return account; - } - } - - public async getWidgets() { - const widgets: Array = await this.db.select( - 'SELECT * FROM widgets WHERE account_id = $1 ORDER BY created_at DESC;', - [this.account.id] - ); - return widgets; - } - - public async createWidget(kind: number, title: string, content: string | string[]) { - const insert = await this.db.execute( - 'INSERT INTO widgets (account_id, kind, title, content) VALUES ($1, $2, $3, $4);', - [this.account.id, kind, title, content] - ); - - if (insert) { - const widgets: Array = await this.db.select( - 'SELECT * FROM widgets ORDER BY id DESC LIMIT 1;' - ); - if (widgets.length < 1) console.error('get created widget failed'); - return widgets[0]; - } else { - console.error('create widget failed'); - } - } - - public async removeWidget(id: string) { - const res = await this.db.execute('DELETE FROM widgets WHERE id = $1;', [id]); - if (res) return id; - } - - public async createEvent(event: NDKEvent) { - const rawNostrEvent = rawEvent(event); - - 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]; - } - - return await this.db.execute( - 'INSERT OR IGNORE INTO events (id, account_id, event, author, kind, root_id, reply_id, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8);', - [ - event.id, - this.account.id, - JSON.stringify(rawNostrEvent), - event.pubkey, - event.kind, - root, - reply, - event.created_at, - ] - ); - } - - public async getEventByID(id: string) { - const results: DBEvent[] = await this.db.select( - 'SELECT * FROM events WHERE id = $1 LIMIT 1;', - [id] - ); - - if (results.length < 1) return null; - return JSON.parse(results[0].event as string) as NDKEvent; - } - - public async countTotalEvents() { - const result: Array<{ total: string }> = await this.db.select( - 'SELECT COUNT(*) AS "total" FROM events WHERE account_id = $1;', - [this.account.id] - ); - return parseInt(result[0].total); - } - - public async getAllEvents(limit: number, offset: number) { - const totalEvents = await this.countTotalEvents(); - const nextCursor = offset + limit; - - const events: { data: DBEvent[] | null; nextCursor: number } = { - data: null, - nextCursor: 0, - }; - - const query: DBEvent[] = await this.db.select( - 'SELECT * FROM events WHERE account_id = $1 GROUP BY root_id ORDER BY created_at DESC LIMIT $2 OFFSET $3;', - [this.account.id, limit, offset] - ); - - if (query && query.length > 0) { - events['data'] = query; - events['nextCursor'] = - Math.round(totalEvents / nextCursor) > 1 ? nextCursor : undefined; - - return events; - } - - return { - data: [], - nextCursor: 0, - }; - } - - public async getAllEventsByAuthors(authors: string[], limit: number, offset: number) { - const totalEvents = await this.countTotalEvents(); - const nextCursor = offset + limit; - const authorsArr = `'${authors.join("','")}'`; - - const events: { data: DBEvent[] | null; nextCursor: number } = { - data: null, - nextCursor: 0, - }; - - const query: DBEvent[] = await this.db.select( - `SELECT * FROM events WHERE author IN (${authorsArr}) ORDER BY created_at DESC LIMIT $1 OFFSET $2;`, - [limit, offset] - ); - - if (query && query.length > 0) { - events['data'] = query; - events['nextCursor'] = - Math.round(totalEvents / nextCursor) > 1 ? nextCursor : undefined; - - return events; - } - - return { - data: [], - nextCursor: 0, - }; - } - - public async getAllEventsByKinds(kinds: number[], limit: number, offset: number) { - const totalEvents = await this.countTotalEvents(); - const nextCursor = offset + limit; - const authorsArr = `'${kinds.join("','")}'`; - - const events: { data: DBEvent[] | null; nextCursor: number } = { - data: null, - nextCursor: 0, - }; - - const query: DBEvent[] = await this.db.select( - `SELECT * FROM events WHERE kinds IN (${authorsArr}) AND account_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3;`, - [this.account.id, limit, offset] - ); - - if (query && query.length > 0) { - events['data'] = query; - events['nextCursor'] = - Math.round(totalEvents / nextCursor) > 1 ? nextCursor : undefined; - - return events; - } - - return { - data: [], - nextCursor: 0, - }; - } - - public async isEventsEmpty() { - const results: DBEvent[] = await this.db.select( - 'SELECT * FROM events WHERE account_id = $1 ORDER BY id DESC LIMIT 1;', - [this.account.id] - ); - - return results.length < 1; - } - - public async createRelay(relay: string, purpose?: string) { - const existRelays: Relays[] = await this.db.select( - 'SELECT * FROM relays WHERE relay = $1 AND account_id = $2 ORDER BY id DESC LIMIT 1;', - [relay, this.account.id] - ); - - if (existRelays.length) return; - - return await this.db.execute( - 'INSERT OR IGNORE INTO relays (account_id, relay, purpose) VALUES ($1, $2, $3);', - [this.account.id, relay, purpose || ''] - ); - } - - public async removeRelay(relay: string) { - return await this.db.execute(`DELETE FROM relays WHERE relay = "${relay}";`); - } - - public async createSetting(key: string, value: string | undefined) { - if (value) { - return await this.db.execute( - 'INSERT OR IGNORE INTO settings (key, value) VALUES ($1, $2);', - [key, value] - ); - } - - const currentSetting = await this.checkSettingValue(key); - - if (!currentSetting) - return await this.db.execute( - 'INSERT OR IGNORE INTO settings (key, value) VALUES ($1, $2);', - [key, value] - ); - - const currentValue = !!parseInt(currentSetting); - - return await this.db.execute('UPDATE settings SET value = $1 WHERE key = $2;', [ - +!currentValue, - key, - ]); - } - - public async getAllSettings() { - const results: { key: string; value: string }[] = await this.db.select( - 'SELECT * FROM settings ORDER BY id DESC;' - ); - if (results.length < 1) return null; - return results; - } - - public async checkSettingValue(key: string) { - const results: { key: string; value: string }[] = await this.db.select( - 'SELECT * FROM settings WHERE key = $1 ORDER BY id DESC LIMIT 1;', - [key] - ); - if (!results.length) return false; - return results[0].value; - } - - public async getSettingValue(key: string) { - const results: { key: string; value: string }[] = await this.db.select( - 'SELECT * FROM settings WHERE key = $1 ORDER BY id DESC LIMIT 1;', - [key] - ); - if (!results.length) return '0'; - return results[0].value; - } - - public async clearCache() { - await this.db.execute('DELETE FROM ndk_events;'); - await this.db.execute('DELETE FROM ndk_eventtags;'); - await this.db.execute('DELETE FROM ndk_users;'); - } - - public async accountLogout() { - // update current account status - await this.db.execute("UPDATE accounts SET is_active = '0' WHERE id = $1;", [ - this.account.id, - ]); - this.account = null; - } - - public async close() { - return this.db.close(); - } -} diff --git a/src/libs/storage/provider.tsx b/src/libs/storage/provider.tsx deleted file mode 100644 index a2bfa4ac..00000000 --- a/src/libs/storage/provider.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { message } from '@tauri-apps/plugin-dialog'; -import { platform } from '@tauri-apps/plugin-os'; -import { relaunch } from '@tauri-apps/plugin-process'; -import Database from '@tauri-apps/plugin-sql'; -import { check } from '@tauri-apps/plugin-updater'; -import Markdown from 'markdown-to-jsx'; -import { PropsWithChildren, createContext, useContext, useEffect, useState } from 'react'; - -import { LumeStorage } from '@libs/storage/instance'; - -import { LoaderIcon } from '@shared/icons'; - -import { QUOTES } from '@utils/constants'; - -interface StorageContext { - db: LumeStorage; -} - -const StorageContext = createContext({ - db: undefined, -}); - -const StorageInstance = () => { - const [db, setDB] = useState(undefined); - const [isNewVersion, setIsNewVersion] = useState(false); - - const initLumeStorage = async () => { - try { - const sqlite = await Database.load('sqlite:lume_v2.db'); - const platformName = await platform(); - - const lumeStorage = new LumeStorage(sqlite, platformName); - if (!lumeStorage.account) await lumeStorage.getActiveAccount(); - - const settings = await lumeStorage.getAllSettings(); - let autoUpdater = false; - - if (settings) { - settings.forEach((item) => { - if (item.key === 'outbox') lumeStorage.settings.outbox = !!parseInt(item.value); - - if (item.key === 'media') lumeStorage.settings.media = !!parseInt(item.value); - - if (item.key === 'hashtag') - lumeStorage.settings.hashtag = !!parseInt(item.value); - - if (item.key === 'autoupdate') { - if (parseInt(item.value)) autoUpdater = true; - } - }); - } - - if (autoUpdater) { - // check update - const update = await check(); - // install new version - if (update) { - setIsNewVersion(true); - - await update.downloadAndInstall(); - await relaunch(); - } - } - - setDB(lumeStorage); - } catch (e) { - await message(`Cannot initialize database: ${e}`, { - title: 'Lume', - type: 'error', - }); - } - }; - - useEffect(() => { - if (!db) initLumeStorage(); - }, []); - - return { db, isNewVersion }; -}; - -const StorageProvider = ({ children }: PropsWithChildren) => { - const { db, isNewVersion } = StorageInstance(); - - if (!db) - return ( -
-
-
TIP:
- - {QUOTES[Math.floor(Math.random() * QUOTES.length)]} - -
-
- -

- {isNewVersion ? 'Found a new version, updating...' : 'Starting...'} -

-
-
- ); - - return {children}; -}; - -const useStorage = () => { - const context = useContext(StorageContext); - if (context === undefined) { - throw new Error('Storage not found'); - } - return context; -}; - -export { StorageProvider, useStorage }; diff --git a/src/shared/notes/repost.tsx b/src/shared/notes/repost.tsx index dbab01ae..f8d24907 100644 --- a/src/shared/notes/repost.tsx +++ b/src/shared/notes/repost.tsx @@ -15,24 +15,21 @@ import { User } from '@shared/user'; export function Repost({ event }: { event: NDKEvent }) { const { ark } = useArk(); - const { status, data: repostEvent } = useQuery({ + const { + isLoading, + isError, + data: repostEvent, + } = useQuery({ queryKey: ['repost', event.id], queryFn: async () => { try { - let event: NDKEvent = undefined; - if (event.content.length > 50) { const embed = JSON.parse(event.content) as NostrEvent; - event = ark.createNDKEvent({ event: embed }); + return ark.createNDKEvent({ event: embed }); } const id = event.tags.find((el) => el[0] === 'e')[1]; - if (!id) throw new Error('Failed to get repost event id'); - - event = await ark.getEventById({ id }); - - if (!event) return Promise.reject(new Error('Failed to get repost event')); - return event; + return await ark.getEventById({ id }); } catch { throw new Error('Failed to get repost event'); } @@ -54,7 +51,7 @@ export function Repost({ event }: { event: NDKEvent }) { } }; - if (status === 'pending') { + if (isLoading) { return (
@@ -62,6 +59,21 @@ export function Repost({ event }: { event: NDKEvent }) { ); } + if (isError) { + return ( +
+
+ +
+
+

Failed to load event

+
+
+
+
+ ); + } + return (
diff --git a/src/shared/widgets/newsfeed.tsx b/src/shared/widgets/newsfeed.tsx index 5ed78a79..d87f83c3 100644 --- a/src/shared/widgets/newsfeed.tsx +++ b/src/shared/widgets/newsfeed.tsx @@ -33,7 +33,9 @@ export function NewsfeedWidget() { const events = await ark.getInfiniteEvents({ filter: { kinds: [NDKKind.Text, NDKKind.Repost], - authors: ark.account.contacts, + authors: !ark.account.contacts.length + ? [ark.account.pubkey] + : ark.account.contacts, }, limit: FETCH_LIMIT, pageParam, diff --git a/src/shared/widgets/notification.tsx b/src/shared/widgets/notification.tsx index 14ef5d6e..d5fcbf46 100644 --- a/src/shared/widgets/notification.tsx +++ b/src/shared/widgets/notification.tsx @@ -1,11 +1,9 @@ -import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; +import { NDKEvent, NDKKind, NDKSubscription } from '@nostr-dev-kit/ndk'; import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useMemo } from 'react'; import { VList } from 'virtua'; import { useArk } from '@libs/ark'; -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { MemoizedNotifyNote, NoteSkeleton } from '@shared/notes'; @@ -30,21 +28,17 @@ export function NotificationWidget() { signal: AbortSignal; pageParam: number; }) => { - const events = await fetcher.fetchLatestEvents( - relayUrls, - { + const events = await ark.getInfiniteEvents({ + filter: { kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], - '#p': [db.account.pubkey], + '#p': [ark.account.pubkey], }, - FETCH_LIMIT, - { asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal } - ); - - const ndkEvents = events.map((event) => { - return new NDKEvent(ndk, event); + limit: FETCH_LIMIT, + pageParam, + signal, }); - return ndkEvents.sort((a, b) => b.created_at - a.created_at); + return events; }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); @@ -63,21 +57,24 @@ export function NotificationWidget() { ); const renderEvent = useCallback((event: NDKEvent) => { - if (event.pubkey === db.account.pubkey) return null; + if (event.pubkey === ark.account.pubkey) return null; return ; }, []); useEffect(() => { - if (status === 'success' && db.account) { + let sub: NDKSubscription = undefined; + + if (status === 'success' && ark.account) { const filter = { kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], - '#p': [db.account.pubkey], + '#p': [ark.account.pubkey], since: Math.floor(Date.now() / 1000), }; - sub( + sub = ark.subscribe({ filter, - async (event) => { + closeOnEose: false, + cb: async (event) => { queryClient.setQueryData( ['notification'], (prev: { pageParams: number; pages: Array }) => ({ @@ -86,21 +83,18 @@ export function NotificationWidget() { }) ); - const user = ndk.getUser({ pubkey: event.pubkey }); - await user.fetchProfile(); + const profile = await ark.getUserProfile({ pubkey: event.pubkey }); switch (event.kind) { case NDKKind.Text: return await sendNativeNotification( - `${ - user.profile.displayName || user.profile.name - } has replied to your note` + `${profile.displayName || profile.name} has replied to your note` ); case NDKKind.EncryptedDirectMessage: { if (location.pathname !== '/chats') { return await sendNativeNotification( `${ - user.profile.displayName || user.profile.name + profile.displayName || profile.name } has send you a encrypted message` ); } else { @@ -109,28 +103,28 @@ export function NotificationWidget() { } case NDKKind.Repost: return await sendNativeNotification( - `${ - user.profile.displayName || user.profile.name - } has reposted to your note` + `${profile.displayName || profile.name} has reposted to your note` ); case NDKKind.Reaction: return await sendNativeNotification( - `${user.profile.displayName || user.profile.name} has reacted ${ + `${profile.displayName || profile.name} has reacted ${ event.content } to your note` ); case NDKKind.Zap: return await sendNativeNotification( - `${user.profile.displayName || user.profile.name} has zapped to your note` + `${profile.displayName || profile.name} has zapped to your note` ); default: break; } }, - false, - 'notification' - ); + }); } + + return () => { + if (sub) sub.stop(); + }; }, [status]); return ( From 6440680898fe2e684cc2ffee06ba358025ba54c8 Mon Sep 17 00:00:00 2001 From: reya Date: Sat, 9 Dec 2023 09:34:20 +0700 Subject: [PATCH 6/8] fix ark --- src/app/auth/create.tsx | 7 ++++++- src/app/auth/follow.tsx | 4 ++-- src/app/auth/import.tsx | 2 +- src/libs/ark/ark.ts | 30 +++++++++++++++++++++++------- src/utils/shortenKey.ts | 2 +- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/app/auth/create.tsx b/src/app/auth/create.tsx index a1ebd64c..100ac88d 100644 --- a/src/app/auth/create.tsx +++ b/src/app/auth/create.tsx @@ -70,7 +70,12 @@ export function CreateAccountScreen() { }); if (publish) { - await ark.createAccount(userNpub, userPubkey, userPrivkey); + await ark.createAccount({ + id: userNpub, + pubkey: userPubkey, + privkey: userPrivkey, + }); + await ark.createEvent({ kind: NDKKind.RelayList, tags: [ark.relays], diff --git a/src/app/auth/follow.tsx b/src/app/auth/follow.tsx index 6805f6c7..79b5c2ba 100644 --- a/src/app/auth/follow.tsx +++ b/src/app/auth/follow.tsx @@ -69,14 +69,14 @@ export function FollowScreen() { const publish = await ark.createEvent({ kind: NDKKind.Contacts, tags: follows.map((item) => { - if (item.startsWith('npub')) return ['p', nip19.decode(item).data as string]; + if (item.startsWith('npub1')) return ['p', nip19.decode(item).data as string]; return ['p', item]; }), }); if (publish) { ark.account.contacts = follows.map((item) => { - if (item.startsWith('npub')) return nip19.decode(item).data as string; + if (item.startsWith('npub1')) return nip19.decode(item).data as string; return item; }); diff --git a/src/app/auth/import.tsx b/src/app/auth/import.tsx index 4ba02515..541d0591 100644 --- a/src/app/auth/import.tsx +++ b/src/app/auth/import.tsx @@ -76,7 +76,7 @@ export function ImportAccountScreen() { setLoading(true); // add account to db - await ark.createAccount(npub, pubkey); + await ark.createAccount({ id: npub, pubkey }); // get account contacts await ark.getUserContacts({ pubkey }); diff --git a/src/libs/ark/ark.ts b/src/libs/ark/ark.ts index 27d316cb..d9089653 100644 --- a/src/libs/ark/ark.ts +++ b/src/libs/ark/ark.ts @@ -81,6 +81,9 @@ export class Ark { async #initNostrSigner({ nsecbunker }: { nsecbunker?: boolean }) { const account = await this.getActiveAccount(); + if (!account) return null; + + // update active account this.account = account; try { @@ -225,7 +228,15 @@ export class Ark { } } - public async createAccount(npub: string, pubkey: string, privkey?: string) { + public async createAccount({ + id, + pubkey, + privkey, + }: { + id: string; + pubkey: string; + privkey?: string; + }) { const existAccounts: Array = await this.#storage.select( 'SELECT * FROM accounts WHERE pubkey = $1 ORDER BY id DESC LIMIT 1;', [pubkey] @@ -239,13 +250,17 @@ export class Ark { } else { await this.#storage.execute( 'INSERT OR IGNORE INTO accounts (id, pubkey, is_active) VALUES ($1, $2, $3);', - [npub, pubkey, 1] + [id, pubkey, 1] ); if (privkey) await this.#keyring_save(pubkey, privkey); } - return await this.getActiveAccount(); + const account = await this.getActiveAccount(); + this.account = account; + this.account.contacts = []; + + return account; } /** @@ -416,10 +431,10 @@ export class Ark { event.kind = kind; event.tags = tags; - if (!publish) { - const publish = await event.publish(); - if (!publish) throw new Error('cannot publish error'); - return publish.size; + if (publish) { + const publishedEvent = await event.publish(); + if (!publishedEvent) throw new Error('Failed to publish event'); + return publishedEvent.size; } return event; @@ -430,6 +445,7 @@ export class Ark { public async getUserProfile({ pubkey }: { pubkey: string }) { try { + console.log(pubkey); const user = this.#ndk.getUser({ pubkey }); const profile = await user.fetchProfile({ cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST, diff --git a/src/utils/shortenKey.ts b/src/utils/shortenKey.ts index cf950fd3..42f7bad3 100644 --- a/src/utils/shortenKey.ts +++ b/src/utils/shortenKey.ts @@ -6,7 +6,7 @@ export function shortenKey(pubkey: string) { } export function displayNpub(pubkey: string, len: number, separator?: string) { - const npub = nip19.npubEncode(pubkey) as string; + const npub = pubkey.startsWith('npub1') ? pubkey : (nip19.npubEncode(pubkey) as string); if (npub.length <= len) return npub; separator = separator || ' ... '; From 38e82a4feb545b2185e2607aed53cc26309890f2 Mon Sep 17 00:00:00 2001 From: reya Date: Sat, 9 Dec 2023 18:11:02 +0700 Subject: [PATCH 7/8] prefetch data --- src/app/auth/finish.tsx | 86 ++++++++++++++++++++++++++-- src/app/auth/tutorials/finish.tsx | 90 ++++++++++++++++++++++++++++-- src/libs/ark/ark.ts | 3 +- src/libs/ark/provider.tsx | 56 ++++++++++++++++++- src/utils/hooks/useRichContent.tsx | 12 +++- 5 files changed, 233 insertions(+), 14 deletions(-) diff --git a/src/app/auth/finish.tsx b/src/app/auth/finish.tsx index 741df4d0..dc4faa38 100644 --- a/src/app/auth/finish.tsx +++ b/src/app/auth/finish.tsx @@ -1,6 +1,81 @@ -import { Link } from 'react-router-dom'; +import { NDKKind } from '@nostr-dev-kit/ndk'; +import { useQueryClient } from '@tanstack/react-query'; +import { useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; + +import { useArk } from '@libs/ark'; + +import { LoaderIcon } from '@shared/icons'; + +import { FETCH_LIMIT } from '@utils/constants'; export function FinishScreen() { + const { ark } = useArk(); + const [loading, setLoading] = useState(false); + + const queryClient = useQueryClient(); + const navigate = useNavigate(); + + const prefetch = async () => { + if (!ark.account.contacts.length) return navigate('/'); + + try { + setLoading(true); + + // prefetch newsfeed + await queryClient.prefetchInfiniteQuery({ + queryKey: ['newsfeed'], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + return await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + authors: !ark.account.contacts.length + ? [ark.account.pubkey] + : ark.account.contacts, + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + }, + }); + + // prefetch notification + await queryClient.prefetchInfiniteQuery({ + queryKey: ['notification'], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + return await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], + '#p': [ark.account.pubkey], + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + }, + }); + + navigate('/'); + } catch (e) { + console.error(e); + } + }; + return (
@@ -17,12 +92,13 @@ export function FinishScreen() { > Start tutorial - - Skip - + {loading ? : 'Skip'} +

You need to restart app to make changes in previous step take effect or you can continue with Lume default settings diff --git a/src/app/auth/tutorials/finish.tsx b/src/app/auth/tutorials/finish.tsx index 8b064f8a..bf969f10 100644 --- a/src/app/auth/tutorials/finish.tsx +++ b/src/app/auth/tutorials/finish.tsx @@ -1,6 +1,81 @@ -import { Link } from 'react-router-dom'; +import { NDKKind } from '@nostr-dev-kit/ndk'; +import { useQueryClient } from '@tanstack/react-query'; +import { useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; + +import { useArk } from '@libs/ark'; + +import { LoaderIcon } from '@shared/icons'; + +import { FETCH_LIMIT } from '@utils/constants'; export function TutorialFinishScreen() { + const { ark } = useArk(); + const [loading, setLoading] = useState(false); + + const queryClient = useQueryClient(); + const navigate = useNavigate(); + + const prefetch = async () => { + if (!ark.account.contacts.length) return navigate('/'); + + try { + setLoading(true); + + // prefetch newsfeed + await queryClient.prefetchInfiniteQuery({ + queryKey: ['newsfeed'], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + return await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + authors: !ark.account.contacts.length + ? [ark.account.pubkey] + : ark.account.contacts, + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + }, + }); + + // prefetch notification + await queryClient.prefetchInfiniteQuery({ + queryKey: ['notification'], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + return await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], + '#p': [ark.account.pubkey], + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + }, + }); + + navigate('/'); + } catch (e) { + console.error(e); + } + }; + return (

@@ -11,12 +86,17 @@ export function TutorialFinishScreen() {
- - Start using - + {loading ? ( + + ) : ( + 'Start using Lume' + )} + user.pubkey ); - this.account.contacts = contacts; + if (pubkey === this.account.pubkey) this.account.contacts = contacts; return contacts; } catch (e) { console.error(e); diff --git a/src/libs/ark/provider.tsx b/src/libs/ark/provider.tsx index 4e3ecc4a..8e3798cd 100644 --- a/src/libs/ark/provider.tsx +++ b/src/libs/ark/provider.tsx @@ -1,3 +1,5 @@ +import { NDKKind } from '@nostr-dev-kit/ndk'; +import { useQueryClient } from '@tanstack/react-query'; import { ask } from '@tauri-apps/plugin-dialog'; import { platform } from '@tauri-apps/plugin-os'; import { relaunch } from '@tauri-apps/plugin-process'; @@ -10,7 +12,7 @@ import { Ark } from '@libs/ark'; import { LoaderIcon } from '@shared/icons'; -import { QUOTES } from '@utils/constants'; +import { FETCH_LIMIT, QUOTES } from '@utils/constants'; interface ArkContext { ark: Ark; @@ -24,6 +26,8 @@ const ArkProvider = ({ children }: PropsWithChildren) => { const [ark, setArk] = useState(undefined); const [isNewVersion, setIsNewVersion] = useState(false); + const queryClient = useQueryClient(); + async function initArk() { try { const sqlite = await Database.load('sqlite:lume_v2.db'); @@ -61,6 +65,56 @@ const ArkProvider = ({ children }: PropsWithChildren) => { } } + if (_ark.account) { + // prefetch newsfeed + await queryClient.prefetchInfiniteQuery({ + queryKey: ['newsfeed'], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + return await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + authors: !ark.account.contacts.length + ? [ark.account.pubkey] + : ark.account.contacts, + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + }, + }); + + // prefetch notification + await queryClient.prefetchInfiniteQuery({ + queryKey: ['notification'], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + return await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], + '#p': [ark.account.pubkey], + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + }, + }); + } + setArk(_ark); } catch (e) { console.error(e); diff --git a/src/utils/hooks/useRichContent.tsx b/src/utils/hooks/useRichContent.tsx index f407aae6..cb392e73 100644 --- a/src/utils/hooks/useRichContent.tsx +++ b/src/utils/hooks/useRichContent.tsx @@ -23,9 +23,19 @@ const NOSTR_MENTIONS = [ 'npub1', 'nprofile1', 'naddr1', + 'Nostr:npub1', + 'Nostr:nprofile1', + 'Nostr:naddre1', ]; -const NOSTR_EVENTS = ['nostr:note1', 'note1', 'nostr:nevent1', 'nevent1']; +const NOSTR_EVENTS = [ + 'nostr:note1', + 'note1', + 'nostr:nevent1', + 'nevent1', + 'Nostr:note1', + 'Nostr:nevent1', +]; // const BITCOINS = ['lnbc', 'bc1p', 'bc1q']; From 72a38e3aa7070b8ec924cf7b4282e30f3d8f2583 Mon Sep 17 00:00:00 2001 From: reya Date: Sun, 10 Dec 2023 08:39:40 +0700 Subject: [PATCH 8/8] polish --- package.json | 12 +- pnpm-lock.yaml | 600 +++++++++++++++--------------- src-tauri/Cargo.lock | 102 ++--- src/app/auth/follow.tsx | 2 + src/app/new/post.tsx | 33 +- src/app/new/privkey.tsx | 4 +- src/libs/ark/ark.ts | 21 +- src/shared/notes/replies/item.tsx | 10 +- src/shared/notes/replies/list.tsx | 2 +- 9 files changed, 398 insertions(+), 388 deletions(-) diff --git a/package.json b/package.json index b310f471..903ca5eb 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", - "@tanstack/react-query": "^5.12.2", - "@tanstack/react-query-devtools": "^5.13.3", + "@tanstack/react-query": "^5.13.4", + "@tanstack/react-query-devtools": "^5.13.4", "@tauri-apps/api": "2.0.0-alpha.11", "@tauri-apps/cli": "2.0.0-alpha.17", "@tauri-apps/plugin-autostart": "2.0.0-alpha.3", @@ -60,9 +60,9 @@ "@tiptap/react": "^2.1.13", "@tiptap/starter-kit": "^2.1.13", "@tiptap/suggestion": "^2.1.13", - "@vidstack/react": "^1.8.3", + "@vidstack/react": "^1.9.2", "dayjs": "^1.11.10", - "framer-motion": "^10.16.15", + "framer-motion": "^10.16.16", "html-to-text": "^9.0.5", "light-bolt11-decoder": "^3.0.0", "lru-cache": "^10.1.0", @@ -76,7 +76,7 @@ "react": "^18.2.0", "react-currency-input-field": "^3.6.12", "react-dom": "^18.2.0", - "react-hook-form": "^7.48.2", + "react-hook-form": "^7.49.0", "react-hotkeys-hook": "^4.4.1", "react-router-dom": "^6.20.1", "react-string-replace": "^1.1.1", @@ -93,7 +93,7 @@ "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/html-to-text": "^9.0.4", "@types/node": "^20.10.4", - "@types/react": "^18.2.42", + "@types/react": "^18.2.43", "@types/react-dom": "^18.2.17", "@typescript-eslint/eslint-plugin": "^6.13.2", "@typescript-eslint/parser": "^6.13.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa7e38ce..406de5dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,43 +19,43 @@ dependencies: version: 0.13.1(@nostr-dev-kit/ndk@2.2.0)(nostr-fetch@0.13.1) '@radix-ui/react-accordion': specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-alert-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-avatar': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-collapsible': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dropdown-menu': specifier: ^2.0.6 - version: 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-hover-card': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-popover': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-switch': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-toolbar': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-tooltip': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@tanstack/react-query': - specifier: ^5.12.2 - version: 5.12.2(react@18.2.0) + specifier: ^5.13.4 + version: 5.13.4(react@18.2.0) '@tanstack/react-query-devtools': - specifier: ^5.13.3 - version: 5.13.3(@tanstack/react-query@5.12.2)(react@18.2.0) + specifier: ^5.13.4 + version: 5.13.4(@tanstack/react-query@5.13.4)(react@18.2.0) '@tauri-apps/api': specifier: 2.0.0-alpha.11 version: 2.0.0-alpha.11 @@ -132,14 +132,14 @@ dependencies: specifier: ^2.1.13 version: 2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13) '@vidstack/react': - specifier: ^1.8.3 - version: 1.8.3(@types/react@18.2.42)(react@18.2.0) + specifier: ^1.9.2 + version: 1.9.2(@types/react@18.2.43)(react@18.2.0) dayjs: specifier: ^1.11.10 version: 1.11.10 framer-motion: - specifier: ^10.16.15 - version: 10.16.15(react-dom@18.2.0)(react@18.2.0) + specifier: ^10.16.16 + version: 10.16.16(react-dom@18.2.0)(react@18.2.0) html-to-text: specifier: ^9.0.5 version: 9.0.5 @@ -180,8 +180,8 @@ dependencies: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) react-hook-form: - specifier: ^7.48.2 - version: 7.48.2(react@18.2.0) + specifier: ^7.49.0 + version: 7.49.0(react@18.2.0) react-hotkeys-hook: specifier: ^4.4.1 version: 4.4.1(react-dom@18.2.0)(react@18.2.0) @@ -208,7 +208,7 @@ dependencies: version: 0.17.5(react-dom@18.2.0)(react@18.2.0) zustand: specifier: ^4.4.7 - version: 4.4.7(@types/react@18.2.42)(react@18.2.0) + version: 4.4.7(@types/react@18.2.43)(react@18.2.0) devDependencies: '@tailwindcss/forms': @@ -227,8 +227,8 @@ devDependencies: specifier: ^20.10.4 version: 20.10.4 '@types/react': - specifier: ^18.2.42 - version: 18.2.42 + specifier: ^18.2.43 + version: 18.2.43 '@types/react-dom': specifier: ^18.2.17 version: 18.2.17 @@ -891,7 +891,7 @@ packages: '@babel/runtime': 7.23.5 dev: false - /@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==} peerDependencies: '@types/react': '*' @@ -906,21 +906,21 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==} peerDependencies: '@types/react': '*' @@ -935,18 +935,18 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: '@types/react': '*' @@ -960,14 +960,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==} peerDependencies: '@types/react': '*' @@ -981,17 +981,17 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} peerDependencies: '@types/react': '*' @@ -1006,20 +1006,20 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: '@types/react': '*' @@ -1033,17 +1033,17 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: '@types/react': '*' @@ -1053,11 +1053,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-context@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-context@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: '@types/react': '*' @@ -1067,11 +1067,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} peerDependencies: '@types/react': '*' @@ -1086,26 +1086,26 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.43)(react@18.2.0) dev: false - /@radix-ui/react-direction@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-direction@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} peerDependencies: '@types/react': '*' @@ -1115,11 +1115,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} peerDependencies: '@types/react': '*' @@ -1134,17 +1134,17 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} peerDependencies: '@types/react': '*' @@ -1159,19 +1159,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} peerDependencies: '@types/react': '*' @@ -1181,11 +1181,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} peerDependencies: '@types/react': '*' @@ -1199,16 +1199,16 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==} peerDependencies: '@types/react': '*' @@ -1223,21 +1223,21 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-id@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-id@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} peerDependencies: '@types/react': '*' @@ -1247,12 +1247,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} peerDependencies: '@types/react': '*' @@ -1267,30 +1267,30 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.43)(react@18.2.0) dev: false - /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} peerDependencies: '@types/react': '*' @@ -1305,27 +1305,27 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.43)(react@18.2.0) dev: false - /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} peerDependencies: '@types/react': '*' @@ -1340,22 +1340,22 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.43)(react@18.2.0) '@radix-ui/rect': 1.0.1 - '@types/react': 18.2.42 + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} peerDependencies: '@types/react': '*' @@ -1369,14 +1369,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} peerDependencies: '@types/react': '*' @@ -1390,15 +1390,15 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: '@types/react': '*' @@ -1412,14 +1412,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: '@types/react': '*' @@ -1434,21 +1434,21 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} peerDependencies: '@types/react': '*' @@ -1462,14 +1462,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-slot@1.0.2(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-slot@1.0.2(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: '@types/react': '*' @@ -1479,12 +1479,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==} peerDependencies: '@types/react': '*' @@ -1499,19 +1499,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==} peerDependencies: '@types/react': '*' @@ -1526,19 +1526,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toggle': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-toggle': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-toggle@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-toggle@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==} peerDependencies: '@types/react': '*' @@ -1553,15 +1553,15 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-tBgmM/O7a07xbaEkYJWYTXkIdU/1pW4/KZORR43toC/4XWyBCURK0ei9kMUdp+gTPPKBgYLxXmRSH1EVcIDp8Q==} peerDependencies: '@types/react': '*' @@ -1576,19 +1576,19 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-separator': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-separator': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==} peerDependencies: '@types/react': '*' @@ -1603,24 +1603,24 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} peerDependencies: '@types/react': '*' @@ -1630,11 +1630,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} peerDependencies: '@types/react': '*' @@ -1644,12 +1644,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: '@types/react': '*' @@ -1659,12 +1659,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} peerDependencies: '@types/react': '*' @@ -1674,11 +1674,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} peerDependencies: '@types/react': '*' @@ -1688,11 +1688,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: '@types/react': '*' @@ -1703,11 +1703,11 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/rect': 1.0.1 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-size@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-size@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: '@types/react': '*' @@ -1717,12 +1717,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} peerDependencies: '@types/react': '*' @@ -1736,8 +1736,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1956,31 +1956,31 @@ packages: tailwindcss: 3.3.6 dev: true - /@tanstack/query-core@5.12.1: - resolution: {integrity: sha512-WbZztNmKq0t6QjdNmHzezbi/uifYo9j6e2GLJkodsYaYUlzMbAp91RDyeHkIZrm7EfO4wa6Sm5sxJZm5SPlh6w==} + /@tanstack/query-core@5.13.4: + resolution: {integrity: sha512-8+rJucXvC/xlr4OrxHhEIob/cTlbT4fgmz1VsvB0D12FRStKaXeLORNGcOhSAynRd2NL74SV/Qq0IIb4DedLcA==} dev: false /@tanstack/query-devtools@5.13.3: resolution: {integrity: sha512-1acztPKZexvM9Ns2T0aq4rMVSDA3VGdB73KF7zT/KNVl6VfnBvs24wuIRVSPZKqyZznZTzT3/DzcpntYqg9hmw==} dev: false - /@tanstack/react-query-devtools@5.13.3(@tanstack/react-query@5.12.2)(react@18.2.0): - resolution: {integrity: sha512-ct58CMRrcjANRWCQ6cxzSUtme2jlX5au63+ckhMONob8bIk5VRfUEi4R49AWNJFL5haTBKe0InC0AV4bWi75VQ==} + /@tanstack/react-query-devtools@5.13.4(@tanstack/react-query@5.13.4)(react@18.2.0): + resolution: {integrity: sha512-htYzfOrE6vucBCRrVLxW8qEg8mfC5UaMJMiFPgbv5yH3zoHtJSjVZP7fcvgTp3RwFKBr1IOQ9yHLTjSSXemS7A==} peerDependencies: - '@tanstack/react-query': ^5.12.2 + '@tanstack/react-query': ^5.13.4 react: ^18.0.0 dependencies: '@tanstack/query-devtools': 5.13.3 - '@tanstack/react-query': 5.12.2(react@18.2.0) + '@tanstack/react-query': 5.13.4(react@18.2.0) react: 18.2.0 dev: false - /@tanstack/react-query@5.12.2(react@18.2.0): - resolution: {integrity: sha512-BeWZu8zVFH20oRc+S/K9ADPgWjEzP/XQCGBNz5IbApUwPQAdwkQYbXODVL5AyAlWiSxhx+P2xlARPBApj2Yrog==} + /@tanstack/react-query@5.13.4(react@18.2.0): + resolution: {integrity: sha512-3HjvkFFriEQwffUXtKHPiwkfFXUGbs46YATTzzyK1+Pw6Ekd3kwzS50e45qdamWuEXmXxyo5S1zp534LdFG0Rw==} peerDependencies: react: ^18.0.0 dependencies: - '@tanstack/query-core': 5.12.1 + '@tanstack/query-core': 5.13.4 react: 18.2.0 dev: false @@ -2410,9 +2410,9 @@ packages: prosemirror-schema-list: 1.3.0 prosemirror-state: 1.4.3 prosemirror-tables: 1.3.5 - prosemirror-trailing-node: 2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.5) + prosemirror-trailing-node: 2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.6) prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.5 + prosemirror-view: 1.32.6 dev: false /@tiptap/react@2.1.13(@tiptap/core@2.1.13)(@tiptap/pm@2.1.13)(react-dom@18.2.0)(react@18.2.0): @@ -2530,10 +2530,10 @@ packages: /@types/react-dom@18.2.17: resolution: {integrity: sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==} dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 - /@types/react@18.2.42: - resolution: {integrity: sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==} + /@types/react@18.2.43: + resolution: {integrity: sha512-nvOV01ZdBdd/KW6FahSbcNplt2jCJfyWdTos61RYHV+FVv5L/g9AOX1bmbVcWcLFL8+KHQfh1zVIQrud6ihyQA==} dependencies: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 @@ -2685,14 +2685,14 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vidstack/react@1.8.3(@types/react@18.2.42)(react@18.2.0): - resolution: {integrity: sha512-QCyHy6e3LpzfajtjrhJPXzGYbBrBCUE5qYAatKXX+nxWqRvspa0fJPlnGeWb+tg6DlDsgwDLFjGNWj8qUeUVXQ==} + /@vidstack/react@1.9.2(@types/react@18.2.43)(react@18.2.0): + resolution: {integrity: sha512-DP7D/uUbHml4xQypDygkrEr80FCRtloj8IFvbaH+xZmiADS3ruy+AngaegorpCyMoWTHEiyBV4EuPXrX2v2sZw==} engines: {node: '>=18'} peerDependencies: '@types/react': ^18.0.0 react: ^18.0.0 dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 media-captions: 1.0.2 react: 18.2.0 dev: false @@ -2936,7 +2936,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001566 - electron-to-chromium: 1.4.608 + electron-to-chromium: 1.4.609 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true @@ -3243,8 +3243,8 @@ packages: domhandler: 5.0.3 dev: false - /electron-to-chromium@1.4.608: - resolution: {integrity: sha512-J2f/3iIIm3Mo0npneITZ2UPe4B1bg8fTNrFjD8715F/k1BvbviRuqYGkET1PgprrczXYTHFvotbBOmUp6KE0uA==} + /electron-to-chromium@1.4.609: + resolution: {integrity: sha512-ihiCP7PJmjoGNuLpl7TjNA8pCQWu09vGyjlPYw1Rqww4gvNuCcmvl+44G+2QyJ6S2K4o+wbTS++Xz0YN8Q9ERw==} dev: true /emoji-regex@10.3.0: @@ -3706,8 +3706,8 @@ packages: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true - /framer-motion@10.16.15(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-BapW1EKc/Sgph1rKEwL9bFt2B79z7bu7jN4Up1jGUaTE78WBsVO+6eCmoUZsQH0hHtRUDnnTqjjBbM7dk9Kp8A==} + /framer-motion@10.16.16(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-je6j91rd7NmUX7L1XHouwJ4v3R+SO4umso2LUcgOct3rHZ0PajZ80ETYZTajzEXEl9DlKyzjyt4AvGQ+lrebOw==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 @@ -4949,7 +4949,7 @@ packages: dependencies: prosemirror-state: 1.4.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.5 + prosemirror-view: 1.32.6 dev: false /prosemirror-gapcursor@1.3.2: @@ -4958,7 +4958,7 @@ packages: prosemirror-keymap: 1.2.2 prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 - prosemirror-view: 1.32.5 + prosemirror-view: 1.32.6 dev: false /prosemirror-history@1.3.2: @@ -4966,7 +4966,7 @@ packages: dependencies: prosemirror-state: 1.4.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.5 + prosemirror-view: 1.32.6 rope-sequence: 1.3.4 dev: false @@ -5025,7 +5025,7 @@ packages: dependencies: prosemirror-model: 1.19.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.5 + prosemirror-view: 1.32.6 dev: false /prosemirror-tables@1.3.5: @@ -5035,10 +5035,10 @@ packages: prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 prosemirror-transform: 1.8.0 - prosemirror-view: 1.32.5 + prosemirror-view: 1.32.6 dev: false - /prosemirror-trailing-node@2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.5): + /prosemirror-trailing-node@2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.32.6): resolution: {integrity: sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==} peerDependencies: prosemirror-model: ^1.19.0 @@ -5050,7 +5050,7 @@ packages: escape-string-regexp: 4.0.0 prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 - prosemirror-view: 1.32.5 + prosemirror-view: 1.32.6 dev: false /prosemirror-transform@1.8.0: @@ -5059,8 +5059,8 @@ packages: prosemirror-model: 1.19.3 dev: false - /prosemirror-view@1.32.5: - resolution: {integrity: sha512-OZ8JSytiOr1mlxu8XSPJSNMn3sQrycbP12Peubwsnq9xiyQk4F58u9wOPJYSXXG/uOXZQNSAITRnScZPdjEh+A==} + /prosemirror-view@1.32.6: + resolution: {integrity: sha512-26r5LvyDlPgUNVf7ZdNdGrMJnylwjJtUJTfDuYOANIVx9lqWD1WCBlGg283weYQGKUC64DXR25LeAmliB9CrFQ==} dependencies: prosemirror-model: 1.19.3 prosemirror-state: 1.4.3 @@ -5112,9 +5112,9 @@ packages: scheduler: 0.23.0 dev: false - /react-hook-form@7.48.2(react@18.2.0): - resolution: {integrity: sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A==} - engines: {node: '>=12.22.0'} + /react-hook-form@7.49.0(react@18.2.0): + resolution: {integrity: sha512-gf4qyY4WiqK2hP/E45UUT6wt3Khl49pleEVcIzxhLBrD6m+GMWtLRk0vMrRv45D1ZH8PnpXFwRPv0Pewske2jw==} + engines: {node: '>=18', pnpm: '8'} peerDependencies: react: ^16.8.0 || ^17 || ^18 dependencies: @@ -5135,7 +5135,7 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true - /react-remove-scroll-bar@2.3.4(@types/react@18.2.42)(react@18.2.0): + /react-remove-scroll-bar@2.3.4(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} engines: {node: '>=10'} peerDependencies: @@ -5145,13 +5145,13 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 - react-style-singleton: 2.2.1(@types/react@18.2.42)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.43)(react@18.2.0) tslib: 2.6.2 dev: false - /react-remove-scroll@2.5.5(@types/react@18.2.42)(react@18.2.0): + /react-remove-scroll@2.5.5(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} engines: {node: '>=10'} peerDependencies: @@ -5161,13 +5161,13 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 - react-remove-scroll-bar: 2.3.4(@types/react@18.2.42)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll-bar: 2.3.4(@types/react@18.2.43)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.43)(react@18.2.0) tslib: 2.6.2 - use-callback-ref: 1.3.0(@types/react@18.2.42)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.42)(react@18.2.0) + use-callback-ref: 1.3.0(@types/react@18.2.43)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.43)(react@18.2.0) dev: false /react-router-dom@6.20.1(react-dom@18.2.0)(react@18.2.0): @@ -5198,7 +5198,7 @@ packages: engines: {node: '>=0.12.0'} dev: false - /react-style-singleton@2.2.1(@types/react@18.2.42)(react@18.2.0): + /react-style-singleton@2.2.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} peerDependencies: @@ -5208,7 +5208,7 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 get-nonce: 1.0.1 invariant: 2.2.4 react: 18.2.0 @@ -5820,7 +5820,7 @@ packages: punycode: 2.3.1 dev: true - /use-callback-ref@1.3.0(@types/react@18.2.42)(react@18.2.0): + /use-callback-ref@1.3.0(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} engines: {node: '>=10'} peerDependencies: @@ -5830,12 +5830,12 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 tslib: 2.6.2 dev: false - /use-sidecar@1.1.2(@types/react@18.2.42)(react@18.2.0): + /use-sidecar@1.1.2(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} peerDependencies: @@ -5845,7 +5845,7 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 detect-node-es: 1.1.0 react: 18.2.0 tslib: 2.6.2 @@ -6078,7 +6078,7 @@ packages: engines: {node: '>=10'} dev: true - /zustand@4.4.7(@types/react@18.2.42)(react@18.2.0): + /zustand@4.4.7(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==} engines: {node: '>=12.7.0'} peerDependencies: @@ -6093,7 +6093,7 @@ packages: react: optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) dev: false diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 751aa4d1..4b2b57fa 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ "windows-sys 0.52.0", ] @@ -253,7 +253,7 @@ dependencies = [ "futures-lite 2.1.0", "parking", "polling 3.3.1", - "rustix 0.38.26", + "rustix 0.38.28", "slab", "tracing", "windows-sys 0.52.0", @@ -292,7 +292,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -319,7 +319,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.26", + "rustix 0.38.28", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -349,7 +349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" dependencies = [ "atk-sys", - "glib 0.18.3", + "glib 0.18.4", "libc", ] @@ -596,7 +596,7 @@ checksum = "f33613627f0dea6a731b0605101fad59ba4f193a52c96c4687728d822605a8a1" dependencies = [ "bitflags 2.4.1", "cairo-sys-rs", - "glib 0.18.3", + "glib 0.18.4", "libc", "once_cell", "thiserror", @@ -1419,7 +1419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -1695,7 +1695,7 @@ dependencies = [ "gdk-pixbuf", "gdk-sys", "gio", - "glib 0.18.3", + "glib 0.18.4", "libc", "pango", ] @@ -1708,7 +1708,7 @@ checksum = "446f32b74d22c33b7b258d4af4ffde53c2bf96ca2e29abdf1a785fe59bd6c82c" dependencies = [ "gdk-pixbuf-sys", "gio", - "glib 0.18.3", + "glib 0.18.4", "libc", "once_cell", ] @@ -1843,16 +1843,16 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gio" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d809baf02bdf1b5ef4ad3bf60dd9d4977149db4612b7bbb58e56aef168193b" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-util", "gio-sys 0.18.1", - "glib 0.18.3", + "glib 0.18.4", "libc", "once_cell", "pin-project-lite", @@ -1910,9 +1910,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cf801b6f7829fa76db37449ab67c9c98a2b1bf21076d9113225621e61a0fa6" +checksum = "951bbd7fdc5c044ede9f05170f05a3ae9479239c3afdfe2d22d537a3add15c4e" dependencies = [ "bitflags 2.4.1", "futures-channel", @@ -2021,7 +2021,7 @@ dependencies = [ "gdk", "gdk-pixbuf", "gio", - "glib 0.18.3", + "glib 0.18.4", "gtk-sys", "gtk3-macros", "libc", @@ -2174,14 +2174,14 @@ checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", - "itoa 1.0.9", + "itoa 1.0.10", ] [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -2215,7 +2215,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.9", + "itoa 1.0.10", "pin-project-lite", "socket2 0.4.10", "tokio", @@ -2403,9 +2403,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "javascriptcore-rs" @@ -2414,7 +2414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" dependencies = [ "bitflags 1.3.2", - "glib 0.18.3", + "glib 0.18.4", "javascriptcore-rs-sys", ] @@ -2541,7 +2541,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" dependencies = [ - "glib 0.18.3", + "glib 0.18.4", "gtk", "gtk-sys", "libappindicator-sys", @@ -3242,9 +3242,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.1.6+3.1.4" +version = "300.2.0+3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085" +checksum = "b1ebed1d188c4cd64c2bcd73d6c1fe1092f3d98c111831923cc1b706c3859fca" dependencies = [ "cc", ] @@ -3306,7 +3306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ "gio", - "glib 0.18.3", + "glib 0.18.4", "libc", "once_cell", "pango-sys", @@ -3639,7 +3639,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.26", + "rustix 0.38.28", "tracing", "windows-sys 0.52.0", ] @@ -4043,9 +4043,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.26" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", @@ -4086,9 +4086,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "safemem" @@ -4223,7 +4223,7 @@ version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "itoa 1.0.9", + "itoa 1.0.10", "ryu", "serde", ] @@ -4255,7 +4255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.9", + "itoa 1.0.10", "ryu", "serde", ] @@ -4436,7 +4436,7 @@ checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" dependencies = [ "futures-channel", "gio", - "glib 0.18.3", + "glib 0.18.4", "libc", "soup3-sys", ] @@ -4632,7 +4632,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "itoa 1.0.9", + "itoa 1.0.10", "log", "md-5", "memchr", @@ -4673,7 +4673,7 @@ dependencies = [ "hkdf", "hmac", "home", - "itoa 1.0.9", + "itoa 1.0.10", "log", "md-5", "memchr", @@ -4888,7 +4888,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gio", - "glib 0.18.3", + "glib 0.18.4", "glib-sys 0.18.1", "gtk", "image", @@ -5398,7 +5398,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -5467,7 +5467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", - "itoa 1.0.9", + "itoa 1.0.10", "powerfmt", "serde", "time-core", @@ -5516,9 +5516,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -6015,7 +6015,7 @@ dependencies = [ "gdk-sys", "gio", "gio-sys 0.18.1", - "glib 0.18.3", + "glib 0.18.4", "glib-sys 0.18.1", "gobject-sys 0.18.0", "gtk", @@ -6539,9 +6539,9 @@ dependencies = [ [[package]] name = "xattr" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "fbc6ab6ec1907d1a901cdbcd2bd4cb9e7d64ce5c9739cbb97d3c391acd8c7fae" dependencies = [ "libc", ] @@ -6635,18 +6635,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.29" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.29" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", diff --git a/src/app/auth/follow.tsx b/src/app/auth/follow.tsx index 79b5c2ba..6bc5b600 100644 --- a/src/app/auth/follow.tsx +++ b/src/app/auth/follow.tsx @@ -72,6 +72,7 @@ export function FollowScreen() { if (item.startsWith('npub1')) return ['p', nip19.decode(item).data as string]; return ['p', item]; }), + publish: true, }); if (publish) { @@ -80,6 +81,7 @@ export function FollowScreen() { return item; }); + setLoading(false); return navigate('/auth/finish'); } } catch (e) { diff --git a/src/app/new/post.tsx b/src/app/new/post.tsx index 6a6def9a..659652c9 100644 --- a/src/app/new/post.tsx +++ b/src/app/new/post.tsx @@ -7,7 +7,7 @@ import { EditorContent, useEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { convert } from 'html-to-text'; import { nip19 } from 'nostr-tools'; -import { useEffect, useLayoutEffect, useRef, useState } from 'react'; +import { useLayoutEffect, useRef, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { toast } from 'sonner'; @@ -81,32 +81,23 @@ export function NewPostScreen() { ], }); - const event = new NDKEvent(); - event.content = serializedContent; - event.kind = NDKKind.Text; - // add reply to tags if present const replyTo = searchParams.get('replyTo'); const rootReplyTo = searchParams.get('rootReplyTo'); - if (rootReplyTo) { - const rootEvent = await ndk.fetchEvent(rootReplyTo); - event.tag(rootEvent, 'root'); - } - - if (replyTo) { - const replyEvent = await ndk.fetchEvent(replyTo); - event.tag(replyEvent, 'reply'); - } - // publish event - const publishedRelays = await ark.createEvent({ + const event = (await ark.createEvent({ kind: NDKKind.Text, + tags: [], content: serializedContent, - }); + replyTo, + rootReplyTo, + })) as NDKEvent; - if (publishedRelays) { - toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`); + const publish = await event.publish(); + + if (publish) { + toast.success(`Broadcasted to ${publish.size} relays successfully.`); // update state setLoading(false); @@ -135,10 +126,6 @@ export function NewPostScreen() { setHeight(containerRef.current.clientHeight); }, []); - useEffect(() => { - if (editor) editor.commands.focus('end'); - }, [editor]); - return (
diff --git a/src/app/new/privkey.tsx b/src/app/new/privkey.tsx index 771b9f6d..02afa98f 100644 --- a/src/app/new/privkey.tsx +++ b/src/app/new/privkey.tsx @@ -62,14 +62,14 @@ export function NewPrivkeyScreen() { diff --git a/src/libs/ark/ark.ts b/src/libs/ark/ark.ts index 42aa6f46..456f8771 100644 --- a/src/libs/ark/ark.ts +++ b/src/libs/ark/ark.ts @@ -192,6 +192,7 @@ export class Ark { public updateNostrSigner({ signer }: { signer: NDKNip46Signer | NDKPrivateKeySigner }) { this.#ndk.signer = signer; + this.readyToSign = true; return this.#ndk.signer; } @@ -418,11 +419,15 @@ export class Ark { kind, tags, content, + rootReplyTo = undefined, + replyTo = undefined, publish, }: { kind: NDKKind | number; tags: NDKTag[]; content?: string; + rootReplyTo?: string; + replyTo?: string; publish?: boolean; }) { try { @@ -431,6 +436,16 @@ export class Ark { event.kind = kind; event.tags = tags; + if (rootReplyTo) { + const rootEvent = await this.#ndk.fetchEvent(rootReplyTo); + if (rootEvent) event.tag(rootEvent, 'root'); + } + + if (replyTo) { + const replyEvent = await this.#ndk.fetchEvent(replyTo); + if (replyEvent) event.tag(replyEvent, 'reply'); + } + if (publish) { const publishedEvent = await event.publish(); if (!publishedEvent) throw new Error('Failed to publish event'); @@ -888,9 +903,9 @@ export class Ark { public async replyTo({ content, event }: { content: string; event: NDKEvent }) { try { const replyEvent = new NDKEvent(this.#ndk); - event.content = content; - event.kind = NDKKind.Text; - event.tag(event, 'reply'); + replyEvent.content = content; + replyEvent.kind = NDKKind.Text; + replyEvent.tag(event, 'reply'); return await replyEvent.publish(); } catch (e) { diff --git a/src/shared/notes/replies/item.tsx b/src/shared/notes/replies/item.tsx index 8ac52dbd..93c69794 100644 --- a/src/shared/notes/replies/item.tsx +++ b/src/shared/notes/replies/item.tsx @@ -8,7 +8,13 @@ import { User } from '@shared/user'; import { NDKEventWithReplies } from '@utils/types'; -export function Reply({ event }: { event: NDKEventWithReplies }) { +export function Reply({ + event, + rootEvent, +}: { + event: NDKEventWithReplies; + rootEvent: string; +}) { const [open, setOpen] = useState(false); return ( @@ -30,7 +36,7 @@ export function Reply({ event }: { event: NDKEventWithReplies }) {
) : null} - +
diff --git a/src/shared/notes/replies/list.tsx b/src/shared/notes/replies/list.tsx index 2b6dfbcd..610d70f9 100644 --- a/src/shared/notes/replies/list.tsx +++ b/src/shared/notes/replies/list.tsx @@ -63,7 +63,7 @@ export function ReplyList({ eventId }: { eventId: string }) {
) : ( - data.map((event) => ) + data.map((event) => ) )} );