import { html, useState, useEffect, useCallback, } from './vendor/htm-preact.js'; import { t } from './i18n.js'; import { Icon } from './icon.js'; import { hubFetch } from './hub-client.js'; export function ProfilePage({ user, onLogout }) { const [nodeKey, setNodeKey] = useState(''); const [currentNodeKey, setCurrentNodeKey] = useState(null); const [nodeKeyStatus, setNodeKeyStatus] = useState(''); const [nodeKeyLoading, setNodeKeyLoading] = useState(false); const [email, setEmail] = useState(''); const [emailDraft, setEmailDraft] = useState(''); const [emailEditing, setEmailEditing] = useState(false); const [emailSaving, setEmailSaving] = useState(false); const [emailStatus, setEmailStatus] = useState(''); const [pinCount, setPinCount] = useState( () => (window.MeshBayTransport?.pinnedNodeCount?.() ?? 0)); const [delOpen, setDelOpen] = useState(false); const [delPass, setDelPass] = useState(''); const [delError, setDelError] = useState(''); const [deleting, setDeleting] = useState(false); const deleteAccount = useCallback(async (e) => { e.preventDefault(); setDeleting(true); setDelError(''); try { const authKey = await window.MeshBayKeys.deriveAuthKey(delPass, user.username); await hubFetch('/v1/users/me', { method: 'DELETE', token: user.token, body: { auth_key: authKey }, }); onLogout(); } catch (err) { setDelError(err.message); } finally { setDeleting(false); } }, [delPass, user]); const clearPins = useCallback(() => { window.MeshBayTransport?.clearNodePin?.(); setPinCount(window.MeshBayTransport?.pinnedNodeCount?.() ?? 0); }, []); useEffect(() => { hubFetch('/v1/users/me', { token: user.token }) .then(data => { if (data.email) { setEmail(data.email); setEmailDraft(data.email); } }) .catch(() => {}); hubFetch(`/v1/users/${user.username}/pubkeys`, { token: user.token }) .then(data => { if (data.pk_node_ed25519) setCurrentNodeKey(data.pk_node_ed25519); }) .catch(() => {}); }, [user.username, user.token]); const saveEmail = useCallback(async () => { const val = emailDraft.trim(); if (!val || val === email) { setEmailEditing(false); return; } setEmailSaving(true); setEmailStatus(''); try { await hubFetch('/v1/users/me', { method: 'PATCH', token: user.token, body: { email: val }, }); setEmail(val); setEmailEditing(false); setEmailStatus(t('settings.email_saved')); setTimeout(() => setEmailStatus(''), 3000); } catch (e) { setEmailStatus(e.message); } finally { setEmailSaving(false); } }, [emailDraft, email, user.token]); const submitNodeKey = useCallback(async () => { const key = nodeKey.trim(); if (!key) return; setNodeKeyLoading(true); setNodeKeyStatus(''); try { await hubFetch('/v1/users/me/node_key', { method: 'PUT', token: user.token, body: { pk_node_ed25519: key }, }); setCurrentNodeKey(key); setNodeKey(''); setNodeKeyStatus(t('settings.node_key_success')); } catch (e) { setNodeKeyStatus(e.message); } finally { setNodeKeyLoading(false); } }, [nodeKey, user.token]); return html`

${t('profile.title')}

${t('settings.profile')}

${t('settings.username')} ${user.username}
${t('settings.email')} ${emailEditing ? html` setEmailDraft(e.target.value)} onKeyDown=${e => e.key === 'Enter' && saveEmail()} style="font-size:0.9em;padding:4px 8px;border:1px solid var(--border);border-radius:4px" /> ` : html` ${email || '—'} ` }
${emailStatus && html`

${emailStatus}

`}

${t('settings.node_key')}

${t('settings.node_key_desc')}

${currentNodeKey && html`
${t('settings.node_key_current')} ${currentNodeKey}
`}
setNodeKey(e.target.value)} onKeyDown=${e => e.key === 'Enter' && submitNodeKey()} />
${nodeKeyStatus && html`

${nodeKeyStatus}

`}

${t('settings.node_pins')}

${t('settings.node_pins_hint')}

${t('settings.node_pins_count', { n: pinCount })}

${t('settings.danger')}

${t('settings.delete_hint')}

${delError && html`

${delError}

`} ${!delOpen ? html`` : html`

${t('settings.delete_confirm')}

setDelPass(e.target.value)} required />
`}
`; }