103 lines
3.4 KiB
React
103 lines
3.4 KiB
React
import { useState } from 'react'
|
|
import { Link, useNavigate } from 'react-router-dom'
|
|
import { apiFetch } from '../api/http'
|
|
|
|
export default function SettingsPage() {
|
|
const userId = localStorage.getItem('userId') ?? ''
|
|
const [recipient, setRecipient] = useState('')
|
|
const [status, setStatus] = useState(null)
|
|
const [busy, setBusy] = useState(false)
|
|
const [copied, setCopied] = useState(false)
|
|
const navigate = useNavigate()
|
|
|
|
async function copyId() {
|
|
if (!userId) return
|
|
try {
|
|
await navigator.clipboard.writeText(userId)
|
|
setCopied(true)
|
|
setTimeout(() => setCopied(false), 1500)
|
|
} catch {
|
|
setStatus({ kind: 'err', text: 'Clipboard unavailable' })
|
|
}
|
|
}
|
|
|
|
async function addConnection() {
|
|
const id = recipient.trim()
|
|
if (!id) return
|
|
setBusy(true)
|
|
setStatus(null)
|
|
try {
|
|
await apiFetch('GET', '/connection', { query: { recipient: id } })
|
|
setStatus({ kind: 'ok', text: 'Connection created' })
|
|
setRecipient('')
|
|
} catch (err) {
|
|
setStatus({ kind: 'err', text: err.message ?? String(err) })
|
|
} finally {
|
|
setBusy(false)
|
|
}
|
|
}
|
|
|
|
function logout() {
|
|
localStorage.removeItem('token')
|
|
localStorage.removeItem('userId')
|
|
navigate('/auth')
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-900 text-white flex justify-center py-10 px-4">
|
|
<div className="w-full max-w-md flex flex-col gap-8">
|
|
<div className="flex items-center justify-between">
|
|
<Link to="/" className="text-blue-400 hover:text-blue-300 text-sm">← Back</Link>
|
|
<button
|
|
onClick={logout}
|
|
className="text-gray-400 hover:text-red-400 text-sm"
|
|
>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
|
|
<section className="flex flex-col gap-2">
|
|
<h2 className="text-lg font-semibold">Your user ID</h2>
|
|
<div className="flex items-stretch gap-2">
|
|
<code className="flex-1 bg-gray-800 text-gray-200 text-xs px-3 py-2 rounded break-all">
|
|
{userId || '(missing)'}
|
|
</code>
|
|
<button
|
|
onClick={copyId}
|
|
disabled={!userId}
|
|
className="px-3 py-2 rounded bg-gray-700 hover:bg-gray-600 text-sm disabled:opacity-50 shrink-0"
|
|
>
|
|
{copied ? 'Copied' : 'Copy'}
|
|
</button>
|
|
</div>
|
|
<p className="text-xs text-gray-500">Share this with someone so they can add you.</p>
|
|
</section>
|
|
|
|
<section className="flex flex-col gap-2">
|
|
<h2 className="text-lg font-semibold">Add connection</h2>
|
|
<input
|
|
className="bg-gray-800 text-white text-sm px-3 py-2 rounded outline-none focus:ring-2 focus:ring-blue-500"
|
|
placeholder="Recipient user UUID"
|
|
value={recipient}
|
|
onChange={e => setRecipient(e.target.value)}
|
|
onKeyDown={e => { if (e.key === 'Enter' && !busy) addConnection() }}
|
|
autoComplete="off"
|
|
/>
|
|
<button
|
|
onClick={addConnection}
|
|
disabled={busy || !recipient.trim()}
|
|
className="self-end px-4 py-2 rounded bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-50"
|
|
>
|
|
{busy ? 'Adding…' : 'Add'}
|
|
</button>
|
|
{status && (
|
|
<p className={`text-sm ${status.kind === 'ok' ? 'text-green-400' : 'text-red-400'}`}>
|
|
{status.text}
|
|
</p>
|
|
)}
|
|
</section>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|