Add Next.js web app

Feed with live updates and pinning, profiles with follow/pin, featured song
picker, settings for all music services, and account deletion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
2026-10-03 09:59:02 -04:00
co-authored by Claude Opus 5.5
parent 6791b62d87
commit 5004beb10d
22 changed files with 1127 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
'use client';
import Link from 'next/link';
import { useState } from 'react';
import useSWR from 'swr';
import type { UserSummaryDTO } from '@musicshare/shared';
import { fetcher } from '@/lib/api';
import { useDebounced } from '@/lib/useDebounced';
import { Avatar } from './Avatar';
export function UserSearch() {
const [q, setQ] = useState('');
const debounced = useDebounced(q.trim(), 250);
const { data } = useSWR<UserSummaryDTO[]>(
debounced.length >= 2 ? `/users/search?q=${encodeURIComponent(debounced)}` : null,
fetcher,
);
return (
<div className="search">
<input placeholder="Find friends by username" value={q} onChange={(e) => setQ(e.target.value)} />
{data && debounced.length >= 2 && (
<ul className="search-results">
{data.length === 0 && <li className="muted">No one found</li>}
{data.map((u) => (
<li key={u.id}>
<Link href={`/u/${u.username}`} className="user-chip">
<Avatar user={u} size={28} />
<span>{u.displayName ?? u.username}</span>
<span className="muted">@{u.username}</span>
</Link>
</li>
))}
</ul>
)}
</div>
);
}