mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-21 22:15:40 +00:00
feat: 10-band EQ, connection indicator, new icon (v1.5.0)
### 10-Band Graphic Equalizer - Full EQ in Rust audio engine via EqSource<S> biquad peak filters - 10 built-in presets + custom preset save/delete - EqSource::try_seek() implemented — also fixes waveform seek which broke silently when EQ was introduced (rodio returned SeekError::NotSupported) - EQ state persisted in localStorage, synced to Rust on startup ### Connection Indicator - LED in header (green/red/pulsing) with server name and LAN/WAN label - Offline overlay with retry button when server is unreachable - useConnectionStatus hook ### New App Icon - New logo (logo-psysonic.png) applied across Login, Sidebar, Settings, README and all Tauri platform icons (Windows, macOS, Linux, Android, iOS) ### Now Playing Page - New /now-playing route added ### Fixes - WaveformSeek: mousemove/mouseup on window to fix drag outside canvas Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { pingWithCredentials } from '../api/subsonic';
|
||||
|
||||
export type ConnectionStatus = 'connected' | 'disconnected' | 'checking';
|
||||
|
||||
export function isLanUrl(url: string): boolean {
|
||||
try {
|
||||
const hostname = new URL(url.startsWith('http') ? url : `http://${url}`).hostname;
|
||||
return (
|
||||
hostname === 'localhost' ||
|
||||
hostname.endsWith('.local') ||
|
||||
/^127\./.test(hostname) ||
|
||||
/^10\./.test(hostname) ||
|
||||
/^192\.168\./.test(hostname) ||
|
||||
/^172\.(1[6-9]|2\d|3[01])\./.test(hostname)
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function useConnectionStatus() {
|
||||
const [status, setStatus] = useState<ConnectionStatus>('checking');
|
||||
const [isRetrying, setIsRetrying] = useState(false);
|
||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
const check = useCallback(async () => {
|
||||
const server = useAuthStore.getState().getActiveServer();
|
||||
if (!server) {
|
||||
setStatus('disconnected');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!navigator.onLine) {
|
||||
setStatus('disconnected');
|
||||
return;
|
||||
}
|
||||
|
||||
const ok = await pingWithCredentials(server.url, server.username, server.password);
|
||||
setStatus(ok ? 'connected' : 'disconnected');
|
||||
}, []);
|
||||
|
||||
const retry = useCallback(async () => {
|
||||
setIsRetrying(true);
|
||||
await check();
|
||||
setIsRetrying(false);
|
||||
}, [check]);
|
||||
|
||||
useEffect(() => {
|
||||
check();
|
||||
intervalRef.current = setInterval(check, 30_000);
|
||||
|
||||
const handleOnline = () => check();
|
||||
const handleOffline = () => setStatus('disconnected');
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
}, [check]);
|
||||
|
||||
const server = useAuthStore(s => s.getActiveServer());
|
||||
|
||||
return {
|
||||
status,
|
||||
isRetrying,
|
||||
retry,
|
||||
isLan: server ? isLanUrl(server.url) : false,
|
||||
serverName: server?.name ?? '',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user