The Spotify Web API integration stopped working, leaving /spotify unable to
suggest songs at all. Replace the artist/album/track search flow with a single
box where the user pastes a link to the track on a music service.
Links are checked against a hardcoded allow-list of legal streaming and store
domains so links to ripped audio cannot be submitted. The check lives in the
service layer, not just the page: any authenticated user can POST to
/api/ganjoor/song directly and bypass the UI. Hosts are matched against the
full host or a dot-prefixed suffix, so look-alikes such as
open.spotify.com.evil.com are rejected; https is required, and userinfo and
non-default ports are refused. Accepted URLs are canonicalised - https,
lowercased host, tracking parameters stripped - so the same track always yields
the same stored URL and duplicate detection actually works.
Links are stored under one new type, PoemMusicTrackType.MusicUrl, with the
platform derived from the host at render time. That needs no migration or
backfill, and supporting another service later needs no new enum value. The
duplicate check no longer keys on TrackType, which closes a gap where the same
URL could be resubmitted as a different type. Several links per poem remain
allowed; only an identical URL for the same poem is refused.
The Spotify search page and its OAuth plumbing are kept and simply redirect to
/musiclink while the existing SpotifyWorking flag is false, so the old flow can
be restored if that API ever works again.
Track URLs are no longer written through Html.Raw into href attributes. They
previously came from the Spotify API; now that they are user supplied, encoding
them prevents stored XSS.
Also fixes two latent bugs in SuggestSong that this flow would have hit: a null
dereference when TrackUrl is empty, and a singer lookup that matched any singer
with an empty Url.
- Refine #page-hierarchy h2 a style: IranNastaliq, 2.1rem, line-height 2.4,
5px 20px padding, max-width 575px, font-smoothing and legibility hints.
- Keep tooltip dark in both color schemes.
- Style tooltip links with white text and a subtle white hover highlight.
- Match the tooltip arrow to the dark tooltip background.
- Make the tooltip close button separator gold for better visibility.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add explicit 3-column grid for desktop footer
- Switch to a 2+1 responsive layout under 768px
- Keep desktop-only 50px indent on tools list
- Balance the bottom 'about' list by flowing it into two columns on mobile
- Use solid #f3e9d0 for .poem light-mode background (average of 7% papere.jpg overlay)
- Refined dark mode color palette
- 18px border-radius (12px on mobile) for #main/.poem/.sitem
- Golden 1px border and shadow for #main/.poem/.sitem in dark mode
- Remove background-image, background-repeat, background-position,
background-size, background-attachment and background-blend-mode
from body, leaving only background-color
- Remove now-unused CSS vars: --damask-medallion, --damask-ground
and --page-pattern-size (light, dark and mobile override)
- Add design-token stylesheet (wwwroot/css/user-panel.css): CSS variables
derived from the existing p8.css palette (brand maroon, success/danger
colors, radius, shadow), plus reusable component classes (card, button
variants, alert, badge, empty state, pagination, list item, toast,
confirm modal). Includes a prefers-color-scheme dark block compatible
with the existing toggleColorScheme() mechanism in bk.js.
- Add wwwroot/js/user-panel.js: upConfirm() and upToast() helpers as
styled, promise-based replacements for window.confirm()/alert(),
falling back to the native dialogs if the modal/toast markup isn't
present on a page.
- Add Areas/User/Pages/Shared/ partials: _Pagination (wraps the
List<NameIdUrlImage> pattern already used by every list page),
_EmptyState, _Toasts and _ConfirmModal (hosts, included once by the
layout).
- Wire the new stylesheet/script and the toast/modal hosts into
_UserPanelLayout.cshtml.
- Refactor Notifications.cshtml as a first demonstration: replaces the
hand-rolled pagination loop, manual gray/lightsteelblue row
alternation, and confirm()/alert() calls with the new components.
No backend/API changes. Rest of the User area is untouched pending
Phase 1+ (nav/dashboard, forms, remaining list pages, moderation
queues, editors).