Repository navigation
1.0.37
Overview
This release brings the social layer of TrueTunes to life — playlists you can build and share, a personal Favourites playlist, full user profile pages, a People row on the home screen, and the recently played feature that kicked the branch off.
Playlists
Full playlist system backed by Azure Cosmos DB with optimistic concurrency control on all mutations.
What's new:
- Create private or public playlists from the home page, profile page, or track context menu
- Add tracks to any playlist via right-click → "Add to playlist" submenu
- Drag to reorder tracks within a playlist (owner and members)
- Remove tracks (owner only)
- Rename a playlist by clicking the title (owner only, Escape correctly cancels without saving)
- Delete a playlist with a confirmation step (owner only, Favourites protected server-side)
- Upload a custom cover image (owner only, stored in Azure Blob with 20-year SAS URL)
- Join / leave public playlists
- Playlist detail page (
/playlist/:id) with full track table, member list, and metadata - Playlist cards on home and profile pages sort Favourites first, then by most recently updated
- Track count on cards updates immediately after adding or removing tracks
Server functions added: playlist-create, playlist-get, playlist-list, playlist-add-track, playlist-remove-track, playlist-reorder, playlist-update, playlist-delete, playlist-join, playlist-upload-image
Infrastructure: new playlists Cosmos container and playlist-images blob container provisioned via ARM template.
Favourites
- Each user gets a Favourites playlist auto-created on first login (
favourites-ensureAzure Function) - Heart button in the player bar toggles the currently playing track in/out of Favourites with optimistic UI update and rollback on failure
- "Add to Favourites" option in the track context menu
- Favourites playlist is pinned to the top of all playlist lists and cannot be deleted or made public
useFavouritehook withisFavouritedstate derived from the cached playlist, full add/remove with rollback
User Profiles
- Profile pages at
/profile/:userNameshowing top tracks, top artists, top albums, playlists, and Queuedle rank - Click any user name anywhere in the app (track rows, playlist "added by", leaderboard) to go to their profile
- Profile picture upload (owner only) stored in Azure Blob
- Own profile shows a Sign Out link and all playlists (owned + joined); other profiles show only public playlists
useUserProfileanduseUserStatshooks;profile-getandprofile-upload-imageAzure Functions
People Row (Home Page)
- New "People" section on the home page showing all users who have queued tracks
- Circular profile picture avatars (gradient initial fallback), sorted by most recently queued
- Current user excluded server-side
- Click through to any user's profile page
users-listAzure Function queries the events container withGROUP BYto get last-queued timestamp per user, joins with profiles for images- Skeleton loaders while fetching; hover scale animation with correct overflow handling
Recently Played
- "Recently Played — Last 7 Days" section on the home page with artist and album grids
- User picker dropdown (gated behind completing today's Queuedle to prevent spoilers)
recently-playedAzure Function aggregates Cosmos events per user- Drag artists/albums from recently played into the queue
Context Menu
- Right-click any track anywhere in the app to get: Play next, Add to queue, Add to Favourites, Add to playlist (submenu), View [user]'s profile
ContextMenuProviderwraps the app shell;useTrackContextMenuhook exposesshowTrackMenuto any componentCreatePlaylistDialoginline in the context menu flow for creating a new playlist and immediately adding the track to it
Toast Notifications
ToastProvider/useToastfor non-blocking error feedback on all mutation failures (add track, remove track, rename, delete, join, reorder, upload image)
Code quality & bug fixes
- IPC error propagation fix: all 7 mutation IPC handlers now use a
playlistFetchhelper that throws on non-2xx responses, so renderercatchblocks and optimistic rollbacks actually fire on server errors (previously handlers returned{ error: '...' }silently) - Escape cancels rename correctly: fixed blur-after-Escape race condition in
PlaylistPanelrename input using acancelRenameref - Track count invalidation: all add/remove callsites now invalidate the playlist list query so card track counts update immediately
- Shared utilities:
getPlaylistColor,createDragGhost,PlaylistCardcomponent, and@keyframes skelPulseextracted to canonical locations, eliminating duplication across HomePanel, ProfilePanel, and PlaylistPanel useEnsureFavouritesretry logic now works correctly (handler throws instead of returning error object)handleJoinin PlaylistPanel had no error handling — fixed with contextual toastuseFavouritetoggle invalidates the playlist list query after success so Favourites track count stays accurate
Test coverage
New test files:
usePlaylists.test.ts—useFavouriteadd/remove happy paths, rollback paths,useEnsureFavouritesstale time and retry configPlaylistPanel.test.tsx—handleRemoveTrackoptimistic update and rollbackCreatePlaylistDialog.test.tsx— create without track, create with pending track, server error inline displayTopNav.test.tsx— updated to match new profile chip navigation behaviour
722 tests passing, full typecheck clean.