merge: integrate origin/main into fix/statistics-i18n-small-fixes

This commit is contained in:
Maxim Isaev
2026-04-10 01:42:14 +03:00
7 changed files with 112 additions and 6 deletions
+22
View File
@@ -413,6 +413,28 @@ function TauriEventBridge() {
return () => { unlisten?.(); };
}, []);
// Audio output device changed (Bluetooth headphones, USB DAC, etc.)
// The Rust device-watcher has already reopened the stream on the new device
// and dropped the old Sink, so we just need to restart playback.
useEffect(() => {
let unlisten: (() => void) | undefined;
listen('audio:device-changed', () => {
const { currentTrack, currentTime, isPlaying, playTrack, resetAudioPause } = usePlayerStore.getState();
if (!currentTrack) return;
if (isPlaying) {
const pos = currentTime;
const dur = currentTrack.duration || 1;
playTrack(currentTrack);
setTimeout(() => usePlayerStore.getState().seek(pos / dur), 600);
} else {
// Paused: clear warm-pause flag so the next resume uses the cold path
// (audio_play + seek) which creates a new Sink on the new device.
resetAudioPause();
}
}).then(u => { unlisten = u; });
return () => { unlisten?.(); };
}, []);
// Sync tray-icon visibility with the user's stored setting.
// Runs once on mount (initial sync) and again whenever the setting changes.
const showTrayIcon = useAuthStore(s => s.showTrayIcon);
+2 -2
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Search, Disc3, Users, Music, SlidersHorizontal } from 'lucide-react';
import { Search, Disc3, Users, Music, SlidersVertical } from 'lucide-react';
import { search, SearchResults, buildCoverArtUrl } from '../api/subsonic';
import { usePlayerStore, songToTrack } from '../store/playerStore';
import { useAuthStore } from '../store/authStore';
@@ -126,7 +126,7 @@ export default function LiveSearch() {
data-tooltip-pos="bottom"
aria-label={t('search.advanced')}
>
<SlidersHorizontal size={14} />
<SlidersVertical size={14} />
</button>
</div>
+2 -2
View File
@@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import {
Play, Pause, SkipBack, SkipForward, Volume2, VolumeX, Music,
Square, Repeat, Repeat1, Maximize2, SlidersHorizontal, X, Heart, MicVocal, Cast
Square, Repeat, Repeat1, Maximize2, SlidersVertical, X, Heart, MicVocal, Cast
} from 'lucide-react';
import { usePlayerStore } from '../store/playerStore';
import { useAuthStore } from '../store/authStore';
@@ -241,7 +241,7 @@ export default function PlayerBar() {
aria-label="Equalizer"
data-tooltip="Equalizer"
>
<SlidersHorizontal size={15} />
<SlidersVertical size={15} />
</button>
{/* Volume */}
+2 -2
View File
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { Play, SlidersHorizontal } from 'lucide-react';
import { Play, SlidersVertical } from 'lucide-react';
import {
search, getGenres, getAlbumsByGenre, getAlbumList, getRandomSongs,
SubsonicGenre, SubsonicArtist, SubsonicAlbum, SubsonicSong,
@@ -140,7 +140,7 @@ export default function AdvancedSearch() {
<div className="content-body animate-fade-in">
<div style={{ marginBottom: '1.5rem' }}>
<h1 className="page-title" style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<SlidersHorizontal size={22} style={{ color: 'var(--accent)', flexShrink: 0 }} />
<SlidersVertical size={22} style={{ color: 'var(--accent)', flexShrink: 0 }} />
{t('search.advanced')}
</h1>
</div>
+5
View File
@@ -117,6 +117,7 @@ interface PlayerState {
setLastfmLovedForSong: (title: string, artist: string, v: boolean) => void;
syncLastfmLovedTracks: () => Promise<void>;
resetAudioPause: () => void;
initializeFromServerQueue: () => Promise<void>;
contextMenu: {
@@ -863,6 +864,10 @@ export const usePlayerStore = create<PlayerState>()(
set({ isPlaying: false });
},
resetAudioPause: () => {
isAudioPaused = false;
},
resume: () => {
if (get().currentRadio) {
radioAudio.play().catch(console.error);