Files
musicshare/apps/web/src/components/Nav.tsx
T
chrisandClaude Opus 5.5 85e1643c29
CI / check (push) Canceled after 0s
Email confirmation, change email/password, show/hide passwords
- 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
2026-10-06 13:05:26 -04:00

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>
);
}