Web: redesign, logo, PWA, QR codes, Spotify guidance

- Album-art-tinted cards that crossfade on song change, equalizer bars,
  live avatar rings, drifting ambient colour, Space Grotesk display font,
  brand purple-to-blue gradient from the logo
- Feed cards rise in and glide to new positions (motion); respects
  reduced-motion
- New logo as nav mark, favicon and Apple touch icon
- Installable PWA: manifest, maskable icons, small service worker
  (cached build assets + offline page), security headers
- Show-QR-code on your own profile; centred profile header with stats
- Settings explains connecting Spotify via Last.fm or ListenBrainz

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-05 19:16:40 -04:00
co-authored by Claude Opus 5.5
parent 586acdbeab
commit 17ae36064c
23 changed files with 551 additions and 152 deletions
+35 -14
View File
@@ -1,8 +1,10 @@
'use client';
import { AnimatePresence, motion } from 'motion/react';
import Link from 'next/link';
import useSWR from 'swr';
import { sortFeed, type FeedItemDTO, type ProfileDTO } from '@musicshare/shared';
import { Equalizer } from '@/components/Equalizer';
import { NowPlayingCard } from '@/components/NowPlayingCard';
import { SharingControls } from '@/components/SharingControls';
import { UserSearch } from '@/components/UserSearch';
@@ -70,26 +72,45 @@ function Feed() {
{feed?.length === 0 && (
<p className="muted">Nothing yet. Follow some friends and their music will show up here live.</p>
)}
{feed?.map((np) => (
<NowPlayingCard
key={np.user.id}
np={np}
preferred={me?.preferredService}
pinned={Boolean(np.pinnedAt)}
onTogglePin={() => togglePin(np)}
/>
))}
{/* Cards rise in on load and glide to new positions when someone pins or starts playing. */}
<AnimatePresence initial={true}>
{feed?.map((np, i) => (
<motion.div
key={np.user.id}
layout
initial={{ opacity: 0, y: 24, scale: 0.98 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, scale: 0.96 }}
transition={{ type: 'spring', stiffness: 380, damping: 32, delay: Math.min(i, 8) * 0.05 }}
>
<NowPlayingCard
np={np}
preferred={me?.preferredService}
pinned={Boolean(np.pinnedAt)}
onTogglePin={() => togglePin(np)}
/>
</motion.div>
))}
</AnimatePresence>
</div>
);
}
function Landing() {
return (
<section className="hero">
<h1>See what your friends are playing.</h1>
<motion.section
className="hero"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
>
<Equalizer playing />
<h1>
See what your friends are <span className="gradient-text">playing.</span>
</h1>
<p>
Connect Spotify, Last.fm, ListenBrainz and more. When a friend on Apple Music is playing something you like,
one tap opens it in your app.
Share what you play on Spotify, Apple Music, YouTube Music, Plex and more. When a friend on Apple Music is
playing something you like, one tap opens it in your app.
</p>
<div className="row">
<Link href="/register" className="btn btn-primary">
@@ -99,6 +120,6 @@ function Landing() {
Log in
</Link>
</div>
</section>
</motion.section>
);
}