import { createSignal, type Component, batch, onMount, For, JSX, Show } from 'solid-js'; import { createMutation } from '@tanstack/solid-query'; import ArrowLeft from 'heroicons/24/outline/arrow-left.svg'; import omit from 'lodash/omit'; import omitBy from 'lodash/omitBy'; import BasicModal from '@/components/modal/BasicModal'; import useConfig from '@/core/useConfig'; import { useTranslation } from '@/i18n/useTranslation'; import { Profile } from '@/nostr/event/Profile'; import useCommands from '@/nostr/useCommands'; import useProfile from '@/nostr/useProfile'; import usePubkey from '@/nostr/usePubkey'; import ensureNonNull from '@/utils/ensureNonNull'; import timeout from '@/utils/timeout'; export type ProfileEditProps = { onClose: () => void; }; const LNURLRegexString = '(LNURL1[AC-HJ-NP-Z02-9]+|lnurl1[ac-hj-np-z02-9]+)'; const InternetIdentifierRegexString = '[-_a-zA-Z0-9.]+@[-a-zA-Z0-9.]+'; const LUDAddressRegexString = `^(${LNURLRegexString}|${InternetIdentifierRegexString})$`; const LNURLRegex = new RegExp(`^${LNURLRegexString}$`); const InternetIdentifierRegex = new RegExp(`^${InternetIdentifierRegexString}$`); const isLNURL = (s: string) => LNURLRegex.test(s); const isInternetIdentifier = (s: string) => InternetIdentifierRegex.test(s); const ProfileEdit: Component = (props) => { const i18n = useTranslation(); const pubkey = usePubkey(); const { config } = useConfig(); const [picture, setPicture] = createSignal(''); const [banner, setBanner] = createSignal(''); const [name, setName] = createSignal(''); const [displayName, setDisplayName] = createSignal(''); const [about, setAbout] = createSignal(''); const [website, setWebsite] = createSignal(''); const [nip05, setNIP05] = createSignal(''); const [lightningAddress, setLightningAddress] = createSignal(''); const { profile, invalidateProfile, query } = useProfile(() => ensureNonNull([pubkey()] as const)(([pubkeyNonNull]) => ({ pubkey: pubkeyNonNull, })), ); const { updateProfile } = useCommands(); const mutation = createMutation(() => ({ mutationKey: ['updateProfile'], mutationFn: (...params: Parameters) => updateProfile(...params).then((promeses) => Promise.allSettled(promeses.map(timeout(10000)))), onSuccess: (results) => { const succeeded = results.filter((res) => res.status === 'fulfilled').length; const failed = results.length - succeeded; if (succeeded === results.length) { window.alert(i18n()('profile.edit.updateSucceeded')); } else if (succeeded > 0) { window.alert(i18n()('profile.edit.failedToUpdatePartially', { count: failed })); } else { window.alert(i18n()('profile.edit.failedToUpdate')); } invalidateProfile() .then(() => query.refetch()) .catch((err) => console.error(err)); props.onClose(); }, onError: (err) => { console.error('failed to delete', err); }, })); const loading = () => query.isPending || mutation.isPending; const disabled = () => loading(); setInterval(() => console.log(query.isPending, mutation.isPending), 1000); const otherProperties = () => omit(profile(), [ 'picture', 'banner', 'name', 'display_name', 'about', 'website', 'nip05', 'lud06', 'lud16', ]); const handleSubmit: JSX.EventHandler = (ev) => { ev.preventDefault(); const p = pubkey(); if (p == null) return; const newProfile: Profile = omitBy( { picture: picture(), banner: banner(), name: name(), display_name: displayName(), about: about(), website: website(), nip05: nip05(), lud06: isLNURL(lightningAddress()) ? lightningAddress() : null, lud16: isInternetIdentifier(lightningAddress()) ? lightningAddress() : null, }, (v) => v == null || v.length === 0, ); mutation.mutate({ relayUrls: config().relayUrls, pubkey: p, profile: newProfile, otherProperties: otherProperties(), }); }; const ignoreEnter = (ev: KeyboardEvent) => ev.key === 'Enter' && ev.preventDefault(); onMount(() => { const currentProfile = profile(); if (currentProfile == null) return; query.refetch().catch((err) => console.error(err)); batch(() => { setPicture((current) => currentProfile.picture ?? current); setBanner((current) => currentProfile.banner ?? current); setName((current) => currentProfile.name ?? current); setDisplayName((current) => currentProfile.display_name ?? current); setAbout((current) => currentProfile.about ?? current); setWebsite((current) => currentProfile.website ?? current); setNIP05((current) => currentProfile.nip05 ?? current); if (currentProfile.lud16 != null) { setLightningAddress(currentProfile.lud16); } else if (currentProfile.lud06 != null) { setLightningAddress(currentProfile.lud06); } }); }); return ( } onClose={props.onClose}>
0} fallback={
} keyed>
header
0}> user icon
{i18n()('general.loading')}
setPicture(ev.currentTarget.value)} onKeyDown={ignoreEnter} />
setBanner(ev.currentTarget.value)} onKeyDown={ignoreEnter} />
@ setName(ev.currentTarget.value)} onKeyDown={ignoreEnter} />
setDisplayName(ev.currentTarget.value)} onKeyDown={ignoreEnter} />