Skip to content

release: v0.2.0 — Spotify data layer and profile page#2

Merged
lebuckman merged 10 commits into
mainfrom
dev
Apr 3, 2026
Merged

release: v0.2.0 — Spotify data layer and profile page#2
lebuckman merged 10 commits into
mainfrom
dev

Conversation

@lebuckman
Copy link
Copy Markdown
Owner

@lebuckman lebuckman commented Apr 3, 2026

Summary by CodeRabbit

Release Notes

New Features

  • Redesigned profile page showcasing Top Artists, Top Tracks, Playlists, and a "Sound DNA" genre breakdown section
  • Added time-range selection for artists and tracks (4 weeks, 6 months, all time)
  • Integrated Last.fm data to provide genre insights based on listening history
  • Improved Spotify image display across the app

Documentation

  • Updated product and system design documentation to reflect Last.fm integration and updated feature scope

lebuckman added 10 commits April 1, 2026 16:43
- add cache-aside utility with typed CacheKey enum and TTL constants
- add Spotify API Client with wrappers for fetching playlists, top artists, and top tracks
- implement /api/spotify routes that utilize the Spotify API Client and cache results
- handle Spotify 401 and 429 errors explicitly in API routes
- transform Spotify responses into simplified formats before caching and returning to clients

Note: need to update deprecated attributes or endpoints
- audio-features
- top artists genres and popularity
- Add Last.fm API client with artist.getTopTags integration
- Add genre tag filtering, normalization, and within-artist weighting
- Add /api/lastfm/genre-breakdown route with 7-day DB cache
- Remove deprecated /api/spotify/audio-features route
- Remove deprecated genres and popularity attributes from SpotifyArtist type
- Fix SpotifyPlaylist tracks -> items field rename
- Add genre_breakdown CacheKey with 7-day TTL
- Add LASTFM_API_KEY to env configuration
- Document Last.fm genre integration replacing deprecated Spotify endpoints
- Note Spotify 2026 breaking changes (genres, popularity, tracks->items)
- Update cache TTL reference table with genre_breakdown 7-day TTL
- Add Spotify development mode 25-user limit to target users section
- Remove audio features from architecture and SonaUserContext
- Add QueryClientProvider with optimized staleTime and gcTime defaults
- Configure Google fonts in root layout
- Add useTopTracks, useTopArtists, usePlaylists, useGenreBreakdown hooks
- Add ReactQueryDevtools for development debugging
- Add SonaVoice and SectionLabel shared components
- Build ArtistsSection with editorial top-3 grid and time range selector
- Build TracksSection with ranked list, album art, and duration formatting
- Build GenreSection with bars from Last.fm genre breakdown
- Build PlaylistsSection with 6-up grid layout
- Wire profile page with sticky nav, identity hero, and section anchors
- Configure next/image remote patterns for Spotify CDN domains
- Fix LCP warning with priority loading on first artist image
- Gate useGenreBreakdown on useTopArtists success to prevent race condition
- Add SPOTIFY_RATE_LIMITED handling to playlists route for consistency
- Harden Last.fm fetchArtistTopTags with try/catch and API key guard
- Remove deprecated audio features endpoint
- Refactor TIME_RANGES constant
- Semantic cleanup in profile page and components
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sona Ready Ready Preview, Comment Apr 3, 2026 9:00pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 3, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dba39837-3719-4394-8e14-785976a01f61

📥 Commits

Reviewing files that changed from the base of the PR and between 839b56f and 6331b33.

📒 Files selected for processing (28)
  • .env.example
  • docs/PRD.md
  • docs/SYSTEM_DESIGN.md
  • next.config.ts
  • src/app/(protected)/profile/_components/artists-section.tsx
  • src/app/(protected)/profile/_components/genre-section.tsx
  • src/app/(protected)/profile/_components/playlists-section.tsx
  • src/app/(protected)/profile/_components/tracks-section.tsx
  • src/app/(protected)/profile/page.tsx
  • src/app/api/lastfm/genre-breakdown/route.ts
  • src/app/api/spotify/playlists/route.ts
  • src/app/api/spotify/top-artists/route.ts
  • src/app/api/spotify/top-tracks/route.ts
  • src/app/globals.css
  • src/app/layout.tsx
  • src/components/layout/section-label.tsx
  • src/components/providers.tsx
  • src/components/sona/sona-voice.tsx
  • src/hooks/use-genre-breakdown.ts
  • src/hooks/use-playlists.ts
  • src/hooks/use-top-artists.ts
  • src/hooks/use-top-tracks.ts
  • src/lib/constants.ts
  • src/lib/lastfm/client.ts
  • src/lib/lastfm/genres.ts
  • src/lib/spotify/cache.ts
  • src/lib/spotify/client.ts
  • src/types/index.ts

📝 Walkthrough

Walkthrough

The pull request introduces a comprehensive profile page redesign with Last.fm genre integration. It adds API routes for fetching Spotify top artists, top tracks, and playlists; implements a Last.fm genre breakdown endpoint; creates React hooks for data fetching; develops multiple profile UI components; establishes caching infrastructure; updates type definitions; and configures the necessary environment variables and Next.js settings.

Changes

Cohort / File(s) Summary
Configuration
.env.example, next.config.ts
Added LASTFM_API_KEY environment variable; configured Spotify image remote patterns for HTTPS hostname allowlist.
Documentation
docs/PRD.md, docs/SYSTEM_DESIGN.md
Updated version to 1.1, revised April 2026 last-updated metadata, replaced audio features scope with Last.fm genre signals, documented new /api/lastfm/* routes, updated database schema and caching strategy, removed outdated audio features references.
Spotify API Routes
src/app/api/spotify/top-artists/route.ts, src/app/api/spotify/top-tracks/route.ts, src/app/api/spotify/playlists/route.ts
Implemented three new authenticated GET handlers with cache-first pattern; fetch data from Spotify, transform to minimal shapes, cache with TTL, and return with cache status; handle auth/rate-limit errors.
Last.fm API Route
src/app/api/lastfm/genre-breakdown/route.ts
New authenticated GET handler that aggregates top tags from Last.fm for cached top artists, with batch-processing (5 concurrent, 200ms delay), caches result, and returns genre breakdown with weight percentages.
Profile Page & Components
src/app/(protected)/profile/page.tsx, src/app/(protected)/profile/_components/*
Restructured profile page with sticky navigation header and hero section; added four new section components (ArtistsSection, TracksSection, GenreSection, PlaylistsSection) with time-range toggles, skeleton loading, accessible markup, and external links to Spotify.
React Query Hooks
src/hooks/use-top-artists.ts, src/hooks/use-top-tracks.ts, src/hooks/use-playlists.ts, src/hooks/use-genre-breakdown.ts
Implemented four custom hooks wrapping React Query useQuery for fetching Spotify/Last.fm data with stable cache keys, error handling, and optional enabled flags.
Spotify Data Layer
src/lib/spotify/client.ts, src/lib/spotify/cache.ts
Created API client wrapper with typed fetch helpers for Spotify Web API endpoints (top tracks/artists/playlists) with explicit error handling; implemented generic cache layer with per-user TTL-based storage and retrieval.
Last.fm Data Layer
src/lib/lastfm/client.ts, src/lib/lastfm/genres.ts
Implemented Last.fm artist top-tags fetcher; created genre aggregation logic with tag normalization, artist rank weighting, and percentage normalization; includes blocklist and validation rules.
UI Components & Types
src/components/providers.tsx, src/components/layout/section-label.tsx, src/components/sona/sona-voice.tsx, src/types/index.ts
Added React Query provider component with default config; created two layout typography components; updated types to remove audio features, add Last.fm types, introduce GenreEntry, refine SonaUserContext.
Styling & Layout
src/app/globals.css, src/app/layout.tsx
Swapped fonts from Playfair/Geist to Fraunces/Instrument Sans; added CSS custom properties for font family variables; updated metadata and wrapped layout in Providers component.
Constants
src/lib/constants.ts
Added TIME_RANGES constant array mapping "4 Weeks"/"6 Months"/"All Time" labels to short_term/medium_term/long_term values.

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser as Browser (Client)
    participant Profile as Profile Page
    participant SpotifyAPI as Spotify API Route
    participant LastFmAPI as Last.fm API Route
    participant SpotifyService as Spotify Client
    participant LastFmService as Last.fm Client
    participant Cache as Cache DB
    participant SpotifyWeb as Spotify Web API
    participant LastFmWeb as Last.fm Web API

    User->>Browser: Visit profile page
    Browser->>Profile: Render page component
    
    Profile->>SpotifyAPI: GET /api/spotify/top-artists?range=short_term
    
    SpotifyAPI->>Cache: Check cached top_artists:short_term
    alt Cache Hit
        Cache-->>SpotifyAPI: Return cached artists
        SpotifyAPI-->>Profile: Return artists + cached:true
    else Cache Miss
        SpotifyAPI->>SpotifyService: fetchTopArtists(token, short_term)
        SpotifyService->>SpotifyWeb: GET /me/top/artists
        SpotifyWeb-->>SpotifyService: Return artist data
        SpotifyService-->>SpotifyAPI: Transformed artists
        SpotifyAPI->>Cache: setCached top_artists:short_term
        SpotifyAPI-->>Profile: Return artists + cached:false
    end
    
    Profile->>LastFmAPI: GET /api/lastfm/genre-breakdown
    
    LastFmAPI->>Cache: Check cached genre_breakdown
    alt Cache Hit
        Cache-->>LastFmAPI: Return cached genres
        LastFmAPI-->>Profile: Return genres + cached:true
    else Cache Miss
        LastFmAPI->>Cache: Load top_artists:short_term
        Cache-->>LastFmAPI: Return cached artists
        
        LastFmAPI->>LastFmService: fetchArtistTopTags(artistName) × N
        LastFmService->>LastFmWeb: GET /2.0 (artist.gettoptags)
        LastFmWeb-->>LastFmService: Return tags
        LastFmService-->>LastFmAPI: Tags for each artist
        
        LastFmAPI->>LastFmAPI: aggregateGenres (normalize, weight, rank)
        LastFmAPI->>Cache: setCached genre_breakdown
        LastFmAPI-->>Profile: Return genres + cached:false
    end
    
    Profile->>Browser: Render all sections with fetched data
    Browser->>User: Display profile page
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

  • Spotify Data Layer #1: Directly related—introduces identical Last.fm API integration with shared type definitions, cache utilities, hooks, and API route patterns for genre aggregation and artist data fetching.

Poem

🐰 Whiskers twitching with delight,
Last.fm tags now gleaming bright,
Artists dance on genres' weight,
Profiles crafted, oh how great!
Spotify songs and Sona's song,
Data flows where it belongs. 🎵

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lebuckman lebuckman merged commit cc304f3 into main Apr 3, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant