From d128af1db81cd92c6c026c8321642fa3b1f7d711 Mon Sep 17 00:00:00 2001 From: reya <123083837+reyamir@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:48:17 +0700 Subject: [PATCH] chore: clean up --- src-tauri/src/commands/metadata.rs | 6 +- src/commons.ts | 58 ++- src/components/note/buttons/zap.tsx | 4 +- src/components/repost.tsx | 20 +- src/components/user/followButton.tsx | 69 ++-- src/components/user/nip05.tsx | 15 +- src/routes/$account/_app/home.lazy.tsx | 25 +- src/routes/$account/_app/home.tsx | 12 +- .../_settings/bitcoin-connect.lazy.tsx | 10 +- .../$account/_settings/profile.lazy.tsx | 44 +-- src/routes/$account/_settings/profile.tsx | 5 +- src/routes/$account/_settings/relay.lazy.tsx | 13 +- src/routes/$account/_settings/wallet.lazy.tsx | 12 +- src/routes/auth/new.lazy.tsx | 4 +- src/routes/columns/_layout/create-group.tsx | 11 +- src/routes/columns/_layout/gallery.lazy.tsx | 4 +- src/routes/columns/_layout/global.tsx | 57 ++- .../columns/_layout/notification.lazy.tsx | 19 +- src/routes/editor/-components/media.tsx | 82 ++--- src/routes/index.tsx | 4 +- src/routes/zap.$id.lazy.tsx | 40 +-- src/routes/zap.$id.tsx | 20 +- src/system/account.ts | 167 --------- src/system/hooks/useEvent.ts | 13 +- src/system/index.ts | 2 - src/system/query.ts | 336 ------------------ src/system/window.ts | 4 +- 27 files changed, 313 insertions(+), 743 deletions(-) delete mode 100644 src/system/account.ts delete mode 100644 src/system/query.ts diff --git a/src-tauri/src/commands/metadata.rs b/src-tauri/src/commands/metadata.rs index 1aa138e5..4e392dc7 100644 --- a/src-tauri/src/commands/metadata.rs +++ b/src-tauri/src/commands/metadata.rs @@ -6,7 +6,7 @@ use std::{str::FromStr, time::Duration}; use tauri::State; use tauri_specta::Event; -use crate::{NewSettings, Nostr, Settings}; +use crate::{common::get_latest_event, NewSettings, Nostr, Settings}; #[derive(Clone, Serialize, Deserialize, Type)] pub struct Profile { @@ -229,14 +229,14 @@ pub async fn get_lume_store(key: String, state: State<'_, Nostr>) -> Result { - if let Some(event) = events.first() { + if let Some(event) = get_latest_event(&events) { match signer.nip44_decrypt(public_key, event.content()).await { Ok(decrypted) => Ok(decrypted), Err(_) => Err(event.content.to_string()), diff --git a/src/commons.ts b/src/commons.ts index 8c797795..8b8abdbc 100644 --- a/src/commons.ts +++ b/src/commons.ts @@ -4,7 +4,8 @@ import type { PersistedQuery, } from "@tanstack/query-persist-client-core"; import { Store } from "@tanstack/store"; -import { ask, message } from "@tauri-apps/plugin-dialog"; +import { ask, message, open } from "@tauri-apps/plugin-dialog"; +import { readFile } from "@tauri-apps/plugin-fs"; import { relaunch } from "@tauri-apps/plugin-process"; import type { Store as TauriStore } from "@tauri-apps/plugin-store"; import { check } from "@tauri-apps/plugin-updater"; @@ -241,6 +242,61 @@ export async function checkForAppUpdates(silent: boolean) { } } +export async function upload(filePath?: string) { + const allowExts = [ + "png", + "jpeg", + "jpg", + "gif", + "mp4", + "mp3", + "webm", + "mkv", + "avi", + "mov", + ]; + + const selected = + filePath || + ( + await open({ + multiple: false, + filters: [ + { + name: "Media", + extensions: allowExts, + }, + ], + }) + ).path; + + // User cancelled action + if (!selected) return null; + + try { + const file = await readFile(selected); + 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; + } catch (e) { + throw new Error(String(e)); + } +} + export function toLumeEvents(richEvents: RichEvent[]) { const events = richEvents.map((item) => { const nostrEvent: NostrEvent = JSON.parse(item.raw); diff --git a/src/components/note/buttons/zap.tsx b/src/components/note/buttons/zap.tsx index f6016c5c..072b9f05 100644 --- a/src/components/note/buttons/zap.tsx +++ b/src/components/note/buttons/zap.tsx @@ -1,10 +1,12 @@ import { appSettings, cn } from "@/commons"; import { LumeWindow } from "@/system"; import { Lightning } from "@phosphor-icons/react"; +import { useSearch } from "@tanstack/react-router"; import { useStore } from "@tanstack/react-store"; import { useNoteContext } from "../provider"; export function NoteZap({ large = false }: { large?: boolean }) { + const search = useSearch({ strict: false }); const visible = useStore(appSettings, (state) => state.display_zap_button); const event = useNoteContext(); @@ -13,7 +15,7 @@ export function NoteZap({ large = false }: { large?: boolean }) { return ( ); } diff --git a/src/components/user/nip05.tsx b/src/components/user/nip05.tsx index 962e87fb..85d28252 100644 --- a/src/components/user/nip05.tsx +++ b/src/components/user/nip05.tsx @@ -1,5 +1,5 @@ +import { commands } from "@/commands.gen"; import { displayLongHandle, displayNpub } from "@/commons"; -import { NostrQuery } from "@/system"; import { SealCheck } from "@phosphor-icons/react"; import * as Tooltip from "@radix-ui/react-tooltip"; import { useQuery } from "@tanstack/react-query"; @@ -10,14 +10,13 @@ export function UserNip05() { const { isLoading, data: verified } = useQuery({ queryKey: ["nip05", user?.pubkey], queryFn: async () => { - if (!user.profile?.nip05?.length) return false; + const res = await commands.verifyNip05(user.pubkey, user.profile?.nip05); - const verify = await NostrQuery.verifyNip05( - user.pubkey, - user.profile?.nip05, - ); - - return verify; + if (res.status === "ok") { + return res.data; + } else { + throw new Error(res.error); + } }, enabled: !!user.profile?.nip05?.length, refetchOnMount: false, diff --git a/src/routes/$account/_app/home.lazy.tsx b/src/routes/$account/_app/home.lazy.tsx index aec03e3b..30c6669c 100644 --- a/src/routes/$account/_app/home.lazy.tsx +++ b/src/routes/$account/_app/home.lazy.tsx @@ -1,6 +1,7 @@ +import { commands } from "@/commands.gen"; import { Spinner } from "@/components"; import { Column } from "@/components/column"; -import { LumeWindow, NostrQuery } from "@/system"; +import { LumeWindow } from "@/system"; import type { ColumnEvent, LumeColumn } from "@/types"; import { ArrowLeft, ArrowRight, Plus, StackPlus } from "@phosphor-icons/react"; import { createLazyFileRoute } from "@tanstack/react-router"; @@ -24,7 +25,7 @@ export const Route = createLazyFileRoute("/$account/_app/home")({ }); function Screen() { - const initialColumnList = Route.useLoaderData(); + const { initialColumns } = Route.useRouteContext(); const [columns, setColumns] = useState([]); const [emblaRef, emblaApi] = useEmblaCarousel({ @@ -105,6 +106,18 @@ function Screen() { event.preventDefault(); }, 150); + const saveAllColumns = useDebouncedCallback(async () => { + const key = "lume_v4:columns"; + const content = JSON.stringify(columns); + const res = await commands.setLumeStore(key, content); + + if (res.status === "ok") { + return res.data; + } else { + console.log(res.error); + } + }, 200); + useEffect(() => { if (emblaApi) { emblaApi.on("scroll", emitScrollEvent); @@ -120,14 +133,12 @@ function Screen() { }, [emblaApi, emitScrollEvent, emitResizeEvent]); useEffect(() => { - if (columns) { - NostrQuery.setColumns(columns).then(() => console.log("saved")); - } + if (columns) saveAllColumns(); }, [columns]); useEffect(() => { - setColumns(initialColumnList); - }, [initialColumnList]); + setColumns(initialColumns); + }, [initialColumns]); // Listen for keyboard event useEffect(() => { diff --git a/src/routes/$account/_app/home.tsx b/src/routes/$account/_app/home.tsx index cbdddc35..9d6a29ff 100644 --- a/src/routes/$account/_app/home.tsx +++ b/src/routes/$account/_app/home.tsx @@ -3,16 +3,18 @@ import type { LumeColumn } from "@/types"; import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/$account/_app/home")({ - loader: async ({ context }) => { + beforeLoad: async ({ context }) => { const key = "lume_v4:columns"; const defaultColumns = context.systemColumns.filter((col) => col.default); const query = await commands.getLumeStore(key); + let initialColumns: LumeColumn[] = defaultColumns; + if (query.status === "ok") { - const columns: LumeColumn[] = JSON.parse(query.data); - return columns; - } else { - return defaultColumns; + initialColumns = JSON.parse(query.data); + return { initialColumns }; } + + return { initialColumns }; }, }); diff --git a/src/routes/$account/_settings/bitcoin-connect.lazy.tsx b/src/routes/$account/_settings/bitcoin-connect.lazy.tsx index 8ce98f57..aa073fd5 100644 --- a/src/routes/$account/_settings/bitcoin-connect.lazy.tsx +++ b/src/routes/$account/_settings/bitcoin-connect.lazy.tsx @@ -1,3 +1,4 @@ +import { commands } from "@/commands.gen"; import { NostrAccount } from "@/system"; import { Button } from "@getalby/bitcoin-connect-react"; import { createLazyFileRoute } from "@tanstack/react-router"; @@ -11,8 +12,13 @@ export const Route = createLazyFileRoute("/$account/_settings/bitcoin-connect")( function Screen() { const setNwcUri = async (uri: string) => { - const cmd = await NostrAccount.setWallet(uri); - if (cmd) getCurrentWebviewWindow().close(); + const res = await commands.setWallet(uri); + + if (res.status === "ok") { + await getCurrentWebviewWindow().close(); + } else { + throw new Error(res.error); + } }; return ( diff --git a/src/routes/$account/_settings/profile.lazy.tsx b/src/routes/$account/_settings/profile.lazy.tsx index 9c5a20b3..32e1d99b 100644 --- a/src/routes/$account/_settings/profile.lazy.tsx +++ b/src/routes/$account/_settings/profile.lazy.tsx @@ -1,8 +1,6 @@ -import { commands } from "@/commands.gen"; -import { cn } from "@/commons"; +import { type Profile, commands } from "@/commands.gen"; +import { cn, upload } from "@/commons"; import { Spinner } from "@/components"; -import { NostrAccount, NostrQuery } from "@/system"; -import type { Metadata } from "@/types"; import { Plus } from "@phosphor-icons/react"; import { createLazyFileRoute } from "@tanstack/react-router"; import { writeText } from "@tauri-apps/plugin-clipboard-manager"; @@ -27,15 +25,16 @@ function Screen() { const [isPending, startTransition] = useTransition(); const [picture, setPicture] = useState(""); - const onSubmit = (data: Metadata) => { + const onSubmit = (data: Profile) => { startTransition(async () => { - try { - const newProfile: Metadata = { ...profile, ...data, picture }; - await NostrAccount.createProfile(newProfile); - } catch (e) { - await message(String(e), { title: "Profile", kind: "error" }); - return; + const newProfile: Profile = { ...profile, ...data, picture }; + const res = await commands.setProfile(newProfile); + + if (res.status === "error") { + await message(res.error, { title: "Profile", kind: "error" }); } + + return; }); }; @@ -220,17 +219,18 @@ function AvatarUploader({ children: ReactNode; className?: string; }) { - const [loading, setLoading] = useState(false); + const [isPending, startTransition] = useTransition(); - const uploadAvatar = async () => { - try { - setLoading(true); - const image = await NostrQuery.upload(); - setPicture(image); - } catch (e) { - setLoading(false); - await message(String(e), { title: "Lume", kind: "error" }); - } + const uploadAvatar = () => { + startTransition(async () => { + try { + const image = await upload(); + setPicture(image); + } catch (e) { + await message(String(e)); + return; + } + }); }; return ( @@ -239,7 +239,7 @@ function AvatarUploader({ onClick={() => uploadAvatar()} className={cn("size-4", className)} > - {loading ? : children} + {isPending ? : children} ); } diff --git a/src/routes/$account/_settings/profile.tsx b/src/routes/$account/_settings/profile.tsx index 9d0582f0..e0e6d07e 100644 --- a/src/routes/$account/_settings/profile.tsx +++ b/src/routes/$account/_settings/profile.tsx @@ -1,5 +1,4 @@ -import { commands } from "@/commands.gen"; -import type { Metadata } from "@/types"; +import { type Profile, commands } from "@/commands.gen"; import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/$account/_settings/profile")({ @@ -7,7 +6,7 @@ export const Route = createFileRoute("/$account/_settings/profile")({ const res = await commands.getProfile(params.account); if (res.status === "ok") { - const profile: Metadata = JSON.parse(res.data); + const profile: Profile = JSON.parse(res.data); return { profile }; } else { throw new Error(res.error); diff --git a/src/routes/$account/_settings/relay.lazy.tsx b/src/routes/$account/_settings/relay.lazy.tsx index 938d4e81..d4df2190 100644 --- a/src/routes/$account/_settings/relay.lazy.tsx +++ b/src/routes/$account/_settings/relay.lazy.tsx @@ -1,5 +1,4 @@ import { commands } from "@/commands.gen"; -import { NostrQuery } from "@/system"; import { Plus, X } from "@phosphor-icons/react"; import { createLazyFileRoute } from "@tanstack/react-router"; import { message } from "@tauri-apps/plugin-dialog"; @@ -16,6 +15,16 @@ function Screen() { const [newRelay, setNewRelay] = useState(""); const [isPending, startTransition] = useTransition(); + const removeRelay = async (relay: string) => { + const res = await commands.removeRelay(relay); + + if (res.status === "ok") { + return res.data; + } else { + throw new Error(res.error); + } + }; + const addNewRelay = () => { startTransition(async () => { try { @@ -69,7 +78,7 @@ function Screen() {
))} diff --git a/src/routes/columns/_layout/global.tsx b/src/routes/columns/_layout/global.tsx index 9b1c7b59..29523d9a 100644 --- a/src/routes/columns/_layout/global.tsx +++ b/src/routes/columns/_layout/global.tsx @@ -1,10 +1,8 @@ -import { Spinner } from "@/components"; -import { Conversation } from "@/components/conversation"; -import { Quote } from "@/components/quote"; -import { RepostNote } from "@/components/repost"; -import { TextNote } from "@/components/text"; -import { type LumeEvent, NostrQuery } from "@/system"; -import { type ColumnRouteSearch, Kind } from "@/types"; +import { commands } from "@/commands.gen"; +import { toLumeEvents } from "@/commons"; +import { Quote, RepostNote, Spinner, TextNote } from "@/components"; +import type { LumeEvent } from "@/system"; +import { Kind } from "@/types"; import { ArrowCircleRight } from "@phosphor-icons/react"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import { useInfiniteQuery } from "@tanstack/react-query"; @@ -13,17 +11,6 @@ import { useCallback, useRef } from "react"; import { Virtualizer } from "virtua"; export const Route = createFileRoute("/columns/_layout/global")({ - validateSearch: (search: Record): ColumnRouteSearch => { - return { - account: search.account, - label: search.label, - name: search.name, - }; - }, - beforeLoad: async () => { - const settings = await NostrQuery.getUserSettings(); - return { settings }; - }, component: Screen, }); @@ -40,8 +27,14 @@ export function Screen() { queryKey: [label, account], initialPageParam: 0, queryFn: async ({ pageParam }: { pageParam: number }) => { - const events = await NostrQuery.getGlobalEvents(pageParam); - return events; + const until = pageParam > 0 ? pageParam.toString() : undefined; + const res = await commands.getGlobalEvents(until); + + if (res.status === "error") { + throw new Error(res.error); + } + + return toLumeEvents(res.data); }, getNextPageParam: (lastPage) => lastPage?.at(-1)?.created_at - 1, select: (data) => data?.pages.flat(), @@ -57,15 +50,11 @@ export function Screen() { case Kind.Repost: return ; default: { - if (event.isConversation) { - return ( - - ); - } if (event.isQuote) { return ; + } else { + return ; } - return ; } } }, @@ -81,12 +70,10 @@ export function Screen() { {isFetching && !isLoading && !isFetchingNextPage ? ( -
-
- - - Getting new notes... - +
+
+ + Getting new notes...
) : null} @@ -96,13 +83,13 @@ export function Screen() { Loading...
) : !data.length ? ( -
- Yo. You're catching up on all the things happening around you. +
+ 🎉 Yo. You're catching up on all latest notes.
) : ( data.map((item) => renderItem(item)) )} - {data?.length && hasNextPage ? ( + {hasNextPage ? (
); diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 48f7934b..9915fd0d 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,5 +1,5 @@ +import { commands } from "@/commands.gen"; import { checkForAppUpdates } from "@/commons"; -import { NostrAccount } from "@/system"; import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/")({ @@ -8,7 +8,7 @@ export const Route = createFileRoute("/")({ await checkForAppUpdates(true); // Get all accounts - const accounts = await NostrAccount.getAccounts(); + const accounts = await commands.getAccounts(); if (accounts.length < 1) { throw redirect({ diff --git a/src/routes/zap.$id.lazy.tsx b/src/routes/zap.$id.lazy.tsx index 71488ef0..77e0dc2a 100644 --- a/src/routes/zap.$id.lazy.tsx +++ b/src/routes/zap.$id.lazy.tsx @@ -2,7 +2,7 @@ import { User } from "@/components/user"; import { createLazyFileRoute } from "@tanstack/react-router"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; import { message } from "@tauri-apps/plugin-dialog"; -import { useState } from "react"; +import { useState, useTransition } from "react"; import CurrencyInput from "react-currency-input-field"; const DEFAULT_VALUES = [21, 50, 100, 200]; @@ -17,28 +17,26 @@ function Screen() { const [amount, setAmount] = useState(21); const [content, setContent] = useState(""); const [isCompleted, setIsCompleted] = useState(false); - const [isLoading, setIsLoading] = useState(false); + const [isPending, startTransition] = useTransition(); - const submit = async () => { - try { - // start loading - setIsLoading(true); + const submit = () => { + startTransition(async () => { + try { + const val = await event.zap(amount, content); - // Zap - const val = await event.zap(amount, content); - - if (val) { - setIsCompleted(true); - // close current window - await getCurrentWebviewWindow().close(); + if (val) { + setIsCompleted(true); + // close current window + await getCurrentWebviewWindow().close(); + } + } catch (e) { + await message(String(e), { + title: "Zap", + kind: "error", + }); + return; } - } catch (e) { - setIsLoading(false); - await message(String(e), { - title: "Zap", - kind: "error", - }); - } + }); }; return ( @@ -104,7 +102,7 @@ function Screen() { onClick={() => submit()} className="inline-flex items-center justify-center w-full h-10 font-medium rounded-xl bg-neutral-950 text-neutral-50 hover:bg-neutral-900 dark:bg-white/20 dark:hover:bg-white/30" > - {isCompleted ? "Zapped" : isLoading ? "Processing..." : "Zap"} + {isCompleted ? "Zapped" : isPending ? "Processing..." : "Zap"}
diff --git a/src/routes/zap.$id.tsx b/src/routes/zap.$id.tsx index fbff159c..c6746521 100644 --- a/src/routes/zap.$id.tsx +++ b/src/routes/zap.$id.tsx @@ -1,9 +1,23 @@ -import { NostrQuery } from "@/system"; +import { commands } from "@/commands.gen"; +import { LumeEvent } from "@/system"; +import type { NostrEvent } from "@/types"; import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/zap/$id")({ beforeLoad: async ({ params }) => { - const event = await NostrQuery.getEvent(params.id); - return { event }; + const res = await commands.getEvent(params.id); + + if (res.status === "ok") { + const data = res.data; + const raw: NostrEvent = JSON.parse(data.raw); + + if (data.parsed) { + raw.meta = data.parsed; + } + + return { event: new LumeEvent(raw) }; + } else { + throw new Error(res.error); + } }, }); diff --git a/src/system/account.ts b/src/system/account.ts deleted file mode 100644 index 1f220c90..00000000 --- a/src/system/account.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { type Result, commands } from "@/commands.gen"; -import type { Metadata } from "@/types"; - -export const NostrAccount = { - getAccounts: async () => { - const query = await commands.getAccounts(); - return query; - }, - loadAccount: async (npub: string) => { - const bunker: string = localStorage.getItem(`${npub}_bunker`); - let query: Result; - - if (bunker?.length && bunker?.startsWith("bunker://")) { - query = await commands.loadAccount(npub, bunker); - } else { - query = await commands.loadAccount(npub, null); - } - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - createAccount: async () => { - const query = await commands.createAccount(); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - createProfile: async (profile: Metadata) => { - const query = await commands.createProfile( - profile.name || "", - profile.display_name || "", - profile.about || "", - profile.picture || "", - profile.banner || "", - profile.nip05 || "", - profile.lud16 || "", - profile.website || "", - ); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - saveAccount: async (nsec: string, password = "") => { - const query = await commands.saveAccount(nsec, password); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - connectRemoteAccount: async (uri: string) => { - const connect = await commands.connectRemoteAccount(uri); - - if (connect.status === "ok") { - const npub = connect.data; - const parsed = new URL(uri); - parsed.searchParams.delete("secret"); - - // save connection string - localStorage.setItem(`${npub}_bunker`, parsed.toString()); - - return npub; - } else { - throw new Error(connect.error); - } - }, - setContactList: async (pubkeys: string[]) => { - const query = await commands.setContactList(pubkeys); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - loadWallet: async () => { - const query = await commands.loadWallet(); - - if (query.status === "ok") { - return Number.parseInt(query.data); - } else { - throw new Error(query.error); - } - }, - setWallet: async (uri: string) => { - const query = await commands.setWallet(uri); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - removeWallet: async () => { - const query = await commands.removeWallet(); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - getProfile: async () => { - const query = await commands.getCurrentProfile(); - - if (query.status === "ok") { - return JSON.parse(query.data) as Metadata; - } else { - return null; - } - }, - getContactList: async () => { - const query = await commands.getContactList(); - - if (query.status === "ok") { - return query.data; - } else { - return []; - } - }, - isContactListEmpty: async () => { - const query = await commands.isContactListEmpty(); - - if (query.status === "ok") { - return query.data; - } else { - return true; - } - }, - checkContact: async (pubkey: string) => { - const query = await commands.checkContact(pubkey); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - toggleContact: async (pubkey: string, alias?: string) => { - const query = await commands.toggleContact(pubkey, alias); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - f2f: async (npub: string) => { - const query = await commands.copyFriend(npub); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, -}; diff --git a/src/system/hooks/useEvent.ts b/src/system/hooks/useEvent.ts index cb2e8f6d..9122d70d 100644 --- a/src/system/hooks/useEvent.ts +++ b/src/system/hooks/useEvent.ts @@ -16,7 +16,7 @@ export function useEvent(id: string) { // Define query let query: Result; - let relayHint: string; + let relayHint: string = null; if (normalizeId.startsWith("nevent1")) { const decoded = nip19.decode(normalizeId); @@ -27,14 +27,11 @@ export function useEvent(id: string) { } } + console.log(relayHint); + // Build query - if (relayHint) { - try { - const url = new URL(relayHint); - query = await commands.getEventFrom(normalizeId, url.toString()); - } catch { - query = await commands.getEvent(normalizeId); - } + if (relayHint?.length) { + query = await commands.getEventFrom(normalizeId, relayHint); } else { query = await commands.getEvent(normalizeId); } diff --git a/src/system/index.ts b/src/system/index.ts index 52a21595..f1ffc835 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -1,6 +1,4 @@ export * from "./event"; -export * from "./account"; -export * from "./query"; export * from "./window"; export * from "./hooks/useEvent"; export * from "./hooks/useProfile"; diff --git a/src/system/query.ts b/src/system/query.ts deleted file mode 100644 index f89400e8..00000000 --- a/src/system/query.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { type Result, type RichEvent, commands } from "@/commands.gen"; -import type { LumeColumn, Metadata, NostrEvent, Relay } from "@/types"; -import { open } from "@tauri-apps/plugin-dialog"; -import { readFile } from "@tauri-apps/plugin-fs"; -import { relaunch } from "@tauri-apps/plugin-process"; -import { nip19 } from "nostr-tools"; -import { LumeEvent } from "./event"; - -function toLumeEvents(richEvents: RichEvent[]) { - const events = richEvents.map((item) => { - const nostrEvent: NostrEvent = JSON.parse(item.raw); - - if (item.parsed) { - nostrEvent.meta = item.parsed; - } else { - nostrEvent.meta = null; - } - - const lumeEvent = new LumeEvent(nostrEvent); - - return lumeEvent; - }); - - return events; -} - -export const NostrQuery = { - upload: async (filePath?: string) => { - const allowExts = [ - "png", - "jpeg", - "jpg", - "gif", - "mp4", - "mp3", - "webm", - "mkv", - "avi", - "mov", - ]; - - const selected = - filePath || - ( - await open({ - multiple: false, - filters: [ - { - name: "Media", - extensions: allowExts, - }, - ], - }) - ).path; - - // User cancelled action - if (!selected) return null; - - try { - const file = await readFile(selected); - 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; - } catch (e) { - throw new Error(String(e)); - } - }, - getNotifications: async () => { - const query = await commands.getNotifications(); - - if (query.status === "ok") { - const data = query.data.map((item) => JSON.parse(item) as NostrEvent); - const events = data.map((ev) => new LumeEvent(ev)); - - return events; - } else { - console.error(query.error); - return []; - } - }, - getProfile: async (pubkey: string) => { - const normalize = pubkey.replace("nostr:", "").replace(/[^\w\s]/gi, ""); - const query = await commands.getProfile(normalize); - - if (query.status === "ok") { - const profile: Metadata = JSON.parse(query.data); - return profile; - } else { - return null; - } - }, - getEvent: async (id: string, hint?: string) => { - // Validate ID - const normalizeId: string = id - .replace("nostr:", "") - .replace(/[^\w\s]/gi, ""); - - // Define query - let query: Result; - let relayHint: string = hint; - - if (normalizeId.startsWith("nevent1")) { - const decoded = nip19.decode(normalizeId); - if (decoded.type === "nevent") relayHint = decoded.data.relays[0]; - } - - // Build query - if (relayHint) { - try { - const url = new URL(relayHint); - query = await commands.getEventFrom(normalizeId, url.toString()); - } catch { - query = await commands.getEvent(normalizeId); - } - } else { - query = await commands.getEvent(normalizeId); - } - - if (query.status === "ok") { - const data = query.data; - const raw = JSON.parse(data.raw) as NostrEvent; - - if (data?.parsed) { - raw.meta = data.parsed; - } - - const event = new LumeEvent(raw); - - return event; - } else { - console.log("[getEvent]: ", query.error); - return null; - } - }, - getRepostEvent: async (event: LumeEvent) => { - try { - const embed: NostrEvent = JSON.parse(event.content); - const query = await commands.getEventMeta(embed.content); - - if (query.status === "ok") { - embed.meta = query.data; - const lumeEvent = new LumeEvent(embed); - return lumeEvent; - } - } catch { - const query = await commands.getEvent(event.repostId); - - if (query.status === "ok") { - const data = query.data; - const raw = JSON.parse(data.raw) as NostrEvent; - - if (data?.parsed) { - raw.meta = data.parsed; - } - - const event = new LumeEvent(raw); - - return event; - } else { - console.log("[getRepostEvent]: ", query.error); - return null; - } - } - }, - getGroupEvents: async (pubkeys: string[], asOf?: number) => { - const until: string = asOf && asOf > 0 ? asOf.toString() : undefined; - const query = await commands.getGroupEvents(pubkeys, until); - - if (query.status === "ok") { - const data = toLumeEvents(query.data); - return data; - } else { - return []; - } - }, - getGlobalEvents: async (asOf?: number) => { - const until: string = asOf && asOf > 0 ? asOf.toString() : undefined; - const query = await commands.getGlobalEvents(until); - - if (query.status === "ok") { - const data = toLumeEvents(query.data); - return data; - } else { - return []; - } - }, - getHashtagEvents: async (hashtags: string[], asOf?: number) => { - const until: string = asOf && asOf > 0 ? asOf.toString() : undefined; - const nostrTags = hashtags.map((tag) => tag.replace("#", "")); - const query = await commands.getHashtagEvents(nostrTags, until); - - if (query.status === "ok") { - const data = toLumeEvents(query.data); - return data; - } else { - return []; - } - }, - verifyNip05: async (pubkey: string, nip05?: string) => { - const query = await commands.verifyNip05(pubkey, nip05); - - if (query.status === "ok") { - return query.data; - } else { - return false; - } - }, - getNstore: async (key: string) => { - const query = await commands.getLumeStore(key); - - if (query.status === "ok") { - const data = query.data ? JSON.parse(query.data) : null; - return data; - } else { - return null; - } - }, - setNstore: async (key: string, value: string) => { - const query = await commands.setLumeStore(key, value); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - getUserSettings: async () => { - const query = await commands.getSettings(); - - if (query.status === "ok") { - return query.data; - } else { - return query.error; - } - }, - setUserSettings: async (newSettings: string) => { - const query = await commands.setNewSettings(newSettings); - - if (query.status === "ok") { - return query.data; - } else { - return query.error; - } - }, - setColumns: async (columns: LumeColumn[]) => { - const key = "lume_v4:columns"; - const content = JSON.stringify(columns); - const query = await commands.setLumeStore(key, content); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - getRelays: async () => { - const query = await commands.getRelays(); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - }, - connectRelay: async (url: string) => { - const relayUrl = new URL(url); - - if (relayUrl.protocol === "wss:" || relayUrl.protocol === "ws:") { - const query = await commands.connectRelay(relayUrl.toString()); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - } - }, - removeRelay: async (url: string) => { - const relayUrl = new URL(url); - - if (relayUrl.protocol === "wss:" || relayUrl.protocol === "ws:") { - const query = await commands.removeRelay(relayUrl.toString()); - - if (query.status === "ok") { - return query.data; - } else { - throw new Error(query.error); - } - } - }, - getBootstrapRelays: async () => { - const query = await commands.getBootstrapRelays(); - - if (query.status === "ok") { - const relays: Relay[] = []; - - for (const item of query.data) { - const line = item.split(","); - const url = line[0]; - const purpose = line[1] ?? ""; - - relays.push({ url, purpose }); - } - - return relays; - } else { - return []; - } - }, - saveBootstrapRelays: async (relays: string[]) => { - const text = relays - .map((relay) => Object.values(relay).join(",")) - .join("\n"); - const query = await commands.saveBootstrapRelays(text); - - if (query.status === "ok") { - return await relaunch(); - } else { - throw new Error(query.error); - } - }, -}; diff --git a/src/system/window.ts b/src/system/window.ts index a803602d..9fa06b35 100644 --- a/src/system/window.ts +++ b/src/system/window.ts @@ -114,7 +114,7 @@ export const LumeWindow = { throw new Error(query.error); } }, - openZap: async (id: string) => { + openZap: async (id: string, account?: string) => { const wallet = await commands.loadWallet(); if (wallet.status === "ok") { @@ -129,7 +129,7 @@ export const LumeWindow = { hidden_title: true, }); } else { - await LumeWindow.openSettings("bitcoin-connect"); + await LumeWindow.openSettings(account, "bitcoin-connect"); } }, openSettings: async (account: string, path?: string) => {