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:
@@ -7,6 +7,7 @@ import useSWR from 'swr';
|
||||
import type { ProfileDTO } from '@musicshare/shared';
|
||||
import { Avatar } from '@/components/Avatar';
|
||||
import { NowPlayingCard } from '@/components/NowPlayingCard';
|
||||
import { RecentPlays } from '@/components/RecentPlays';
|
||||
import { TrackCard } from '@/components/TrackCard';
|
||||
import { api, fetcher, useMe } from '@/lib/api';
|
||||
import { useRealtime } from '@/lib/useRealtime';
|
||||
@@ -20,7 +21,9 @@ export default function ProfilePage() {
|
||||
// Live updates arrive for people you follow (and yourself)
|
||||
useRealtime(Boolean(me), (msg) => {
|
||||
if (msg.type === 'now_playing' && profile && msg.data.user.id === profile.user.id) {
|
||||
mutate({ ...profile, nowPlaying: msg.data }, { revalidate: false });
|
||||
// A new track moves the previous one into "Recently played", so refetch; otherwise patch in place.
|
||||
const newTrack = msg.data.track.id !== profile.nowPlaying?.track.id;
|
||||
mutate({ ...profile, nowPlaying: msg.data }, { revalidate: newTrack });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -94,6 +97,8 @@ export default function ProfilePage() {
|
||||
) : (
|
||||
<p className="muted">Nothing playing right now.</p>
|
||||
)}
|
||||
|
||||
<RecentPlays plays={profile.recent} preferred={preferred} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user