diff --git a/src/app/users/components/modal.tsx b/src/app/users/components/modal.tsx deleted file mode 100644 index 4765ae11..00000000 --- a/src/app/users/components/modal.tsx +++ /dev/null @@ -1,418 +0,0 @@ -import { NDKEvent, NDKKind, NDKUserProfile } from '@nostr-dev-kit/ndk'; -import * as Dialog from '@radix-ui/react-dialog'; -import { useQueryClient } from '@tanstack/react-query'; -import { message, open } from '@tauri-apps/plugin-dialog'; -import { readBinaryFile } from '@tauri-apps/plugin-fs'; -import { fetch } from '@tauri-apps/plugin-http'; -import { useEffect, useState } from 'react'; -import { useForm } from 'react-hook-form'; - -import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; - -import { - CancelIcon, - CheckCircleIcon, - LoaderIcon, - PlusIcon, - UnverifiedIcon, -} from '@shared/icons'; - -interface NIP05 { - names: { - [key: string]: string; - }; -} - -export function EditProfileModal() { - const queryClient = useQueryClient(); - - const [isOpen, setIsOpen] = useState(false); - const [loading, setLoading] = useState(false); - const [picture, setPicture] = useState(''); - const [banner, setBanner] = useState(''); - const [nip05, setNIP05] = useState({ verified: false, text: '' }); - - const { db } = useStorage(); - const { ndk } = useNDK(); - const { - register, - handleSubmit, - reset, - setError, - formState: { isValid, errors }, - } = useForm({ - defaultValues: async () => { - const res: NDKUserProfile = queryClient.getQueryData(['user', db.account.pubkey]); - if (res.image) { - setPicture(res.image); - } - if (res.banner) { - setBanner(res.banner); - } - if (res.nip05) { - setNIP05((prev) => ({ ...prev, text: res.nip05 })); - } - return res; - }, - }); - - const verifyNIP05 = async (nip05: string) => { - 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', - }, - }); - - 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] !== db.account.pubkey) return false; - return true; - } - return false; - }; - - const uploadAvatar = async () => { - try { - // start loading - setLoading(true); - - const selected = await open({ - multiple: false, - filters: [ - { - name: 'Image', - extensions: ['png', 'jpeg', 'jpg', 'gif'], - }, - ], - }); - - if (!selected) { - setLoading(false); - return; - } - - 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) { - const json = await res.json(); - const content = json.data[0]; - - setPicture(content.url); - - // stop loading - setLoading(false); - } - } catch (e) { - // stop loading - setLoading(false); - await message(`Upload failed, error: ${e}`, { title: 'Lume', type: 'error' }); - } - }; - - const uploadBanner = async () => { - try { - // start loading - setLoading(true); - - const selected = await open({ - multiple: false, - filters: [ - { - name: 'Image', - extensions: ['png', 'jpeg', 'jpg', 'gif'], - }, - ], - }); - - if (!selected) { - setLoading(false); - return; - } - - 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) { - const json = await res.json(); - const content = json.data[0]; - - setBanner(content.url); - - // stop loading - setLoading(false); - } - } catch (e) { - // stop loading - setLoading(false); - await message(`Upload failed, error: ${e}`, { title: 'Lume', type: 'error' }); - } - }; - - const onSubmit = async (data: NDKUserProfile) => { - // start loading - setLoading(true); - - const content = { - ...data, - username: data.name, - display_name: data.name, - bio: data.about, - image: data.picture, - }; - - const event = new NDKEvent(ndk); - event.kind = NDKKind.Metadata; - event.tags = []; - - if (data.nip05) { - const nip05IsVerified = await verifyNIP05(data.nip05); - if (nip05IsVerified) { - event.content = JSON.stringify({ ...content, nip05: data.nip05 }); - } else { - setNIP05((prev) => ({ ...prev, verified: false })); - setError('nip05', { - type: 'manual', - message: "Can't verify your Lume ID / NIP-05, please check again", - }); - } - } else { - event.content = JSON.stringify(content); - } - - const publishedRelays = await event.publish(); - - if (publishedRelays) { - // invalid cache - queryClient.invalidateQueries({ - queryKey: ['user', db.account.pubkey] - }); - // reset form - reset(); - // reset state - setLoading(false); - setIsOpen(false); - setPicture('https://void.cat/d/5VKmKyuHyxrNMf9bWSVPih'); - setBanner(null); - } else { - setLoading(false); - } - }; - - useEffect(() => { - if (!nip05.verified && /\S+@\S+\.\S+/.test(nip05.text)) { - verifyNIP05(nip05.text); - } - }, [nip05.text]); - - return ( - - - - - - - -
-
-
- - Edit profile - - - - -
-
-
-
- - -
-
- {banner ? ( - user's banner - ) : ( -
- )} -
- -
-
-
-
- user's avatar -
- -
-
-
-
-
-
- - -
-
- -
- -
- {nip05.verified ? ( - - - Verified - - ) : ( - - - Unverified - - )} -
- {errors.nip05 && ( -

- {errors.nip05.message.toString()} -

- )} -
-
-
- -