CI / check (push) Canceled after 0s
- Sign-up emails a confirmation link (24h); unconfirmed accounts see a banner on the web with "send it again", and status in Settings. - Change email (needs the password): the new address gets a confirmation link and only replaces the old one once clicked; the old address gets a heads-up with a reset-password link. Pending changes can be cancelled. - Change password (needs the current one): signs out other sessions. - Password fields have Show/Hide on web and mobile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
69 lines
2.0 KiB
TypeScript
69 lines
2.0 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/navigation';
|
|
import { useState } from 'react';
|
|
import { api, client, useMe } from '@/lib/api';
|
|
import { SupportLink } from './SupportLink';
|
|
|
|
export function Nav() {
|
|
const { me, mutate } = useMe();
|
|
const router = useRouter();
|
|
const logout = async () => {
|
|
await api('/auth/logout', { method: 'POST', body: {} });
|
|
await mutate(undefined, { revalidate: true });
|
|
router.push('/');
|
|
};
|
|
return (
|
|
<>
|
|
<nav className="nav">
|
|
<Link href="/" className="brand">
|
|
<img className="brand-logo" src="/logo.png" alt="" width={30} height={30} />
|
|
musicshare
|
|
</Link>
|
|
<div className="nav-links">
|
|
<SupportLink />
|
|
{me ? (
|
|
<>
|
|
<Link href="/">Feed</Link>
|
|
<Link href={`/u/${me.username}`}>Profile</Link>
|
|
<Link href="/settings">Settings</Link>
|
|
{me.role === 'admin' && <Link href="/admin">Admin</Link>}
|
|
<button className="link-button" onClick={logout}>
|
|
Log out
|
|
</button>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Link href="/login">Log in</Link>
|
|
<Link href="/register" className="btn btn-primary">
|
|
Sign up
|
|
</Link>
|
|
</>
|
|
)}
|
|
</div>
|
|
</nav>
|
|
{me && !me.emailVerified && <VerifyBanner email={me.pendingEmail ?? me.email} />}
|
|
</>
|
|
);
|
|
}
|
|
|
|
/** Until they confirm, a forgotten password can't be reset; nudge them. */
|
|
function VerifyBanner({ email }: { email: string }) {
|
|
const [sent, setSent] = useState(false);
|
|
const resend = async () => {
|
|
try {
|
|
await client.resendVerification();
|
|
setSent(true);
|
|
} catch (err) {
|
|
alert((err as Error).message);
|
|
}
|
|
};
|
|
return (
|
|
<div className="banner">
|
|
Confirm your email: we sent a link to <strong>{email}</strong>.{' '}
|
|
{sent ? <span className="muted">Sent again.</span> : <button className="link-button" onClick={resend}>Send it again</button>}
|
|
</div>
|
|
);
|
|
}
|