feat: UI improvements for menus and favorites

- Remove blue circle from fullscreen play button (match other controls)
- Add icons to TrackMenu items (Play, ListPlus, Heart, Share2, Disc3, User)
- Check album favorite status on mount in AlbumDetailView
- Improve control button styling with flexbox alignment
This commit is contained in:
vicrodh committed 2026-01-09 17:52:40 -06:00
1 parent e8c6f7372f
commit d6ca09cda3
4 files changed
+97 -41

No files matched your search

+6 -25
View File
@@ -61,8 +61,7 @@
let volumeRef: HTMLDivElement;
let isDraggingProgress = $state(false);
let isDraggingVolume = $state(false);
let playBtnHovered = $state(false);
const progress = $derived((currentTime / duration) * 100);
function formatTime(seconds: number): string {
@@ -177,17 +176,11 @@
<button class="control-btn primary" onclick={onSkipBack}>
<SkipBack size={32} />
</button>
<button
class="play-btn"
style="background-color: {playBtnHovered ? 'var(--accent-hover)' : 'var(--accent-primary)'}"
onmouseenter={() => (playBtnHovered = true)}
onmouseleave={() => (playBtnHovered = false)}
onclick={onTogglePlay}
>
<button class="control-btn primary play-pause" onclick={onTogglePlay}>
{#if isPlaying}
<Pause size={28} fill="white" color="white" />
<Pause size={40} />
{:else}
<Play size={28} fill="white" color="white" class="play-icon" />
<Play size={40} />
{/if}
</button>
<button class="control-btn primary" onclick={onSkipForward}>
@@ -433,20 +426,8 @@
font-weight: 700;
}
.play-btn {
width: 64px;
height: 64px;
border-radius: 50%;
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 150ms ease;
}
.play-btn :global(.play-icon) {
margin-left: 2px;
.control-btn.play-pause {
margin: 0 16px;
}
.volume-container {
+11 -3
View File
@@ -395,11 +395,15 @@
}
.control-btn {
background: none;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
background: transparent;
border: none;
color: var(--text-muted);
cursor: pointer;
transition: color 150ms ease;
transition: color 150ms ease, transform 100ms ease;
position: relative;
}
@@ -407,12 +411,16 @@
color: var(--text-primary);
}
.control-btn:active {
transform: scale(0.95);
}
.control-btn.primary {
color: var(--text-primary);
}
.control-btn.primary:hover {
color: var(--text-muted);
color: var(--accent-primary);
}
.control-btn.active {
+60 -13
View File
@@ -1,6 +1,18 @@
<script lang="ts">
import { tick } from 'svelte';
import { ChevronRight, MoreHorizontal } from 'lucide-svelte';
import {
ChevronRight,
MoreHorizontal,
Play,
ListPlus,
ListEnd,
Heart,
ListMusic,
Share2,
Disc3,
User,
Link
} from 'lucide-svelte';
interface Props {
onPlayNow?: () => void;
@@ -157,13 +169,22 @@
<div class="menu" bind:this={menuEl} style={menuStyle}>
{#if hasPlayback}
{#if onPlayNow}
<button class="menu-item" onclick={() => handleAction(onPlayNow)}>Play now</button>
<button class="menu-item" onclick={() => handleAction(onPlayNow)}>
<Play size={14} />
<span>Play now</span>
</button>
{/if}
{#if onPlayNext}
<button class="menu-item" onclick={() => handleAction(onPlayNext)}>Play next</button>
<button class="menu-item" onclick={() => handleAction(onPlayNext)}>
<ListPlus size={14} />
<span>Play next</span>
</button>
{/if}
{#if onPlayLater}
<button class="menu-item" onclick={() => handleAction(onPlayLater)}>Play later</button>
<button class="menu-item" onclick={() => handleAction(onPlayLater)}>
<ListEnd size={14} />
<span>Play later</span>
</button>
{/if}
{/if}
@@ -173,10 +194,16 @@
{#if hasLibrary}
{#if onAddFavorite}
<button class="menu-item" onclick={() => handleAction(onAddFavorite)}>Add to favorites</button>
<button class="menu-item" onclick={() => handleAction(onAddFavorite)}>
<Heart size={14} />
<span>Add to favorites</span>
</button>
{/if}
{#if onAddToPlaylist}
<button class="menu-item" onclick={() => handleAction(onAddToPlaylist)}>Add to playlist</button>
<button class="menu-item" onclick={() => handleAction(onAddToPlaylist)}>
<ListMusic size={14} />
<span>Add to playlist</span>
</button>
{/if}
{/if}
@@ -197,15 +224,22 @@
if (shareOpen) setSubmenuPosition();
}}
>
<Share2 size={14} />
<span>Share</span>
<ChevronRight size={14} />
<ChevronRight size={14} class="chevron" />
{#if shareOpen}
<div class="submenu" bind:this={submenuEl} style={submenuStyle}>
{#if onShareQobuz}
<button class="menu-item" onclick={() => handleAction(onShareQobuz)}>Share Qobuz link</button>
<button class="menu-item" onclick={() => handleAction(onShareQobuz)}>
<Link size={14} />
<span>Qobuz link</span>
</button>
{/if}
{#if onShareSonglink}
<button class="menu-item" onclick={() => handleAction(onShareSonglink)}>Share Song.link</button>
<button class="menu-item" onclick={() => handleAction(onShareSonglink)}>
<Link size={14} />
<span>Song.link</span>
</button>
{/if}
</div>
{/if}
@@ -218,10 +252,16 @@
{#if hasNav}
{#if onGoToAlbum}
<button class="menu-item" onclick={() => handleAction(onGoToAlbum)}>Go to album</button>
<button class="menu-item" onclick={() => handleAction(onGoToAlbum)}>
<Disc3 size={14} />
<span>Go to album</span>
</button>
{/if}
{#if onGoToArtist}
<button class="menu-item" onclick={() => handleAction(onGoToArtist)}>Go to artist</button>
<button class="menu-item" onclick={() => handleAction(onGoToArtist)}>
<User size={14} />
<span>Go to artist</span>
</button>
{/if}
{/if}
</div>
@@ -276,11 +316,18 @@
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
gap: 10px;
transition: background-color 150ms ease, color 150ms ease;
}
.menu-item span {
flex: 1;
}
.menu-item :global(.chevron) {
margin-left: auto;
}
.menu-item:hover {
background-color: var(--bg-hover);
color: var(--text-primary);
@@ -1,4 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { invoke } from '@tauri-apps/api/core';
import { ArrowLeft, Play, Shuffle, Heart, Plus, MoreHorizontal } from 'lucide-svelte';
import TrackRow from '../TrackRow.svelte';
@@ -71,6 +72,25 @@
let isFavoriteLoading = $state(false);
let playBtnHovered = $state(false);
interface FavoritesResponse {
items: Array<{ id: string }>;
total: number;
}
// Check if album is already in favorites on mount
onMount(async () => {
try {
const response = await invoke<FavoritesResponse>('get_favorites', {
favType: 'albums',
limit: 500,
offset: 0
});
isFavorite = response.items.some(item => item.id === album.id);
} catch (err) {
console.error('Failed to check favorite status:', err);
}
});
async function toggleFavorite() {
if (isFavoriteLoading) return;