Files
Psychotoxical-psysonic/src/lib/network/subsonicNetworkGuard.ts
T
Psychotoxical c37d5f7389 refactor(network): decouple network-reachability layer from features → lib/network
The connection-reachability + Subsonic network-guard helpers were pinned low by
four lib/api consumers (subsonicLibrary/Playlists/Ratings/Scrobble) yet ran two
lower-layer→feature inversions. Both removed without a registry:

- devOfflineBrowseStore is a self-contained DEV-only toggle with zero offline-
  feature coupling — it was only colocated there. Relocated features/offline/store
  → store/ (global). The @/features/offline barrel re-exports it so feature/UI
  consumers are unchanged; the three lower-layer readers (subsonicNetworkGuard,
  activeServerReachability, useConnectionStatus) now import it from @/store directly.

- subsonicNetworkGuard's only other feature dep was playback's resolvePlaybackUrl,
  used solely for the psysonic-local:// skip check. Added hasLocalPlaybackUrl to the
  existing M4 substrate store/localPlaybackResolve — it mirrors resolvePlaybackUrl's
  local-source branch exactly (same profile resolution; the empty-serverId playback
  fallback never applies in the guard), so the skip stays bit-identical.

network/ (subsonicNetworkGuard + activeServerReachability + tests) → lib/network,
now @/features-free. useConnectionStatus is now iron-rule-clean and stays in hooks/
(cross-cutting). Test mocks retargeted to the new seam modules.

Behavior-adjacent (covered by suite; default paths identical): the local-bytes skip
helper — flag for offline-playback runtime QA alongside the M4 media-resolver seam.
2026-06-30 21:23:44 +02:00

47 lines
2.0 KiB
TypeScript

import { useAuthStore } from '@/store/authStore';
import { hasLocalPlaybackUrl } from '@/store/localPlaybackResolve';
import { isDevOfflineBrowseForced } from '@/store/devOfflineBrowseStore';
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { isActiveServerReachable } from '@/lib/network/activeServerReachability';
function isSameServerProfile(a: string, b: string): boolean {
if (!a || !b) return false;
if (a === b) return true;
return resolveServerIdForIndexKey(a) === resolveServerIdForIndexKey(b);
}
/**
* Whether `serverId` is worth a best-effort Subsonic call while the browser is
* online. The active profile uses the connection-status probe; other profiles
* (e.g. queue playback while browsing another server) attempt optimistically.
*/
export function isSubsonicServerReachable(serverId: string): boolean {
if (!serverId) return false;
if (isDevOfflineBrowseForced()) return false;
if (typeof navigator !== 'undefined' && !navigator.onLine) return false;
const activeId = useAuthStore.getState().activeServerId;
if (!activeId || isSameServerProfile(serverId, activeId)) {
return isActiveServerReachable();
}
return true;
}
/**
* Whether a Subsonic API call for `serverId` is worth attempting.
* Skips when the browser or target server is down, or when the track already
* plays from a local `psysonic-local://` URL (offline / favorite-auto bytes).
*/
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 (trackId && hasLocalPlaybackUrl(trackId, serverId)) return false;
return isSubsonicServerReachable(serverId);
}
/** Convenience for call sites that only know the active server id. */
export function shouldAttemptSubsonicForActiveServer(): boolean {
const activeId = useAuthStore.getState().activeServerId;
return activeId ? shouldAttemptSubsonicForServer(activeId) : false;
}