Files
musicshare/apps/web/src/components/RecentPlays.tsx
T
chrisandClaude Opus 5.5 4402171f46 Comments on plays
- Anyone who can see a play can read its comments; the player and the people they follow can write.
  The author, the player and admins can delete; comments can be reported (admins see the text).
- Notifications: "commented on" for the player, "replied on" for earlier commenters, each with its
  own setting. Reaction and comment notifications now open the play.
- A 💬 chip with the count next to reactions on the feed and recent plays, opening the thread on
  the app (/play/[id]) and the website. Threads refresh every few seconds while open.
- Blocking removes comments between the two people. Plays with comments survive history trimming.
- Privacy page updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Y2iUsKEWDuRGjK5FPmcUP
2026-10-15 13:32:14 -04:00

67 lines
2.3 KiB
TypeScript

'use client';
import { Reactions } from './Reactions';
import { SERVICE_LABELS, openUrl, timeAgo, type ProfileDTO, type RecentPlayDTO, type StreamingService } from '@musicshare/shared';
export function RecentPlays({
plays,
preferred,
attribution = {},
ownerId,
}: {
plays: RecentPlayDTO[];
/** Whose plays these are (they can't react to their own). */
ownerId: string;
preferred: StreamingService | null | undefined;
attribution?: ProfileDTO['attribution'];
}) {
const credits = (['lastfm', 'listenbrainz'] as const).filter((s) => attribution[s] && plays.some((p) => p.source === s));
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.listenId ?? `${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>
)}
<Reactions listenId={p.listenId} reactions={p.reactions} ownerId={ownerId} commentCount={p.commentCount ?? 0} />
</li>
);
})}
</ul>
{credits.map((s) => (
<p key={s} className="muted credit">
Some plays via{' '}
<a href={attribution[s]} target="_blank" rel="noreferrer">
{s === 'lastfm' ? 'Last.fm' : 'ListenBrainz'}
</a>
</p>
))}
</section>
);
}