Unlock everything, add a donation link, credit Last.fm, nudge re-linking

- Every feature is free for everyone (the plan system stays for later);
  remaining limits are abuse guards
- Optional website donation link (NEXT_PUBLIC_DONATION_URL). Not shown in
  the mobile apps, where tips to the developer generally need in-app
  purchase
- Last.fm/ListenBrainz-sourced plays link to the user's profile, as
  Last.fm's API terms require
- Connections track when they last saw activity; a scrobbler silent for
  over a week prompts re-linking Spotify (its link expires every 6 months)

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-04 08:13:54 -04:00
co-authored by Claude Opus 5.5
parent 91a78a8ad4
commit 63d618f0e3
33 changed files with 1277 additions and 38 deletions
+14
View File
@@ -5,13 +5,16 @@ import { Suspense, useState } from 'react';
import useSWR from 'swr';
import {
SERVICE_LABELS,
SPOTIFY_LINK_PAGES,
STREAMING_SERVICES,
isQuietScrobbler,
type ConnectionDTO,
type MeDTO,
type NowPlayingProvider,
type TrackSearchResultDTO,
} from '@musicshare/shared';
import { SharingControls } from '@/components/SharingControls';
import { SupportCard } from '@/components/SupportLink';
import { api, fetcher, useMe } from '@/lib/api';
import { timeAgo } from '@musicshare/shared';
import { useDebounced } from '@/lib/useDebounced';
@@ -36,6 +39,7 @@ function Settings() {
<SharingControls />
<Connections />
<FeaturedPicker />
<SupportCard />
<DangerZone />
</div>
);
@@ -189,6 +193,16 @@ function Connections() {
)}
</div>
{connections.filter((c) => isQuietScrobbler(c)).map((c) => (
<div className="notice" key={`quiet-${c.provider}`}>
Nothing new from {SERVICE_LABELS[c.provider]} in over a week. If you listen on Spotify, its link to{' '}
{SERVICE_LABELS[c.provider]} expires every 6 months.{' '}
<a href={SPOTIFY_LINK_PAGES[c.provider as 'lastfm' | 'listenbrainz']} target="_blank" rel="noreferrer">
Re-link Spotify
</a>
.
</div>
))}
{spotifyLimited && (
<div className="notice">
<strong>Spotify sign-in is limited during beta.</strong> Spotify only lets a few accounts sign in to new apps