mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
refactor(orbit): co-locate orbit feature into features/orbit
This commit is contained in:
@@ -20,8 +20,7 @@ import SongInfoModal from '../components/SongInfoModal';
|
||||
import { DownloadFolderModal } from '@/features/offline';
|
||||
import GlobalConfirmModal from '../components/GlobalConfirmModal';
|
||||
import ThemeMigrationNotice from '../components/ThemeMigrationNotice';
|
||||
import OrbitAccountPicker from '../components/OrbitAccountPicker';
|
||||
import OrbitHelpModal from '../components/OrbitHelpModal';
|
||||
import { OrbitAccountPicker, OrbitHelpModal } from '@/features/orbit';
|
||||
import TooltipPortal from '@/ui/TooltipPortal';
|
||||
import OverlayScrollArea from '@/ui/OverlayScrollArea';
|
||||
import {
|
||||
@@ -33,11 +32,10 @@ import MusicNetworkIndicator from '../components/MusicNetworkIndicator';
|
||||
import { OfflineBanner } from '@/features/offline';
|
||||
import AppUpdater from '../components/AppUpdater';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import OrbitSessionBar from '../components/OrbitSessionBar';
|
||||
import OrbitStartTrigger from '../components/OrbitStartTrigger';
|
||||
import { useOrbitHost } from '../hooks/useOrbitHost';
|
||||
import { useOrbitGuest } from '../hooks/useOrbitGuest';
|
||||
import { useOrbitBodyAttrs } from '../hooks/useOrbitBodyAttrs';
|
||||
import { OrbitSessionBar, OrbitStartTrigger } from '@/features/orbit';
|
||||
import { useOrbitHost } from '@/features/orbit';
|
||||
import { useOrbitGuest } from '@/features/orbit';
|
||||
import { useOrbitBodyAttrs } from '@/features/orbit';
|
||||
import { usePlatformShellSetup } from '../hooks/usePlatformShellSetup';
|
||||
import { useOfflineBrowseContext } from '@/features/offline';
|
||||
import { offlineBrowseNavFlags } from '@/features/offline';
|
||||
@@ -67,7 +65,7 @@ import { useThemeStore } from '../store/themeStore';
|
||||
import { useFontStore } from '../store/fontStore';
|
||||
import { useEqStore } from '../store/eqStore';
|
||||
import { usePlaybackRateStore } from '../store/playbackRateStore';
|
||||
import { usePlaybackRateOrbitSync } from '../hooks/usePlaybackRateOrbitSync';
|
||||
import { usePlaybackRateOrbitSync } from '@/features/orbit';
|
||||
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
|
||||
import {
|
||||
persistSidebarCollapsed,
|
||||
|
||||
@@ -30,7 +30,7 @@ vi.mock('@/api/subsonic', () => ({
|
||||
}));
|
||||
|
||||
|
||||
vi.mock('@/utils/orbitBulkGuard', () => ({
|
||||
vi.mock('@/features/orbit', () => ({
|
||||
orbitBulkGuard: vi.fn(async () => true),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { Track } from '../store/playerStoreTypes';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
|
||||
@@ -3,14 +3,14 @@ import type { SubsonicSong } from '../api/subsonicTypes';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Check, X, Inbox } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import {
|
||||
approveOrbitSuggestion,
|
||||
declineOrbitSuggestion,
|
||||
suggestionKey,
|
||||
} from '../utils/orbit';
|
||||
} from '@/features/orbit';
|
||||
import { TrackCoverArtImage } from '../cover/TrackCoverArtImage';
|
||||
import { ORBIT_DEFAULT_SETTINGS } from '../api/orbit';
|
||||
import { ORBIT_DEFAULT_SETTINGS } from '@/features/orbit';
|
||||
|
||||
const HOST_APPROVAL_COVER_CSS_PX = 36;
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import {
|
||||
findSessionPlaylistId,
|
||||
readOrbitState,
|
||||
OrbitJoinError,
|
||||
} from '../utils/orbit';
|
||||
} from '@/features/orbit';
|
||||
import { switchActiveServer } from '../utils/server/switchActiveServer';
|
||||
import { useOrbitAccountPickerStore } from '../store/orbitAccountPickerStore';
|
||||
import { useOrbitAccountPickerStore } from '@/features/orbit';
|
||||
import ConfirmModal from './ConfirmModal';
|
||||
|
||||
const ORBIT_JOIN_ERROR_KEYS: Record<string, string> = {
|
||||
|
||||
@@ -26,7 +26,7 @@ vi.mock('@/api/subsonic', () => ({
|
||||
}));
|
||||
|
||||
|
||||
vi.mock('@/utils/orbitBulkGuard', () => ({
|
||||
vi.mock('@/features/orbit', () => ({
|
||||
orbitBulkGuard: vi.fn(async () => true),
|
||||
}));
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@ import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import type { Track } from '../store/playerStoreTypes';
|
||||
import { useState, useRef, useMemo } from 'react';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import OrbitGuestQueue from './OrbitGuestQueue';
|
||||
import OrbitQueueHead from './OrbitQueueHead';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { OrbitGuestQueue, OrbitQueueHead } from '@/features/orbit';
|
||||
import HostApprovalQueue from './HostApprovalQueue';
|
||||
import { usePlaylistStore } from '../store/playlistStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useTrackCoverRef } from '../cover/useLibraryCoverRef';
|
||||
import { COVER_DENSE_RAIL_CELL_CSS_PX } from '../cover/layoutSizes';
|
||||
import { enqueueAndPlay } from '../utils/playback/playSong';
|
||||
import { useDragDrop } from '../contexts/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { useNavigateToAlbum } from '../hooks/useNavigateToAlbum';
|
||||
import { useNavigateToArtist } from '../hooks/useNavigateToArtist';
|
||||
import { OpenArtistRefInline } from './OpenArtistRefInline';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { enqueueAndPlay } from '../utils/playback/playSong';
|
||||
import { useDragDrop } from '../contexts/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { formatTrackTime } from '../utils/format/formatDuration';
|
||||
import { resolveTrackArtistRefs } from '../utils/playback/trackArtistRefs';
|
||||
import { tooltipAttrs } from '@/ui/tooltipAttrs';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AudioLines, ChevronRight, Play, Square } from 'lucide-react';
|
||||
import type { SubsonicAlbum, SubsonicSong } from '../../api/subsonicTypes';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '../../store/previewStore';
|
||||
import { useOrbitSongRowBehavior } from '../../hooks/useOrbitSongRowBehavior';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||
import { formatTrackTime } from '../../utils/format/formatDuration';
|
||||
import ArtistTopTrackCover from './ArtistTopTrackCover';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useAuthStore } from '../../store/authStore';
|
||||
import { usePlaylistStore } from '../../store/playlistStore';
|
||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import { suggestOrbitTrack, hostEnqueueToOrbit, evaluateOrbitSuggestGate, OrbitSuggestBlockedError } from '../../utils/orbit';
|
||||
import { suggestOrbitTrack, hostEnqueueToOrbit, evaluateOrbitSuggestGate, OrbitSuggestBlockedError } from '@/features/orbit';
|
||||
import { renderPresetIcon } from '../settings/musicNetwork/presetIcon';
|
||||
import StarRating from '../StarRating';
|
||||
import { AddToPlaylistSubmenu } from './AddToPlaylistSubmenu';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createPortal } from 'react-dom';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { PlaybackRateControls } from '@/features/settings';
|
||||
import { usePlaybackRateStore } from '../../store/playbackRateStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import {
|
||||
clampPlaybackPitch,
|
||||
clampPlaybackSpeed,
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
playbackSpeedStep,
|
||||
varispeedSpeedFromSemitones,
|
||||
} from '../../utils/audio/playbackRateHelpers';
|
||||
import { isOrbitPlaybackSyncActive } from '../../utils/orbit';
|
||||
import { isOrbitPlaybackSyncActive } from '@/features/orbit';
|
||||
import { usePlayerBarAnchoredPopover } from '../../hooks/usePlayerBarAnchoredPopover';
|
||||
|
||||
const POPOVER_WIDTH = 320;
|
||||
|
||||
@@ -16,7 +16,7 @@ import { usePlayerStore } from '../../store/playerStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '../../store/previewStore';
|
||||
import { useThemeStore } from '../../store/themeStore';
|
||||
import { useDragDrop } from '../../contexts/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '../../hooks/useOrbitSongRowBehavior';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||
import type { PlaylistSortKey, PlaylistSortDir } from '../../utils/playlist/playlistDisplayedSongs';
|
||||
import { AddToPlaylistSubmenu } from '../ContextMenu';
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
QueueToolbarButtonId,
|
||||
} from '../../store/queueToolbarStore';
|
||||
import { getTransitionMode, setTransitionMode } from '../../utils/playback/playbackTransition';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
|
||||
interface Props {
|
||||
queue: QueueItemRef[];
|
||||
|
||||
@@ -11,7 +11,7 @@ import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
|
||||
import { useSelectionStore } from '@/store/selectionStore';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useDragDrop } from '@/contexts/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '@/hooks/useOrbitSongRowBehavior';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { appendServerQuery } from '@/utils/navigation/detailServerScope';
|
||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X, User } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitAccountPickerStore } from '../store/orbitAccountPickerStore';
|
||||
import { useOrbitAccountPickerStore } from '@/features/orbit/store/orbitAccountPickerStore';
|
||||
|
||||
/**
|
||||
* Modal shown when joining an Orbit session and the user has more than
|
||||
+5
-5
@@ -2,16 +2,16 @@ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Copy, Trash2 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import { computeOrbitDriftMs } from '../utils/orbit';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { computeOrbitDriftMs } from '@/features/orbit/utils/orbit';
|
||||
import {
|
||||
clearOrbitEvents,
|
||||
formatOrbitEvents,
|
||||
getOrbitEvents,
|
||||
subscribeOrbitEvents,
|
||||
} from '../utils/orbitDiag';
|
||||
} from '@/features/orbit/utils/orbitDiag';
|
||||
|
||||
interface Props {
|
||||
anchorRef: React.RefObject<HTMLElement | null>;
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { leaveOrbitSession } from '../utils/orbit';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { leaveOrbitSession } from '@/features/orbit/utils/orbit';
|
||||
|
||||
/**
|
||||
* Orbit — exit notification modal.
|
||||
+5
-5
@@ -1,11 +1,11 @@
|
||||
import { getSong } from '../api/subsonicLibrary';
|
||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
||||
import { getSong } from '@/api/subsonicLibrary';
|
||||
import type { SubsonicSong } from '@/api/subsonicTypes';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Radio, Clock } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { TrackCoverArtImage } from '../cover/TrackCoverArtImage';
|
||||
import OrbitQueueHead from './OrbitQueueHead';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { TrackCoverArtImage } from '@/cover/TrackCoverArtImage';
|
||||
import OrbitQueueHead from '@/features/orbit/components/OrbitQueueHead';
|
||||
|
||||
const ORBIT_QUEUE_COVER_LG_CSS_PX = 54;
|
||||
const ORBIT_QUEUE_COVER_SM_CSS_PX = 48;
|
||||
+1
-1
@@ -5,7 +5,7 @@ import {
|
||||
ListMusic, Inbox, Sliders, LogOut,
|
||||
} from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHelpModalStore } from '../store/helpModalStore';
|
||||
import { useHelpModalStore } from '@/store/helpModalStore';
|
||||
import { SettingsSubSection } from '@/features/settings';
|
||||
|
||||
/**
|
||||
+5
-5
@@ -2,16 +2,16 @@ import { useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X, LogIn, ClipboardPaste } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import {
|
||||
parseOrbitShareLink,
|
||||
findSessionPlaylistId,
|
||||
readOrbitState,
|
||||
joinOrbitSession,
|
||||
} from '../utils/orbit';
|
||||
import { switchActiveServer } from '../utils/server/switchActiveServer';
|
||||
import { useOrbitAccountPickerStore } from '../store/orbitAccountPickerStore';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
} from '@/features/orbit/utils/orbit';
|
||||
import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
||||
import { useOrbitAccountPickerStore } from '@/features/orbit/store/orbitAccountPickerStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
+3
-3
@@ -2,9 +2,9 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Crown, User, UserMinus, ShieldOff, Mic, MicOff } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { kickOrbitParticipant, removeOrbitParticipant, setOrbitSuggestionBlocked } from '../utils/orbit';
|
||||
import ConfirmModal from './ConfirmModal';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { kickOrbitParticipant, removeOrbitParticipant, setOrbitSuggestionBlocked } from '@/features/orbit/utils/orbit';
|
||||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
|
||||
interface Props {
|
||||
/** Anchor — we position the popover directly below its bottom-right. */
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Users, Wifi, WifiOff } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import type { OrbitState } from '../api/orbit';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import type { OrbitState } from '@/features/orbit/api/orbit';
|
||||
|
||||
interface Props {
|
||||
state: OrbitState;
|
||||
+14
-14
@@ -1,25 +1,25 @@
|
||||
import { getSong } from '../api/subsonicLibrary';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { getSong } from '@/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { X, RefreshCw, Shuffle, Settings2, Share2, HelpCircle, Activity } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useHelpModalStore } from '../store/helpModalStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { useHelpModalStore } from '@/store/helpModalStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import {
|
||||
endOrbitSession,
|
||||
leaveOrbitSession,
|
||||
computeOrbitDriftMs,
|
||||
effectiveShuffleIntervalMs,
|
||||
} from '../utils/orbit';
|
||||
import { estimateLivePosition } from '../api/orbit';
|
||||
import OrbitParticipantsPopover from './OrbitParticipantsPopover';
|
||||
import OrbitExitModal from './OrbitExitModal';
|
||||
import OrbitSettingsPopover from './OrbitSettingsPopover';
|
||||
import OrbitSharePopover from './OrbitSharePopover';
|
||||
import OrbitDiagnosticsPopover from './OrbitDiagnosticsPopover';
|
||||
import ConfirmModal from './ConfirmModal';
|
||||
import { formatTrackTime } from '../utils/format/formatDuration';
|
||||
} from '@/features/orbit/utils/orbit';
|
||||
import { estimateLivePosition } from '@/features/orbit/api/orbit';
|
||||
import OrbitParticipantsPopover from '@/features/orbit/components/OrbitParticipantsPopover';
|
||||
import OrbitExitModal from '@/features/orbit/components/OrbitExitModal';
|
||||
import OrbitSettingsPopover from '@/features/orbit/components/OrbitSettingsPopover';
|
||||
import OrbitSharePopover from '@/features/orbit/components/OrbitSharePopover';
|
||||
import OrbitDiagnosticsPopover from '@/features/orbit/components/OrbitDiagnosticsPopover';
|
||||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
import { formatTrackTime } from '@/utils/format/formatDuration';
|
||||
|
||||
/**
|
||||
* Orbit — top-strip session indicator.
|
||||
+4
-4
@@ -2,10 +2,10 @@ import { useEffect, useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Shuffle } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { updateOrbitSettings, triggerOrbitShuffleNow } from '../utils/orbit';
|
||||
import { ORBIT_DEFAULT_SETTINGS, ORBIT_SHUFFLE_INTERVAL_PRESETS_MIN, type OrbitShuffleIntervalMin } from '../api/orbit';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { updateOrbitSettings, triggerOrbitShuffleNow } from '@/features/orbit/utils/orbit';
|
||||
import { ORBIT_DEFAULT_SETTINGS, ORBIT_SHUFFLE_INTERVAL_PRESETS_MIN, type OrbitShuffleIntervalMin } from '@/features/orbit/api/orbit';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
|
||||
interface Props {
|
||||
anchorRef: React.RefObject<HTMLElement | null>;
|
||||
+4
-4
@@ -2,10 +2,10 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Copy, Check } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { buildOrbitShareLink } from '../utils/orbit';
|
||||
import { serverShareBaseUrl } from '../utils/server/serverEndpoint';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { buildOrbitShareLink } from '@/features/orbit/utils/orbit';
|
||||
import { serverShareBaseUrl } from '@/utils/server/serverEndpoint';
|
||||
|
||||
interface Props {
|
||||
anchorRef: React.RefObject<HTMLElement | null>;
|
||||
+6
-6
@@ -9,12 +9,12 @@ import {
|
||||
startOrbitSession,
|
||||
buildOrbitShareLink,
|
||||
generateSessionId,
|
||||
} from '../utils/orbit';
|
||||
import { randomOrbitSessionName } from '../utils/orbitNames';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { isLanUrl, serverShareBaseUrl } from '../utils/server/serverEndpoint';
|
||||
import { ORBIT_DEFAULT_MAX_USERS } from '../api/orbit';
|
||||
} from '@/features/orbit/utils/orbit';
|
||||
import { randomOrbitSessionName } from '@/features/orbit/utils/orbitNames';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { isLanUrl, serverShareBaseUrl } from '@/utils/server/serverEndpoint';
|
||||
import { ORBIT_DEFAULT_MAX_USERS } from '@/features/orbit/api/orbit';
|
||||
|
||||
interface Props { onClose: () => void; }
|
||||
|
||||
+6
-6
@@ -2,12 +2,12 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Orbit as OrbitIcon, Plus, LogIn, HelpCircle } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useHelpModalStore } from '../store/helpModalStore';
|
||||
import OrbitStartModal from './OrbitStartModal';
|
||||
import OrbitJoinModal from './OrbitJoinModal';
|
||||
import OrbitWordmark from './OrbitWordmark';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useHelpModalStore } from '@/store/helpModalStore';
|
||||
import OrbitStartModal from '@/features/orbit/components/OrbitStartModal';
|
||||
import OrbitJoinModal from '@/features/orbit/components/OrbitJoinModal';
|
||||
import OrbitWordmark from '@/features/orbit/components/OrbitWordmark';
|
||||
|
||||
/**
|
||||
* Topbar trigger — opens a small launch popover offering three choices:
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
|
||||
/**
|
||||
* Mirror the live Orbit role + phase onto `<html data-orbit-active>` and
|
||||
@@ -1,9 +1,9 @@
|
||||
import { getSong } from '../api/subsonicLibrary';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { getSong } from '@/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import {
|
||||
readOrbitState,
|
||||
applyOrbitTransitionSettings,
|
||||
@@ -13,12 +13,12 @@ import {
|
||||
planPendingResends,
|
||||
forgetPendingSuggestion,
|
||||
resetPendingResendState,
|
||||
} from '../utils/orbit';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import i18n from '../i18n';
|
||||
import { estimateLivePosition, type OrbitState } from '../api/orbit';
|
||||
import { pushOrbitEvent } from '../utils/orbitDiag';
|
||||
import { useOrbitOutboxHeartbeat } from './useOrbitOutboxHeartbeat';
|
||||
} from '@/features/orbit/utils/orbit';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import i18n from '@/i18n';
|
||||
import { estimateLivePosition, type OrbitState } from '@/features/orbit/api/orbit';
|
||||
import { pushOrbitEvent } from '@/features/orbit/utils/orbitDiag';
|
||||
import { useOrbitOutboxHeartbeat } from '@/features/orbit/hooks/useOrbitOutboxHeartbeat';
|
||||
|
||||
/**
|
||||
* Orbit — guest-side tick hook.
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getSong } from '../api/subsonicLibrary';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { getSong } from '@/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { useEffect } from 'react';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import {
|
||||
writeOrbitState,
|
||||
sweepGuestOutboxes,
|
||||
@@ -12,17 +12,17 @@ import {
|
||||
makeCoalescedRunner,
|
||||
readOrbitTransitionSettings,
|
||||
suggestionKey,
|
||||
} from '../utils/orbit';
|
||||
} from '@/features/orbit/utils/orbit';
|
||||
import {
|
||||
ORBIT_DEFAULT_SETTINGS,
|
||||
ORBIT_PLAY_QUEUE_LIMIT,
|
||||
type OrbitState,
|
||||
type OrbitQueueItem,
|
||||
} from '../api/orbit';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import i18n from '../i18n';
|
||||
import { pushOrbitEvent } from '../utils/orbitDiag';
|
||||
import { useOrbitOutboxHeartbeat } from './useOrbitOutboxHeartbeat';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import i18n from '@/i18n';
|
||||
import { pushOrbitEvent } from '@/features/orbit/utils/orbitDiag';
|
||||
import { useOrbitOutboxHeartbeat } from '@/features/orbit/hooks/useOrbitOutboxHeartbeat';
|
||||
|
||||
/**
|
||||
* Orbit — host-side tick hook.
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { writeOrbitHeartbeat } from '../utils/orbit';
|
||||
import { orbitOutboxPlaylistName } from '../api/orbit';
|
||||
import { writeOrbitHeartbeat } from '@/features/orbit/utils/orbit';
|
||||
import { orbitOutboxPlaylistName } from '@/features/orbit/api/orbit';
|
||||
|
||||
const HEARTBEAT_TICK_MS = 10_000;
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import {
|
||||
suggestOrbitTrack,
|
||||
hostEnqueueToOrbit,
|
||||
evaluateOrbitSuggestGate,
|
||||
OrbitSuggestBlockedError,
|
||||
} from '../utils/orbit';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
} from '@/features/orbit/utils/orbit';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
|
||||
/**
|
||||
* Shared behaviour for song rows that in "normal mode" swallow a full list
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { usePlaybackRateStore } from '../store/playbackRateStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlaybackRateStore } from '@/store/playbackRateStore';
|
||||
|
||||
/** Re-sync playback rate when Orbit enters or leaves shared playback. */
|
||||
export function usePlaybackRateOrbitSync() {
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Orbit feature — multi-user listen-together: the shared-session state types
|
||||
* and Navidrome-playlist transport (`api/orbit`), host/guest lifecycle +
|
||||
* moderation + outbox sweep + drift math (`utils/*`), the orbit stores, the
|
||||
* session/account hooks, and all Orbit UI (session bar, modals, popovers,
|
||||
* guest queue, wordmark). Replaces the former `utils/orbit.ts` re-export shim.
|
||||
*
|
||||
* Playback-core (`playTrackAction`, `nextAction`, `queueMutationActions`,
|
||||
* `resumeAction`, `playbackRateStore`, `playbackReportSession`, `previewStore`)
|
||||
* drives orbit and so consumes this barrel — the correct playback→orbit edge,
|
||||
* realized early (playback moves last in M3). `nextActionOrbitRadio.test.ts`
|
||||
* stays out: it tests `nextAction` (playback-core), not orbit.
|
||||
*/
|
||||
export * from './api/orbit';
|
||||
export * from './hooks/useOrbitBodyAttrs';
|
||||
export * from './hooks/useOrbitGuest';
|
||||
export * from './hooks/useOrbitHost';
|
||||
export * from './hooks/useOrbitOutboxHeartbeat';
|
||||
export * from './hooks/useOrbitSongRowBehavior';
|
||||
export * from './hooks/usePlaybackRateOrbitSync';
|
||||
export * from './store/orbitAccountPickerStore';
|
||||
export * from './store/orbitSession';
|
||||
export * from './store/orbitStore';
|
||||
export * from './utils/cleanup';
|
||||
export * from './utils/constants';
|
||||
export * from './utils/guest';
|
||||
export * from './utils/helpers';
|
||||
export * from './utils/host';
|
||||
export * from './utils/moderation';
|
||||
export * from './utils/orbitBulkGuard';
|
||||
export * from './utils/orbitDiag';
|
||||
export * from './utils/orbitNames';
|
||||
export * from './utils/pendingResend';
|
||||
export * from './utils/remote';
|
||||
export * from './utils/sessionActive';
|
||||
export * from './utils/shareLink';
|
||||
export * from './utils/stateMath';
|
||||
export * from './utils/sweep';
|
||||
export * from './utils/transitions';
|
||||
export { default as OrbitAccountPicker } from './components/OrbitAccountPicker';
|
||||
export { default as OrbitDiagnosticsPopover } from './components/OrbitDiagnosticsPopover';
|
||||
export { default as OrbitExitModal } from './components/OrbitExitModal';
|
||||
export { default as OrbitGuestQueue } from './components/OrbitGuestQueue';
|
||||
export { default as OrbitHelpModal } from './components/OrbitHelpModal';
|
||||
export { default as OrbitJoinModal } from './components/OrbitJoinModal';
|
||||
export { default as OrbitParticipantsPopover } from './components/OrbitParticipantsPopover';
|
||||
export { default as OrbitQueueHead } from './components/OrbitQueueHead';
|
||||
export { default as OrbitSessionBar } from './components/OrbitSessionBar';
|
||||
export { default as OrbitSettingsPopover } from './components/OrbitSettingsPopover';
|
||||
export { default as OrbitSharePopover } from './components/OrbitSharePopover';
|
||||
export { default as OrbitStartModal } from './components/OrbitStartModal';
|
||||
export { default as OrbitStartTrigger } from './components/OrbitStartTrigger';
|
||||
export { default as OrbitWordmark } from './components/OrbitWordmark';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { ServerProfile } from './authStoreTypes';
|
||||
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||
import { create } from 'zustand';
|
||||
let _resolve: ((server: ServerProfile | null) => void) | null = null;
|
||||
|
||||
@@ -7,11 +7,11 @@ const { orbitState } = vi.hoisted(() => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('./orbitStore', () => ({
|
||||
vi.mock('@/features/orbit/store/orbitStore', () => ({
|
||||
useOrbitStore: { getState: () => orbitState },
|
||||
}));
|
||||
|
||||
import { isInOrbitSession } from './orbitSession';
|
||||
import { isInOrbitSession } from '@/features/orbit/store/orbitSession';
|
||||
|
||||
beforeEach(() => {
|
||||
orbitState.role = null;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useOrbitStore } from './orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
|
||||
/**
|
||||
* True when the user is part of an Orbit session (any role, any phase short
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand';
|
||||
import type { OrbitState } from '../api/orbit';
|
||||
import type { OrbitState } from '@/features/orbit/api/orbit';
|
||||
|
||||
/**
|
||||
* Orbit — local session store.
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
orbitOutboxPlaylistName,
|
||||
orbitSessionPlaylistName,
|
||||
type OrbitState,
|
||||
} from '../../api/orbit';
|
||||
import { ORBIT_ORPHAN_TTL_MS } from './constants';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
import { ORBIT_ORPHAN_TTL_MS } from '@/features/orbit/utils/constants';
|
||||
|
||||
const { getPlaylists, deletePlaylist } = vi.hoisted(() => ({
|
||||
getPlaylists: vi.fn(),
|
||||
@@ -19,19 +19,19 @@ const { authState, orbitState } = vi.hoisted(() => ({
|
||||
orbitState: { sessionId: null as string | null },
|
||||
}));
|
||||
|
||||
vi.mock('../../api/subsonicPlaylists', () => ({ getPlaylists, deletePlaylist }));
|
||||
vi.mock('../../store/authStore', () => ({
|
||||
vi.mock('@/api/subsonicPlaylists', () => ({ getPlaylists, deletePlaylist }));
|
||||
vi.mock('@/store/authStore', () => ({
|
||||
useAuthStore: {
|
||||
getState: () => ({
|
||||
getActiveServer: () => (authState.username ? { username: authState.username } : undefined),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
vi.mock('../../store/orbitStore', () => ({
|
||||
vi.mock('@/features/orbit/store/orbitStore', () => ({
|
||||
useOrbitStore: { getState: () => ({ sessionId: orbitState.sessionId }) },
|
||||
}));
|
||||
|
||||
import { cleanupOrphanedOrbitPlaylists } from './cleanup';
|
||||
import { cleanupOrphanedOrbitPlaylists } from '@/features/orbit/utils/cleanup';
|
||||
|
||||
type FakePlaylist = {
|
||||
id: string;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { deletePlaylist, getPlaylists } from '../../api/subsonicPlaylists';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { ORBIT_PLAYLIST_PREFIX, parseOrbitState } from '../../api/orbit';
|
||||
import { ORBIT_ORPHAN_TTL_MS } from './constants';
|
||||
import { deletePlaylist, getPlaylists } from '@/api/subsonicPlaylists';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { ORBIT_PLAYLIST_PREFIX, parseOrbitState } from '@/features/orbit/api/orbit';
|
||||
import { ORBIT_ORPHAN_TTL_MS } from '@/features/orbit/utils/constants';
|
||||
|
||||
/**
|
||||
* App-start sweep: delete our own __psyorbit_* playlists that no longer
|
||||
@@ -1,17 +1,17 @@
|
||||
import { createPlaylist, deletePlaylist, getPlaylist, getPlaylists, updatePlaylist } from '../../api/subsonicPlaylists';
|
||||
import { getSong } from '../../api/subsonicLibrary';
|
||||
import { songToTrack } from '../playback/songToTrack';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { createPlaylist, deletePlaylist, getPlaylist, getPlaylists, updatePlaylist } from '@/api/subsonicPlaylists';
|
||||
import { getSong } from '@/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import {
|
||||
orbitOutboxPlaylistName,
|
||||
type OrbitQueueItem,
|
||||
type OrbitState,
|
||||
} from '../../api/orbit';
|
||||
import { suggestionKey } from './helpers';
|
||||
import { notePendingSuggestion } from './pendingResend';
|
||||
import { findSessionPlaylistId, readOrbitState, writeOrbitHeartbeat } from './remote';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
import { suggestionKey } from '@/features/orbit/utils/helpers';
|
||||
import { notePendingSuggestion } from '@/features/orbit/utils/pendingResend';
|
||||
import { findSessionPlaylistId, readOrbitState, writeOrbitHeartbeat } from '@/features/orbit/utils/remote';
|
||||
|
||||
export class OrbitJoinError extends Error {
|
||||
constructor(
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
parseOrbitState,
|
||||
type OrbitQueueItem,
|
||||
type OrbitState,
|
||||
} from '../../api/orbit';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
import {
|
||||
makeCoalescedRunner,
|
||||
OrbitStateTooLarge,
|
||||
serialiseOrbitState,
|
||||
serialiseOrbitStateForWire,
|
||||
} from './helpers';
|
||||
} from '@/features/orbit/utils/helpers';
|
||||
|
||||
function baseState(): OrbitState {
|
||||
return makeInitialOrbitState({ sid: 'aaaa1111', host: 'host', name: 'sesh' });
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
type OrbitOutboxMeta,
|
||||
type OrbitQueueItem,
|
||||
type OrbitState,
|
||||
} from '../../api/orbit';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
|
||||
/** 8 lowercase hex chars — unique enough for concurrent-session collision-free naming. */
|
||||
export function generateSessionId(): string {
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ORBIT_DEFAULT_SETTINGS,
|
||||
type OrbitSettings,
|
||||
type OrbitState,
|
||||
} from '../../api/orbit';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
|
||||
const { writeOrbitState } = vi.hoisted(() => ({ writeOrbitState: vi.fn(() => Promise.resolve()) }));
|
||||
const { orbitStore } = vi.hoisted(() => ({
|
||||
@@ -17,18 +17,18 @@ const { orbitStore } = vi.hoisted(() => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('./remote', () => ({
|
||||
vi.mock('@/features/orbit/utils/remote', () => ({
|
||||
writeOrbitState,
|
||||
writeOrbitHeartbeat: vi.fn(() => Promise.resolve()),
|
||||
}));
|
||||
vi.mock('../../store/orbitStore', () => ({ useOrbitStore: { getState: () => orbitStore } }));
|
||||
vi.mock('../../store/authStore', () => ({ useAuthStore: { getState: () => ({}) } }));
|
||||
vi.mock('../../store/playerStore', () => ({ usePlayerStore: { getState: () => ({ enqueue: vi.fn() }) } }));
|
||||
vi.mock('../../api/subsonicPlaylists', () => ({ createPlaylist: vi.fn(), deletePlaylist: vi.fn() }));
|
||||
vi.mock('../../api/subsonicLibrary', () => ({ getSong: vi.fn() }));
|
||||
vi.mock('../playback/songToTrack', () => ({ songToTrack: vi.fn() }));
|
||||
vi.mock('@/features/orbit/store/orbitStore', () => ({ useOrbitStore: { getState: () => orbitStore } }));
|
||||
vi.mock('@/store/authStore', () => ({ useAuthStore: { getState: () => ({}) } }));
|
||||
vi.mock('@/store/playerStore', () => ({ usePlayerStore: { getState: () => ({ enqueue: vi.fn() }) } }));
|
||||
vi.mock('@/api/subsonicPlaylists', () => ({ createPlaylist: vi.fn(), deletePlaylist: vi.fn() }));
|
||||
vi.mock('@/api/subsonicLibrary', () => ({ getSong: vi.fn() }));
|
||||
vi.mock('@/utils/playback/songToTrack', () => ({ songToTrack: vi.fn() }));
|
||||
|
||||
import { updateOrbitSettings } from './host';
|
||||
import { updateOrbitSettings } from '@/features/orbit/utils/host';
|
||||
|
||||
function hostStateWith(settings: OrbitSettings | undefined): OrbitState {
|
||||
const base = makeInitialOrbitState({ sid: 'aaaa1111', host: 'host', name: 'sesh' });
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createPlaylist, deletePlaylist } from '../../api/subsonicPlaylists';
|
||||
import { getSong } from '../../api/subsonicLibrary';
|
||||
import { songToTrack } from '../playback/songToTrack';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { createPlaylist, deletePlaylist } from '@/api/subsonicPlaylists';
|
||||
import { getSong } from '@/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import {
|
||||
makeInitialOrbitState,
|
||||
orbitOutboxPlaylistName,
|
||||
@@ -13,10 +13,10 @@ import {
|
||||
type OrbitQueueItem,
|
||||
type OrbitSettings,
|
||||
type OrbitState,
|
||||
} from '../../api/orbit';
|
||||
import { generateSessionId } from './helpers';
|
||||
import { readOrbitTransitionSettings } from './transitions';
|
||||
import { writeOrbitHeartbeat, writeOrbitState } from './remote';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
import { generateSessionId } from '@/features/orbit/utils/helpers';
|
||||
import { readOrbitTransitionSettings } from '@/features/orbit/utils/transitions';
|
||||
import { writeOrbitHeartbeat, writeOrbitState } from '@/features/orbit/utils/remote';
|
||||
|
||||
export interface StartOrbitArgs {
|
||||
/** Human-readable name the host chose. */
|
||||
@@ -1,7 +1,7 @@
|
||||
import { deletePlaylist, getPlaylists } from '../../api/subsonicPlaylists';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { orbitOutboxPlaylistName, type OrbitState } from '../../api/orbit';
|
||||
import { writeOrbitState } from './remote';
|
||||
import { deletePlaylist, getPlaylists } from '@/api/subsonicPlaylists';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { orbitOutboxPlaylistName, type OrbitState } from '@/features/orbit/api/orbit';
|
||||
import { writeOrbitState } from '@/features/orbit/utils/remote';
|
||||
|
||||
/**
|
||||
* Host: kick a participant by username.
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Orbit — multi-user listen-together feature.
|
||||
*
|
||||
* This file is a re-export shim. Implementation lives under `utils/orbit/`:
|
||||
* Orbit utils aggregator — re-exports every public symbol from the sibling
|
||||
* `utils/*` modules under one specifier. Orbit's own components/hooks import
|
||||
* from here; the rest of the app goes through the feature barrel (`index.ts`).
|
||||
*
|
||||
* - `constants.ts` — cadence + TTL ms values.
|
||||
* - `helpers.ts` — pure utilities (id gen, serialisation, key fns).
|
||||
@@ -14,9 +14,6 @@
|
||||
* approve/decline reactions.
|
||||
* - `sweep.ts` — host-side outbox sweep loop.
|
||||
* - `cleanup.ts` — app-start orphan playlist sweep.
|
||||
*
|
||||
* Importers everywhere else in the codebase keep using `'../utils/orbit'`;
|
||||
* this shim re-exports every public symbol unchanged.
|
||||
*/
|
||||
|
||||
export {
|
||||
@@ -24,39 +21,39 @@ export {
|
||||
ORBIT_ORPHAN_TTL_MS,
|
||||
ORBIT_REMOVED_TTL_MS,
|
||||
ORBIT_SHUFFLE_INTERVAL_MS,
|
||||
} from './orbit/constants';
|
||||
} from './constants';
|
||||
export {
|
||||
generateSessionId,
|
||||
makeCoalescedRunner,
|
||||
OrbitStateTooLarge,
|
||||
serialiseOrbitState,
|
||||
suggestionKey,
|
||||
} from './orbit/helpers';
|
||||
export { isOrbitPlaybackSyncActive } from './orbit/sessionActive';
|
||||
} from './helpers';
|
||||
export { isOrbitPlaybackSyncActive } from './sessionActive';
|
||||
export {
|
||||
applyOutboxSnapshotsToState,
|
||||
computeOrbitDriftMs,
|
||||
effectiveShuffleIntervalMs,
|
||||
maybeShuffleQueue,
|
||||
patchOrbitState,
|
||||
} from './orbit/stateMath';
|
||||
} from './stateMath';
|
||||
export {
|
||||
findSessionPlaylistId,
|
||||
readOrbitState,
|
||||
writeOrbitHeartbeat,
|
||||
writeOrbitState,
|
||||
} from './orbit/remote';
|
||||
} from './remote';
|
||||
export {
|
||||
readOrbitTransitionSettings,
|
||||
applyOrbitTransitionSettings,
|
||||
saveGuestTransitionsOnce,
|
||||
restoreGuestTransitions,
|
||||
} from './orbit/transitions';
|
||||
} from './transitions';
|
||||
export {
|
||||
buildOrbitShareLink,
|
||||
parseOrbitShareLink,
|
||||
type OrbitShareLink,
|
||||
} from './orbit/shareLink';
|
||||
} from './shareLink';
|
||||
export {
|
||||
endOrbitSession,
|
||||
hostEnqueueToOrbit,
|
||||
@@ -64,12 +61,12 @@ export {
|
||||
triggerOrbitShuffleNow,
|
||||
updateOrbitSettings,
|
||||
type StartOrbitArgs,
|
||||
} from './orbit/host';
|
||||
} from './host';
|
||||
export {
|
||||
kickOrbitParticipant,
|
||||
removeOrbitParticipant,
|
||||
setOrbitSuggestionBlocked,
|
||||
} from './orbit/moderation';
|
||||
} from './moderation';
|
||||
export {
|
||||
approveOrbitSuggestion,
|
||||
declineOrbitSuggestion,
|
||||
@@ -81,11 +78,11 @@ export {
|
||||
OrbitSuggestBlockedError,
|
||||
suggestOrbitTrack,
|
||||
type OrbitSuggestGateReason,
|
||||
} from './orbit/guest';
|
||||
} from './guest';
|
||||
export {
|
||||
forgetPendingSuggestion,
|
||||
planPendingResends,
|
||||
resetPendingResendState,
|
||||
} from './orbit/pendingResend';
|
||||
export { sweepGuestOutboxes } from './orbit/sweep';
|
||||
export { cleanupOrphanedOrbitPlaylists } from './orbit/cleanup';
|
||||
} from './pendingResend';
|
||||
export { sweepGuestOutboxes } from './sweep';
|
||||
export { cleanupOrphanedOrbitPlaylists } from './cleanup';
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useConfirmModalStore } from '../store/confirmModalStore';
|
||||
import i18n from '../i18n';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { useConfirmModalStore } from '@/store/confirmModalStore';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
/**
|
||||
* Ask the user before dropping many tracks into the shared Orbit queue.
|
||||
@@ -11,7 +11,7 @@
|
||||
* for power users who want a persistent file.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
/** Hard cap so a long session doesn't spend memory unbounded. ~200 entries. */
|
||||
const MAX_EVENTS = 200;
|
||||
+1
-1
@@ -8,7 +8,7 @@ import {
|
||||
pendingResendTrackedCount,
|
||||
planPendingResends,
|
||||
resetPendingResendState,
|
||||
} from './pendingResend';
|
||||
} from '@/features/orbit/utils/pendingResend';
|
||||
|
||||
const T0 = 1_000_000;
|
||||
const GRACE = ORBIT_SUGGESTION_RESEND_GRACE_MS;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { getPlaylist, getPlaylists, updatePlaylistMeta } from '../../api/subsonicPlaylists';
|
||||
import { getPlaylist, getPlaylists, updatePlaylistMeta } from '@/api/subsonicPlaylists';
|
||||
import {
|
||||
orbitSessionPlaylistName,
|
||||
parseOrbitState,
|
||||
type OrbitOutboxMeta,
|
||||
type OrbitState,
|
||||
} from '../../api/orbit';
|
||||
import { serialiseOrbitStateForWire, serialiseOutboxMeta } from './helpers';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
import { serialiseOrbitStateForWire, serialiseOutboxMeta } from '@/features/orbit/utils/helpers';
|
||||
|
||||
/** Pull + parse the canonical state from the session playlist. Null on miss or parse error. */
|
||||
export async function readOrbitState(sessionPlaylistId: string): Promise<OrbitState | null> {
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { OrbitPhase, OrbitRole } from '../../store/orbitStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import type { OrbitPhase, OrbitRole } from '@/features/orbit/store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
|
||||
/**
|
||||
* True while this client is in an Orbit session on the shared playback path
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { buildOrbitShareLink, parseOrbitShareLink } from './shareLink';
|
||||
import { buildOrbitShareLink, parseOrbitShareLink } from '@/features/orbit/utils/shareLink';
|
||||
|
||||
describe('parseOrbitShareLink', () => {
|
||||
it('round-trips an https invite', () => {
|
||||
@@ -1,4 +1,4 @@
|
||||
import { decodeOrbitSharePayloadFromText, encodeSharePayload } from '../share/shareLink';
|
||||
import { decodeOrbitSharePayloadFromText, encodeSharePayload } from '@/utils/share/shareLink';
|
||||
|
||||
export interface OrbitShareLink {
|
||||
/** Base URL of the Navidrome server (decoded). */
|
||||
@@ -1,12 +1,12 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('../../store/orbitStore', () => ({
|
||||
vi.mock('@/features/orbit/store/orbitStore', () => ({
|
||||
useOrbitStore: { getState: () => ({ state: null, setState: vi.fn() }) },
|
||||
}));
|
||||
|
||||
import { makeInitialOrbitState, type OrbitQueueItem, type OrbitState } from '../../api/orbit';
|
||||
import { ORBIT_QUEUE_HISTORY_LIMIT } from './constants';
|
||||
import { applyOutboxSnapshotsToState, type OutboxSnapshot } from './stateMath';
|
||||
import { makeInitialOrbitState, type OrbitQueueItem, type OrbitState } from '@/features/orbit/api/orbit';
|
||||
import { ORBIT_QUEUE_HISTORY_LIMIT } from '@/features/orbit/utils/constants';
|
||||
import { applyOutboxSnapshotsToState, type OutboxSnapshot } from '@/features/orbit/utils/stateMath';
|
||||
|
||||
function stateWithQueue(queue: OrbitQueueItem[]): OrbitState {
|
||||
return { ...makeInitialOrbitState({ sid: 'aaaa1111', host: 'host', name: 'sesh' }), queue };
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import type {
|
||||
OrbitParticipant,
|
||||
OrbitQueueItem,
|
||||
OrbitState,
|
||||
} from '../../api/orbit';
|
||||
} from '@/features/orbit/api/orbit';
|
||||
import {
|
||||
ORBIT_HEARTBEAT_ALIVE_MS,
|
||||
ORBIT_QUEUE_HISTORY_LIMIT,
|
||||
ORBIT_REMOVED_TTL_MS,
|
||||
ORBIT_SHUFFLE_INTERVAL_MS,
|
||||
} from './constants';
|
||||
} from '@/features/orbit/utils/constants';
|
||||
|
||||
/**
|
||||
* Keep `OrbitState.queue` bounded. Drops the oldest suggestions (by `addedAt`)
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getPlaylist, getPlaylists, updatePlaylist } from '../../api/subsonicPlaylists';
|
||||
import { type OrbitOutboxMeta } from '../../api/orbit';
|
||||
import { parseOutboxPlaylistName } from './helpers';
|
||||
import { type OutboxSnapshot } from './stateMath';
|
||||
import { getPlaylist, getPlaylists, updatePlaylist } from '@/api/subsonicPlaylists';
|
||||
import { type OrbitOutboxMeta } from '@/features/orbit/api/orbit';
|
||||
import { parseOutboxPlaylistName } from '@/features/orbit/utils/helpers';
|
||||
import { type OutboxSnapshot } from '@/features/orbit/utils/stateMath';
|
||||
|
||||
/**
|
||||
* Host: list all guest outbox playlists for the current session.
|
||||
@@ -1,6 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { OrbitTransitionSettings } from '../../api/orbit';
|
||||
import type { OrbitTransitionSettings } from '@/features/orbit/api/orbit';
|
||||
|
||||
const { store, setState } = vi.hoisted(() => {
|
||||
const store = {
|
||||
@@ -20,7 +20,7 @@ const { store, setState } = vi.hoisted(() => {
|
||||
return { store, setState };
|
||||
});
|
||||
|
||||
vi.mock('../../store/authStore', () => ({
|
||||
vi.mock('@/store/authStore', () => ({
|
||||
useAuthStore: { getState: () => store.state, setState },
|
||||
}));
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
readOrbitTransitionSettings,
|
||||
restoreGuestTransitions,
|
||||
saveGuestTransitionsOnce,
|
||||
} from './transitions';
|
||||
} from '@/features/orbit/utils/transitions';
|
||||
|
||||
const GUEST_OWN: OrbitTransitionSettings = {
|
||||
crossfadeEnabled: false,
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import type { OrbitTransitionSettings } from '../../api/orbit';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import type { OrbitTransitionSettings } from '@/features/orbit/api/orbit';
|
||||
import {
|
||||
sanitizeAutodjOverlapCapMode,
|
||||
sanitizeAutodjOverlapCapSec,
|
||||
} from '../playback/autodjOverlapCap';
|
||||
} from '@/utils/playback/autodjOverlapCap';
|
||||
|
||||
/**
|
||||
* Bridge between the local playback-transition settings (in `authStore`) and
|
||||
@@ -19,9 +19,9 @@ import {
|
||||
type PlaybackStrategy,
|
||||
} from '@/utils/audio/playbackRateHelpers';
|
||||
import { usePlaybackRateStore } from '@/store/playbackRateStore';
|
||||
import { useOrbitStore } from '@/store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { isOrbitPlaybackSyncActive } from '@/utils/orbit';
|
||||
import { isOrbitPlaybackSyncActive } from '@/features/orbit';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitStore } from '@/store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import {
|
||||
AUTODJ_OVERLAP_CAP_MAX_SEC,
|
||||
AUTODJ_OVERLAP_CAP_MIN_SEC,
|
||||
|
||||
@@ -9,8 +9,8 @@ import { playbackReportStopped } from '../../store/playbackReportSession';
|
||||
import { getPlaybackProgressSnapshot } from '../../store/playbackProgress';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { endOrbitSession, leaveOrbitSession } from '../../utils/orbit';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { endOrbitSession, leaveOrbitSession } from '@/features/orbit';
|
||||
import {
|
||||
canRunShortcutActionInMiniWindow,
|
||||
executeRuntimeAction,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { applyServerPlayQueue } from '../store/applyServerPlayQueue';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import {
|
||||
getPlaybackIdleSinceMs,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import type { ConnectionStatus } from '../hooks/useConnectionStatus';
|
||||
import { pullPlayQueueFromActiveServer } from '../store/applyServerPlayQueue';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { getPlaybackServerId, queueIsMultiServer } from '../utils/playback/playbackServer';
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
|
||||
import { getMusicFolders } from '../api/subsonicLibrary';
|
||||
import { probeEntityRatingSupport } from '../api/subsonicStarRating';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { cleanupOrphanedOrbitPlaylists } from '../utils/orbit';
|
||||
import { cleanupOrphanedOrbitPlaylists } from '@/features/orbit';
|
||||
|
||||
/**
|
||||
* Per-server one-shot probe run after login:
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { useAlbumDetailData } from '../hooks/useAlbumDetailData';
|
||||
import { useAlbumOfflineState } from '../hooks/useAlbumOfflineState';
|
||||
import { useAlbumDetailSort } from '../hooks/useAlbumDetailSort';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useAuthStore } from '../store/authStore';
|
||||
import { useLibraryIndexStore } from '../store/libraryIndexStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import {
|
||||
fetchRandomMixSongsUntilFull,
|
||||
getMixMinRatingsConfigFromAuth,
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
isInfiniteQueueFetching,
|
||||
setInfiniteQueueFetching,
|
||||
} from './infiniteQueueState';
|
||||
import { isInOrbitSession } from './orbitSession';
|
||||
import { isInOrbitSession } from '@/features/orbit';
|
||||
import type { PlayerState, QueueItemRef, Track } from './playerStoreTypes';
|
||||
import { toQueueItemRefs } from '../utils/library/queueItemRef';
|
||||
import { resolveQueueTrack } from '../utils/library/queueTrackView';
|
||||
|
||||
@@ -9,7 +9,7 @@ const { inOrbit, getSimilarSongs2, getTopSongs } = vi.hoisted(() => ({
|
||||
|
||||
vi.mock('../api/subsonicArtists', () => ({ getSimilarSongs2, getTopSongs }));
|
||||
vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn(() => Promise.resolve()) }));
|
||||
vi.mock('./orbitSession', () => ({ isInOrbitSession: () => inOrbit.value }));
|
||||
vi.mock('@/features/orbit', () => ({ isInOrbitSession: () => inOrbit.value }));
|
||||
vi.mock('./authStore', () => ({
|
||||
useAuthStore: { getState: () => ({ infiniteQueueEnabled: false }) },
|
||||
}));
|
||||
|
||||
@@ -2,7 +2,7 @@ import { playbackReportStart, playbackReportStopped } from './playbackReportSess
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getMusicNetworkRuntimeOrNull } from '../music-network';
|
||||
import { setDeferHotCachePrefetch } from '../utils/cache/hotCacheGate';
|
||||
import { orbitBulkGuard } from '../utils/orbitBulkGuard';
|
||||
import { orbitBulkGuard } from '@/features/orbit';
|
||||
import { sameQueueTrackId } from '../utils/playback/queueIdentity';
|
||||
import {
|
||||
computeAutodjManualBlendPlan,
|
||||
@@ -53,7 +53,7 @@ import {
|
||||
import { refreshLoudnessForTrack } from './loudnessRefresh';
|
||||
import { fetchWaveformBins, refreshWaveformForTrack } from '@/store/waveformRefresh';
|
||||
import { deriveNormalizationSnapshot } from './normalizationSnapshot';
|
||||
import { useOrbitStore } from './orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import {
|
||||
playbackSourceHintForResolvedUrl,
|
||||
recordEnginePlayUrl,
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
shouldRestartPlaybackForRateChange,
|
||||
type PlaybackRateSnapshot,
|
||||
} from '../utils/audio/playbackRateRestart';
|
||||
import { isOrbitPlaybackSyncActive } from '../utils/orbit';
|
||||
import { isOrbitPlaybackSyncActive } from '@/features/orbit';
|
||||
|
||||
interface PlaybackRateState extends PlaybackRateSnapshot {
|
||||
/** UI-only: smaller slider steps (Advanced). Not sent to the engine. */
|
||||
|
||||
@@ -19,7 +19,7 @@ vi.mock('./playbackRateStore', () => ({
|
||||
},
|
||||
}));
|
||||
vi.mock('../utils/audio/playbackRateHelpers', () => ({ isPlaybackRateApplied: () => false }));
|
||||
vi.mock('../utils/orbit', () => ({ isOrbitPlaybackSyncActive: () => false }));
|
||||
vi.mock('@/features/orbit', () => ({ isOrbitPlaybackSyncActive: () => false }));
|
||||
|
||||
import { reportNowPlaying, reportPlayback } from '../api/subsonicScrobble';
|
||||
import { isFeatureActiveForServer } from '../serverCapabilities/storeView';
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PlaybackReportState } from '../api/subsonicTypes';
|
||||
import { FEATURE_PLAYBACK_REPORT } from '../serverCapabilities/catalog';
|
||||
import { isFeatureActiveForServer } from '../serverCapabilities/storeView';
|
||||
import { isPlaybackRateApplied } from '../utils/audio/playbackRateHelpers';
|
||||
import { isOrbitPlaybackSyncActive } from '../utils/orbit';
|
||||
import { isOrbitPlaybackSyncActive } from '@/features/orbit';
|
||||
import { useAuthStore } from './authStore';
|
||||
import { getPlaybackProgressSnapshot } from './playbackProgress';
|
||||
import { usePlaybackRateStore } from './playbackRateStore';
|
||||
|
||||
@@ -44,7 +44,7 @@ vi.mock('@/music-network', () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/utils/orbitBulkGuard', () => ({
|
||||
vi.mock('@/features/orbit', () => ({
|
||||
orbitBulkGuard: vi.fn(async () => true),
|
||||
}));
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ vi.mock('@/music-network', () => ({
|
||||
getMusicNetworkRuntimeOrNull: () => runtimeMock,
|
||||
}));
|
||||
|
||||
vi.mock('@/utils/orbitBulkGuard', () => ({
|
||||
vi.mock('@/features/orbit', () => ({
|
||||
orbitBulkGuard: vi.fn(async () => true),
|
||||
}));
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ vi.mock('@/music-network', () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/utils/orbitBulkGuard', () => ({
|
||||
vi.mock('@/features/orbit', () => ({
|
||||
orbitBulkGuard: vi.fn(async () => true),
|
||||
}));
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ vi.mock('@/api/subsonic', async () => {
|
||||
|
||||
// `enqueue` / `enqueueAt` call `orbitBulkGuard` for multi-track inserts when
|
||||
// the caller hasn't pre-confirmed. Force the guard to short-circuit through.
|
||||
vi.mock('@/utils/orbitBulkGuard', () => ({
|
||||
vi.mock('@/features/orbit', () => ({
|
||||
orbitBulkGuard: vi.fn(async () => true),
|
||||
}));
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ vi.mock('@/api/subsonic', () => ({
|
||||
|
||||
import { usePreviewStore } from './previewStore';
|
||||
import { useAuthStore } from './authStore';
|
||||
import { useOrbitStore } from './orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { usePlayerStore } from './playerStore';
|
||||
import './previewPlayerVolumeSync';
|
||||
import { onInvoke, invokeMock } from '@/test/mocks/tauri';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { usePlayerStore } from './playerStore';
|
||||
import { useAuthStore } from './authStore';
|
||||
import { isOrbitPlaybackSyncActive } from '../utils/orbit';
|
||||
import { isOrbitPlaybackSyncActive } from '@/features/orbit';
|
||||
|
||||
/** Minimal track info needed to surface the preview in the player bar UI. */
|
||||
export interface PreviewingTrack {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { orbitBulkGuard } from '../utils/orbitBulkGuard';
|
||||
import { orbitBulkGuard } from '@/features/orbit';
|
||||
import { useAuthStore } from './authStore';
|
||||
import { setIsAudioPaused } from './engineState';
|
||||
import { prefetchLoudnessForEnqueuedTracks } from './loudnessPrefetch';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getSong } from '../api/subsonicLibrary';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { estimateLivePosition } from '../api/orbit';
|
||||
import { estimateLivePosition } from '@/features/orbit';
|
||||
import { setDeferHotCachePrefetch } from '../utils/cache/hotCacheGate';
|
||||
import {
|
||||
getPlaybackCacheServerKey,
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
isReplayGainActive,
|
||||
loudnessGainDbForEngineBind,
|
||||
} from './loudnessGainCache';
|
||||
import { useOrbitStore } from './orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import {
|
||||
playbackSourceHintForResolvedUrl,
|
||||
recordEnginePlayUrl,
|
||||
|
||||
@@ -25,7 +25,7 @@ import { _resetTimelineSessionHistoryForTest } from '@/store/timelineSessionHist
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePreviewStore } from '@/store/previewStore';
|
||||
import { useOrbitStore } from '@/store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
|
||||
const INITIAL_PLAYER_STATE = usePlayerStore.getState();
|
||||
const INITIAL_AUTH_STATE = useAuthStore.getState();
|
||||
|
||||
@@ -13,7 +13,7 @@ vi.mock('./fadeOut', () => ({
|
||||
}));
|
||||
|
||||
import { resolveAlbumForActiveServer } from '@/features/offline';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { playAlbum, playAlbumShuffled } from './playAlbum';
|
||||
import * as shuffleModule from './shuffleArray';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { resolveAlbumForActiveServer } from '@/features/offline';
|
||||
import { songToTrack } from './songToTrack';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { fadeOut } from './fadeOut';
|
||||
import { shouldAutodjInterruptBlend } from './autodjManualBlend';
|
||||
import type { Track } from '../../store/playerStoreTypes';
|
||||
|
||||
@@ -5,10 +5,10 @@ import {
|
||||
coverTrafficEndServerSwitch,
|
||||
} from '../../cover/coverTraffic';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { flushPlayQueueForServer } from '../../store/queueSync';
|
||||
import { markQueueHandoffPending } from '../../store/queueSyncUiState';
|
||||
import { endOrbitSession, leaveOrbitSession } from '../orbit';
|
||||
import { endOrbitSession, leaveOrbitSession } from '@/features/orbit';
|
||||
import { ensureConnectUrlResolved } from './serverEndpoint';
|
||||
import { syncServerHttpContextForProfile } from './syncServerHttpContext';
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ vi.mock('../playback/songToTrack', () => ({
|
||||
songToTrack: mocks.songToTrack,
|
||||
}));
|
||||
|
||||
vi.mock('../orbitBulkGuard', () => ({
|
||||
vi.mock('@/features/orbit', () => ({
|
||||
orbitBulkGuard: mocks.orbitBulkGuard,
|
||||
}));
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { ServerProfile } from '../../store/authStoreTypes';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { songToTrack } from '../playback/songToTrack';
|
||||
import type { Track } from '../../store/playerStoreTypes';
|
||||
import { orbitBulkGuard } from '../orbitBulkGuard';
|
||||
import { orbitBulkGuard } from '@/features/orbit';
|
||||
import { findServerIdForShareUrl } from './shareLink';
|
||||
import { connectBaseUrlForServer } from '../server/serverEndpoint';
|
||||
import { serverIndexKeyFromUrl } from '../server/serverIndexKey';
|
||||
|
||||
Reference in New Issue
Block a user