Wire settings: account info, themes, and cache management

- Account section now shows real user name and subscription
- Logout button connected to logout handler
- Theme switching with persistence (Dark/Light/OLED Black/Warm)
- Theme loads on app startup from localStorage
- Cache stats display (size, max, track count)
- Clear cache functionality with disabled state
This commit is contained in:
vicrodh committed 2026-01-09 11:10:41 -06:00
1 parent ef8feebc45
commit 9b8e1352eb
5 files changed
+163 -11

No files matched your search

+125 -10
View File
@@ -1,4 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { invoke } from '@tauri-apps/api/core';
import { ArrowLeft } from 'lucide-svelte';
import Toggle from '../Toggle.svelte';
import Dropdown from '../Dropdown.svelte';
@@ -7,9 +9,39 @@
interface Props {
onBack?: () => void;
onLogout?: () => void;
userName?: string;
userEmail?: string;
subscription?: string;
}
let { onBack }: Props = $props();
interface CacheStats {
cached_tracks: number;
current_size_bytes: number;
max_size_bytes: number;
fetching_count: number;
}
let { onBack, onLogout, userName = 'User', userEmail = '', subscription = 'Qobuz' }: Props = $props();
// Cache state
let cacheStats = $state<CacheStats | null>(null);
let isClearing = $state(false);
// Theme mapping: display name -> data-theme value
const themeMap: Record<string, string> = {
'Dark': '',
'Light': 'light',
'OLED Black': 'oled',
'Warm': 'warm'
};
const themeReverseMap: Record<string, string> = {
'': 'Dark',
'light': 'Light',
'oled': 'OLED Black',
'warm': 'Warm'
};
// Audio settings
let streamingQuality = $state('Hi-Res');
@@ -30,6 +62,60 @@
// Integrations
let lastfmConnected = $state(false);
let scrobbling = $state(false);
// Load saved settings on mount
onMount(() => {
const savedTheme = localStorage.getItem('qbz-theme') || '';
theme = themeReverseMap[savedTheme] || 'Dark';
applyTheme(savedTheme);
// Load cache stats
loadCacheStats();
});
async function loadCacheStats() {
try {
cacheStats = await invoke<CacheStats>('get_cache_stats');
} catch (err) {
console.error('Failed to load cache stats:', err);
}
}
async function handleClearCache() {
if (isClearing) return;
isClearing = true;
try {
await invoke('clear_cache');
await loadCacheStats();
} catch (err) {
console.error('Failed to clear cache:', err);
} finally {
isClearing = false;
}
}
function formatBytes(bytes: number): string {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
}
function applyTheme(themeValue: string) {
if (themeValue) {
document.documentElement.setAttribute('data-theme', themeValue);
} else {
document.documentElement.removeAttribute('data-theme');
}
}
function handleThemeChange(newTheme: string) {
theme = newTheme;
const themeValue = themeMap[newTheme] || '';
applyTheme(themeValue);
localStorage.setItem('qbz-theme', themeValue);
}
</script>
<div class="settings-view">
@@ -48,13 +134,15 @@
<section class="section">
<h3 class="section-title">Account</h3>
<div class="account-card">
<div class="avatar">U</div>
<div class="avatar">{userName.charAt(0).toUpperCase()}</div>
<div class="account-info">
<div class="username">Username</div>
<div class="email">email@example.com</div>
<div class="subscription">Studio Premier</div>
<div class="username">{userName}</div>
{#if userEmail}
<div class="email">{userEmail}</div>
{/if}
<div class="subscription">{subscription}</div>
</div>
<button class="logout-btn">Logout</button>
<button class="logout-btn" onclick={onLogout}>Logout</button>
</div>
</section>
@@ -128,7 +216,7 @@
<Dropdown
value={theme}
options={['Dark', 'Light', 'OLED Black', 'Warm']}
onchange={(v) => (theme = v)}
onchange={handleThemeChange}
/>
</div>
<div class="setting-row last">
@@ -163,11 +251,33 @@
<h3 class="section-title">Storage</h3>
<div class="setting-row">
<span class="setting-label">Cache Size</span>
<span class="setting-value">2.4 GB</span>
<span class="setting-value">
{#if cacheStats}
{formatBytes(cacheStats.current_size_bytes)} / {formatBytes(cacheStats.max_size_bytes)}
{:else}
Loading...
{/if}
</span>
</div>
<div class="setting-row">
<span class="setting-label">Cached Tracks</span>
<span class="setting-value">
{#if cacheStats}
{cacheStats.cached_tracks} tracks
{:else}
-
{/if}
</span>
</div>
<div class="setting-row last">
<span class="setting-label">Clear Cache</span>
<button class="clear-btn">Clear</button>
<button
class="clear-btn"
onclick={handleClearCache}
disabled={isClearing || !cacheStats || cacheStats.current_size_bytes === 0}
>
{isClearing ? 'Clearing...' : 'Clear'}
</button>
</div>
</section>
</div>
@@ -345,7 +455,12 @@
transition: background-color 150ms ease;
}
.clear-btn:hover {
.clear-btn:hover:not(:disabled) {
background-color: rgba(255, 107, 107, 0.1);
}
.clear-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
+12 -1
View File
@@ -839,6 +839,12 @@
});
onMount(() => {
// Load saved theme
const savedTheme = localStorage.getItem('qbz-theme');
if (savedTheme) {
document.documentElement.setAttribute('data-theme', savedTheme);
}
document.addEventListener('keydown', handleKeydown);
return () => document.removeEventListener('keydown', handleKeydown);
});
@@ -888,7 +894,12 @@
onTrackPlay={handleTrackPlay}
/>
{:else if activeView === 'settings'}
<SettingsView onBack={goBack} />
<SettingsView
onBack={goBack}
onLogout={handleLogout}
userName={userInfo?.userName}
subscription={userInfo?.subscription}
/>
{:else if activeView === 'album' && selectedAlbum}
<AlbumDetailView
album={selectedAlbum}