Add recently played to profiles

A play is recorded when a new track starts (pausing and resuming doesn't
count; returning after 10+ minutes does). Profiles show the last 10,
excluding the track playing now; 50 are kept per user. Recent plays are
free on every plan; the paid flag is renamed to fullHistory.

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 16:02:38 -04:00
co-authored by Claude Opus 5.5
parent 87353ccca3
commit ece9c4fd64
14 changed files with 1147 additions and 11 deletions
+44
View File
@@ -0,0 +1,44 @@
'use client';
import { SERVICE_LABELS, openUrl, timeAgo, type RecentPlayDTO, type StreamingService } from '@musicshare/shared';
export function RecentPlays({ plays, preferred }: { plays: RecentPlayDTO[]; preferred: StreamingService | null | undefined }) {
if (plays.length === 0) return null;
return (
<section className="card">
<h2>Recently played</h2>
<ul className="recent">
{plays.map((p, i) => {
const target = preferred ? openUrl(p.track, preferred) : null;
const row = (
<>
{p.track.artworkUrl ? <img className="artwork-sm" src={p.track.artworkUrl} alt="" /> : <div className="artwork-sm artwork-empty">♪</div>}
<span className="recent-text">
<span className="recent-title">{p.track.title}</span>
<span className="muted">{p.track.artist}</span>
</span>
<span className="muted recent-time">{timeAgo(p.playedAt)}</span>
</>
);
return (
<li key={`${p.playedAt}-${i}`}>
{target ? (
<a
className="recent-row"
href={target.url}
target="_blank"
rel="noreferrer"
title={`${target.exact ? 'Open' : 'Search'} in ${SERVICE_LABELS[preferred!]}`}
>
{row}
</a>
) : (
<div className="recent-row">{row}</div>
)}
</li>
);
})}
</ul>
</section>
);
}