mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
fix(connection): ignore spurious navigator.onLine offline hint in Tauri (#1234)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { isDevOfflineBrowseForced } from '@/store/devOfflineBrowseStore';
|
||||
import { isNavigatorOfflineHint } from '@/lib/network/navigatorOnlineHint';
|
||||
|
||||
export type ConnectionStatus = 'connected' | 'disconnected' | 'checking';
|
||||
|
||||
@@ -53,9 +54,9 @@ export function resetActiveServerConnectionSnapshot(): void {
|
||||
connectionStatus = 'checking';
|
||||
}
|
||||
|
||||
/** True only when the browser is online and the last active-server probe succeeded. */
|
||||
/** True only when no navigator offline hint applies and the last active-server probe succeeded. */
|
||||
export function isActiveServerReachable(): boolean {
|
||||
if (isDevOfflineBrowseForced()) return false;
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return false;
|
||||
if (isNavigatorOfflineHint()) return false;
|
||||
return activeServerReachable === true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
const isTauri = vi.fn(() => false);
|
||||
|
||||
vi.mock('@tauri-apps/api/core', () => ({
|
||||
isTauri: () => isTauri(),
|
||||
}));
|
||||
|
||||
import { isNavigatorOfflineHint } from './navigatorOnlineHint';
|
||||
|
||||
describe('isNavigatorOfflineHint', () => {
|
||||
beforeEach(() => {
|
||||
isTauri.mockReturnValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('returns false in Tauri even when navigator.onLine is false', () => {
|
||||
isTauri.mockReturnValue(true);
|
||||
vi.stubGlobal('navigator', { onLine: false });
|
||||
expect(isNavigatorOfflineHint()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true in non-Tauri when navigator.onLine is false (offline hint applies)', () => {
|
||||
vi.stubGlobal('navigator', { onLine: false });
|
||||
expect(isNavigatorOfflineHint()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when navigator.onLine is true', () => {
|
||||
vi.stubGlobal('navigator', { onLine: true });
|
||||
expect(isNavigatorOfflineHint()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { isTauri } from '@tauri-apps/api/core';
|
||||
|
||||
/**
|
||||
* Whether callers should treat `navigator.onLine === false` as an offline signal.
|
||||
*
|
||||
* Returns `true` only in non-Tauri hosts when `navigator.onLine` is false.
|
||||
* WebKitGTK inside Tauri often leaves `navigator.onLine === false` even when
|
||||
* HTTP to the user's Subsonic/Navidrome server works (ping, search, playback).
|
||||
* Desktop builds must not trust that hint — use Subsonic probes instead.
|
||||
*
|
||||
* @see https://github.com/orgs/tauri-apps/discussions/9269
|
||||
*/
|
||||
export function isNavigatorOfflineHint(): boolean {
|
||||
if (typeof navigator === 'undefined') return false;
|
||||
try {
|
||||
if (isTauri()) return false;
|
||||
} catch {
|
||||
/* isTauri unavailable in some test harnesses — fall through */
|
||||
}
|
||||
return !navigator.onLine;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { hasLocalPlaybackUrl } from '@/store/localPlaybackResolve';
|
||||
import { isDevOfflineBrowseForced } from '@/store/devOfflineBrowseStore';
|
||||
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||
import { isActiveServerReachable } from '@/lib/network/activeServerReachability';
|
||||
import { isNavigatorOfflineHint } from '@/lib/network/navigatorOnlineHint';
|
||||
|
||||
function isSameServerProfile(a: string, b: string): boolean {
|
||||
if (!a || !b) return false;
|
||||
@@ -18,7 +19,7 @@ function isSameServerProfile(a: string, b: string): boolean {
|
||||
export function isSubsonicServerReachable(serverId: string): boolean {
|
||||
if (!serverId) return false;
|
||||
if (isDevOfflineBrowseForced()) return false;
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return false;
|
||||
if (isNavigatorOfflineHint()) return false;
|
||||
const activeId = useAuthStore.getState().activeServerId;
|
||||
if (!activeId || isSameServerProfile(serverId, activeId)) {
|
||||
return isActiveServerReachable();
|
||||
@@ -34,7 +35,7 @@ export function isSubsonicServerReachable(serverId: string): boolean {
|
||||
export function shouldAttemptSubsonicForServer(serverId: string, trackId?: string): boolean {
|
||||
if (!serverId) return false;
|
||||
if (isDevOfflineBrowseForced()) return false;
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return false;
|
||||
if (isNavigatorOfflineHint()) return false;
|
||||
if (trackId && hasLocalPlaybackUrl(trackId, serverId)) return false;
|
||||
return isSubsonicServerReachable(serverId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user