fix(favorites): heart button works in tracks tab, animate out on unfavorite
Remove isFavoriteOverride={true} that forced all hearts filled,
preventing the toggle from having any visible effect. Subscribe to
favoritesStore so unfavorited tracks animate out (slide+fade) and
are removed from the list after 300ms without requiring a refresh.
This commit is contained in:
1 file changed
+54
-2
@@ -19,7 +19,7 @@
|
|||||||
import { type OfflineCacheStatus } from '$lib/stores/offlineCacheState';
|
import { type OfflineCacheStatus } from '$lib/stores/offlineCacheState';
|
||||||
import { consumeContextTrackFocus, setPlaybackContext } from '$lib/stores/playbackContextStore';
|
import { consumeContextTrackFocus, setPlaybackContext } from '$lib/stores/playbackContextStore';
|
||||||
import { normalizeFavoritesTabOrder } from '$lib/utils/favorites';
|
import { normalizeFavoritesTabOrder } from '$lib/utils/favorites';
|
||||||
import { syncCache as syncTrackCache } from '$lib/stores/favoritesStore';
|
import { syncCache as syncTrackCache, subscribe as subscribeFavorites, isTrackFavorite } from '$lib/stores/favoritesStore';
|
||||||
import { syncCache as syncAlbumCache } from '$lib/stores/albumFavoritesStore';
|
import { syncCache as syncAlbumCache } from '$lib/stores/albumFavoritesStore';
|
||||||
import { syncCache as syncArtistCache } from '$lib/stores/artistFavoritesStore';
|
import { syncCache as syncArtistCache } from '$lib/stores/artistFavoritesStore';
|
||||||
import { categorizeAlbum, getQobuzImage, formatQuality } from '$lib/adapters/qobuzAdapters';
|
import { categorizeAlbum, getQobuzImage, formatQuality } from '$lib/adapters/qobuzAdapters';
|
||||||
@@ -622,6 +622,30 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for favorite changes — animate out unfavorited tracks
|
||||||
|
const unsubFavorites = subscribeFavorites(() => {
|
||||||
|
if (activeTab !== 'tracks' || favoriteTracks.length === 0) return;
|
||||||
|
const unfavorited = favoriteTracks.filter(track => !isTrackFavorite(track.id));
|
||||||
|
if (unfavorited.length === 0) return;
|
||||||
|
|
||||||
|
// Add exit animation class via DOM (virtualized list doesn't support per-item props)
|
||||||
|
for (const track of unfavorited) {
|
||||||
|
const el = scrollContainer?.querySelector<HTMLElement>(`[data-track-id="${track.id}"]`);
|
||||||
|
if (el) {
|
||||||
|
el.classList.add('track-removing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After animation completes, remove from data array
|
||||||
|
setTimeout(() => {
|
||||||
|
favoriteTracks = favoriteTracks.filter(track => isTrackFavorite(track.id));
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubFavorites();
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleFavoritesScroll(e: Event) {
|
function handleFavoritesScroll(e: Event) {
|
||||||
@@ -1638,7 +1662,6 @@
|
|||||||
isLocal={false}
|
isLocal={false}
|
||||||
hideDownload={false}
|
hideDownload={false}
|
||||||
hideFavorite={false}
|
hideFavorite={false}
|
||||||
isFavoriteOverride={true}
|
|
||||||
getOfflineCacheStatus={getTrackOfflineCacheStatus}
|
getOfflineCacheStatus={getTrackOfflineCacheStatus}
|
||||||
onDownload={onTrackDownload ? (trk) => onTrackDownload(buildDisplayTrackFromFavorite(trk)) : undefined}
|
onDownload={onTrackDownload ? (trk) => onTrackDownload(buildDisplayTrackFromFavorite(trk)) : undefined}
|
||||||
onRemoveDownload={onTrackRemoveDownload}
|
onRemoveDownload={onTrackRemoveDownload}
|
||||||
@@ -2041,6 +2064,35 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Exit animation for unfavorited tracks */
|
||||||
|
:global(.track-removing) {
|
||||||
|
animation: track-fade-out 300ms ease forwards;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes track-fade-out {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
max-height: 60px;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
max-height: 60px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
max-height: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.favorites-view {
|
.favorites-view {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
|
|||||||
Reference in new issue
Block a user