mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
feat: instant UI update after manual track mapping
- Backend now returns mapped track details after saving - Frontend updates track in-place without requiring page refresh - Track status changes from External to Local immediately - Map button is removed after successful mapping - Playlist counts refresh in background - Improved UX: no more 'refresh the playlist' message All 225 tests pass.
This commit is contained in:
@@ -656,6 +656,37 @@ public class AdminController : ControllerBase
|
||||
|
||||
_logger.LogInformation("Cleared playlist caches for {Playlist} to force rebuild", decodedName);
|
||||
|
||||
// Fetch the mapped Jellyfin track details to return to the UI
|
||||
try
|
||||
{
|
||||
var trackUrl = $"{_jellyfinSettings.Url}/Items/{request.JellyfinId}?api_key={_jellyfinSettings.ApiKey}";
|
||||
var response = await _jellyfinHttpClient.GetAsync(trackUrl);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var trackData = await response.Content.ReadAsStringAsync();
|
||||
using var doc = JsonDocument.Parse(trackData);
|
||||
var track = doc.RootElement;
|
||||
|
||||
var mappedTrack = new
|
||||
{
|
||||
id = request.JellyfinId,
|
||||
title = track.TryGetProperty("Name", out var nameEl) ? nameEl.GetString() : "",
|
||||
artist = track.TryGetProperty("AlbumArtist", out var artistEl) ? artistEl.GetString() :
|
||||
(track.TryGetProperty("Artists", out var artistsEl) && artistsEl.GetArrayLength() > 0
|
||||
? artistsEl[0].GetString() : ""),
|
||||
album = track.TryGetProperty("Album", out var albumEl) ? albumEl.GetString() : "",
|
||||
isLocal = true
|
||||
};
|
||||
|
||||
return Ok(new { message = "Mapping saved successfully", track = mappedTrack });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to fetch mapped track details, but mapping was saved");
|
||||
}
|
||||
|
||||
return Ok(new { message = "Mapping saved successfully" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -1760,7 +1760,7 @@
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="track-item">
|
||||
<div class="track-item" data-position="${t.position}">
|
||||
<span class="track-position">${t.position + 1}</span>
|
||||
<div class="track-info">
|
||||
<h4>${escapeHtml(t.title)}${statusBadge}${mapButton}</h4>
|
||||
@@ -2021,6 +2021,7 @@
|
||||
const playlistName = document.getElementById('map-playlist-name').value;
|
||||
const spotifyId = document.getElementById('map-spotify-id').value;
|
||||
const jellyfinId = document.getElementById('map-selected-jellyfin-id').value;
|
||||
const position = parseInt(document.getElementById('map-position').textContent) - 1; // Convert back to 0-indexed
|
||||
|
||||
if (!jellyfinId) {
|
||||
showToast('Please select a track', 'error');
|
||||
@@ -2037,10 +2038,43 @@
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
showToast('Track mapped successfully! Refresh the playlist to see changes.', 'success');
|
||||
showToast('✓ Track mapped successfully', 'success');
|
||||
closeModal('manual-map-modal');
|
||||
// Refresh the tracks view
|
||||
viewTracks(playlistName);
|
||||
|
||||
// Update the track in the UI without refreshing
|
||||
if (data.track) {
|
||||
const trackItem = document.querySelector(`.track-item[data-position="${position}"]`);
|
||||
if (trackItem) {
|
||||
// Update the track info
|
||||
const titleEl = trackItem.querySelector('.track-info h4');
|
||||
const artistEl = trackItem.querySelector('.track-info .artists');
|
||||
const statusBadge = trackItem.querySelector('.status-badge');
|
||||
const mapButton = trackItem.querySelector('.map-track-btn');
|
||||
const searchLink = trackItem.querySelector('.track-meta a');
|
||||
|
||||
if (titleEl) {
|
||||
// Remove the old status badge and map button, add new content
|
||||
const titleText = data.track.title;
|
||||
const newStatusBadge = '<span class="status-badge success" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>Local</span>';
|
||||
titleEl.innerHTML = escapeHtml(titleText) + newStatusBadge;
|
||||
}
|
||||
|
||||
if (artistEl) artistEl.textContent = data.track.artist;
|
||||
|
||||
// Remove the search link since it's now local
|
||||
if (searchLink) {
|
||||
const metaEl = trackItem.querySelector('.track-meta');
|
||||
if (metaEl) {
|
||||
// Keep album and ISRC, remove search link
|
||||
const albumText = data.track.album ? escapeHtml(data.track.album) : '';
|
||||
metaEl.innerHTML = albumText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also refresh the playlist counts in the background
|
||||
fetchPlaylists();
|
||||
} else {
|
||||
showToast(data.error || 'Failed to save mapping', 'error');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user