From 321d5ff6ab21f528d644a4045e3b2cfdf4aef63e Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Tue, 30 Jun 2026 01:02:58 +0200 Subject: [PATCH] refactor(orbit): co-locate orbit feature into features/orbit --- src/app/AppShell.tsx | 14 +++-- src/components/ContextMenu.test.tsx | 2 +- src/components/ContextMenu.tsx | 2 +- src/components/HostApprovalQueue.tsx | 6 +-- src/components/PasteClipboardHandler.tsx | 4 +- src/components/QueuePanel.test.tsx | 2 +- src/components/QueuePanel.tsx | 5 +- src/components/SongCard.tsx | 2 +- src/components/SongRow.tsx | 2 +- .../artistDetail/ArtistDetailTopTracks.tsx | 2 +- .../contextMenu/SongContextItems.tsx | 2 +- .../playerBar/PlayerPlaybackRate.tsx | 4 +- src/components/playlist/PlaylistTracklist.tsx | 2 +- src/components/queuePanel/QueueToolbar.tsx | 2 +- .../components/FavoritesSongsTracklist.tsx | 2 +- src/{ => features/orbit}/api/orbit.ts | 0 .../orbit}/components/OrbitAccountPicker.tsx | 2 +- .../components/OrbitDiagnosticsPopover.tsx | 10 ++-- .../orbit}/components/OrbitExitModal.tsx | 4 +- .../orbit}/components/OrbitGuestQueue.tsx | 10 ++-- .../orbit}/components/OrbitHelpModal.tsx | 2 +- .../orbit}/components/OrbitJoinModal.tsx | 10 ++-- .../components/OrbitParticipantsPopover.tsx | 6 +-- .../orbit}/components/OrbitQueueHead.tsx | 4 +- .../orbit}/components/OrbitSessionBar.tsx | 28 +++++----- .../components/OrbitSettingsPopover.tsx | 8 +-- .../orbit}/components/OrbitSharePopover.tsx | 8 +-- .../orbit}/components/OrbitStartModal.tsx | 12 ++--- .../orbit}/components/OrbitStartTrigger.tsx | 12 ++--- .../orbit}/components/OrbitWordmark.tsx | 0 .../orbit}/hooks/useOrbitBodyAttrs.ts | 2 +- .../orbit}/hooks/useOrbitGuest.ts | 22 ++++---- .../orbit}/hooks/useOrbitHost.ts | 20 +++---- .../orbit}/hooks/useOrbitOutboxHeartbeat.ts | 4 +- .../orbit}/hooks/useOrbitSongRowBehavior.ts | 6 +-- .../orbit}/hooks/usePlaybackRateOrbitSync.ts | 4 +- src/features/orbit/index.ts | 53 +++++++++++++++++++ .../orbit}/store/orbitAccountPickerStore.ts | 2 +- .../orbit}/store/orbitSession.test.ts | 4 +- .../orbit}/store/orbitSession.ts | 2 +- src/{ => features/orbit}/store/orbitStore.ts | 2 +- .../orbit/utils}/cleanup.test.ts | 12 ++--- .../orbit => features/orbit/utils}/cleanup.ts | 10 ++-- .../orbit/utils}/constants.ts | 0 .../orbit => features/orbit/utils}/guest.ts | 20 +++---- .../orbit/utils}/helpers.test.ts | 4 +- .../orbit => features/orbit/utils}/helpers.ts | 2 +- .../orbit/utils}/host.test.ts | 18 +++---- .../orbit => features/orbit/utils}/host.ts | 20 +++---- .../orbit/utils}/moderation.ts | 8 +-- src/{ => features/orbit}/utils/orbit.ts | 35 ++++++------ .../orbit}/utils/orbitBulkGuard.ts | 6 +-- src/{ => features/orbit}/utils/orbitDiag.ts | 2 +- src/{ => features/orbit}/utils/orbitNames.ts | 0 .../orbit/utils}/pendingResend.test.ts | 2 +- .../orbit/utils}/pendingResend.ts | 0 .../orbit => features/orbit/utils}/remote.ts | 6 +-- .../orbit/utils}/sessionActive.ts | 4 +- .../orbit/utils}/shareLink.test.ts | 2 +- .../orbit/utils}/shareLink.ts | 2 +- .../orbit/utils}/stateMath.test.ts | 8 +-- .../orbit/utils}/stateMath.ts | 6 +-- .../orbit => features/orbit/utils}/sweep.ts | 8 +-- .../orbit/utils}/transitions.test.ts | 6 +-- .../orbit/utils}/transitions.ts | 6 +-- .../components/audio/PlaybackRateBlock.tsx | 4 +- .../audio/TrackTransitionsBlock.tsx | 2 +- .../tauriBridge/useMediaAndWindowBridge.ts | 4 +- src/hooks/useIdlePlayQueuePull.ts | 2 +- src/hooks/usePlayQueueSyncLedState.ts | 2 +- src/hooks/useServerCapabilitiesProbe.ts | 2 +- src/pages/AlbumDetail.tsx | 2 +- src/pages/RandomMix.tsx | 2 +- src/store/nextAction.ts | 2 +- src/store/nextActionOrbitRadio.test.ts | 2 +- src/store/playTrackAction.ts | 4 +- src/store/playbackRateStore.ts | 2 +- src/store/playbackReportSession.test.ts | 2 +- src/store/playbackReportSession.ts | 2 +- src/store/playerStore.events.test.ts | 2 +- src/store/playerStore.misc.test.ts | 2 +- src/store/playerStore.playbackActions.test.ts | 2 +- src/store/playerStore.queue.test.ts | 2 +- src/store/previewStore.test.ts | 2 +- src/store/previewStore.ts | 2 +- src/store/queueMutationActions.ts | 2 +- src/store/resumeAction.ts | 4 +- src/test/helpers/storeReset.ts | 2 +- src/utils/playback/playAlbum.test.ts | 2 +- src/utils/playback/playAlbum.ts | 2 +- src/utils/server/switchActiveServer.ts | 4 +- .../share/enqueueShareSearchPayload.test.ts | 2 +- src/utils/share/enqueueShareSearchPayload.ts | 2 +- 93 files changed, 295 insertions(+), 248 deletions(-) rename src/{ => features/orbit}/api/orbit.ts (100%) rename src/{ => features/orbit}/components/OrbitAccountPicker.tsx (97%) rename src/{ => features/orbit}/components/OrbitDiagnosticsPopover.tsx (96%) rename src/{ => features/orbit}/components/OrbitExitModal.tsx (96%) rename src/{ => features/orbit}/components/OrbitGuestQueue.tsx (96%) rename src/{ => features/orbit}/components/OrbitHelpModal.tsx (98%) rename src/{ => features/orbit}/components/OrbitJoinModal.tsx (94%) rename src/{ => features/orbit}/components/OrbitParticipantsPopover.tsx (97%) rename src/{ => features/orbit}/components/OrbitQueueHead.tsx (94%) rename src/{ => features/orbit}/components/OrbitSessionBar.tsx (93%) rename src/{ => features/orbit}/components/OrbitSettingsPopover.tsx (94%) rename src/{ => features/orbit}/components/OrbitSharePopover.tsx (92%) rename src/{ => features/orbit}/components/OrbitStartModal.tsx (95%) rename src/{ => features/orbit}/components/OrbitStartTrigger.tsx (91%) rename src/{ => features/orbit}/components/OrbitWordmark.tsx (100%) rename src/{ => features/orbit}/hooks/useOrbitBodyAttrs.ts (94%) rename src/{ => features/orbit}/hooks/useOrbitGuest.ts (97%) rename src/{ => features/orbit}/hooks/useOrbitHost.ts (95%) rename src/{ => features/orbit}/hooks/useOrbitOutboxHeartbeat.ts (90%) rename src/{ => features/orbit}/hooks/useOrbitSongRowBehavior.ts (94%) rename src/{ => features/orbit}/hooks/usePlaybackRateOrbitSync.ts (72%) create mode 100644 src/features/orbit/index.ts rename src/{ => features/orbit}/store/orbitAccountPickerStore.ts (94%) rename src/{ => features/orbit}/store/orbitSession.test.ts (91%) rename src/{ => features/orbit}/store/orbitSession.ts (91%) rename src/{ => features/orbit}/store/orbitStore.ts (98%) rename src/{utils/orbit => features/orbit/utils}/cleanup.test.ts (92%) rename src/{utils/orbit => features/orbit/utils}/cleanup.ts (89%) rename src/{utils/orbit => features/orbit/utils}/constants.ts (100%) rename src/{utils/orbit => features/orbit/utils}/guest.ts (93%) rename src/{utils/orbit => features/orbit/utils}/helpers.test.ts (97%) rename src/{utils/orbit => features/orbit/utils}/helpers.ts (99%) rename src/{utils/orbit => features/orbit/utils}/host.test.ts (79%) rename src/{utils/orbit => features/orbit/utils}/host.ts (92%) rename src/{utils/orbit => features/orbit/utils}/moderation.ts (94%) rename src/{ => features/orbit}/utils/orbit.ts (73%) rename src/{ => features/orbit}/utils/orbitBulkGuard.ts (85%) rename src/{ => features/orbit}/utils/orbitDiag.ts (98%) rename src/{ => features/orbit}/utils/orbitNames.ts (100%) rename src/{utils/orbit => features/orbit/utils}/pendingResend.test.ts (98%) rename src/{utils/orbit => features/orbit/utils}/pendingResend.ts (100%) rename src/{utils/orbit => features/orbit/utils}/remote.ts (93%) rename src/{utils/orbit => features/orbit/utils}/sessionActive.ts (76%) rename src/{utils/orbit => features/orbit/utils}/shareLink.test.ts (93%) rename src/{utils/orbit => features/orbit/utils}/shareLink.ts (97%) rename src/{utils/orbit => features/orbit/utils}/stateMath.test.ts (95%) rename src/{utils/orbit => features/orbit/utils}/stateMath.ts (98%) rename src/{utils/orbit => features/orbit/utils}/sweep.ts (89%) rename src/{utils/orbit => features/orbit/utils}/transitions.test.ts (94%) rename src/{utils/orbit => features/orbit/utils}/transitions.ts (95%) diff --git a/src/app/AppShell.tsx b/src/app/AppShell.tsx index 8e3d550b..57a67314 100644 --- a/src/app/AppShell.tsx +++ b/src/app/AppShell.tsx @@ -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, diff --git a/src/components/ContextMenu.test.tsx b/src/components/ContextMenu.test.tsx index 80465d93..40d0b6d6 100644 --- a/src/components/ContextMenu.test.tsx +++ b/src/components/ContextMenu.test.tsx @@ -30,7 +30,7 @@ vi.mock('@/api/subsonic', () => ({ })); -vi.mock('@/utils/orbitBulkGuard', () => ({ +vi.mock('@/features/orbit', () => ({ orbitBulkGuard: vi.fn(async () => true), })); diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx index 09022c1c..bbde22de 100644 --- a/src/components/ContextMenu.tsx +++ b/src/components/ContextMenu.tsx @@ -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'; diff --git a/src/components/HostApprovalQueue.tsx b/src/components/HostApprovalQueue.tsx index 34b7c445..d1c26efa 100644 --- a/src/components/HostApprovalQueue.tsx +++ b/src/components/HostApprovalQueue.tsx @@ -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; diff --git a/src/components/PasteClipboardHandler.tsx b/src/components/PasteClipboardHandler.tsx index b4c8fade..c722eb27 100644 --- a/src/components/PasteClipboardHandler.tsx +++ b/src/components/PasteClipboardHandler.tsx @@ -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 = { diff --git a/src/components/QueuePanel.test.tsx b/src/components/QueuePanel.test.tsx index b74eaef7..5e1e3ce3 100644 --- a/src/components/QueuePanel.test.tsx +++ b/src/components/QueuePanel.test.tsx @@ -26,7 +26,7 @@ vi.mock('@/api/subsonic', () => ({ })); -vi.mock('@/utils/orbitBulkGuard', () => ({ +vi.mock('@/features/orbit', () => ({ orbitBulkGuard: vi.fn(async () => true), })); diff --git a/src/components/QueuePanel.tsx b/src/components/QueuePanel.tsx index c617b02f..86dbd562 100644 --- a/src/components/QueuePanel.tsx +++ b/src/components/QueuePanel.tsx @@ -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'; diff --git a/src/components/SongCard.tsx b/src/components/SongCard.tsx index 3d9ede7c..f304308b 100644 --- a/src/components/SongCard.tsx +++ b/src/components/SongCard.tsx @@ -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'; diff --git a/src/components/SongRow.tsx b/src/components/SongRow.tsx index 2004105c..c7d86c5e 100644 --- a/src/components/SongRow.tsx +++ b/src/components/SongRow.tsx @@ -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'; diff --git a/src/components/artistDetail/ArtistDetailTopTracks.tsx b/src/components/artistDetail/ArtistDetailTopTracks.tsx index 27c0f87f..37255b7e 100644 --- a/src/components/artistDetail/ArtistDetailTopTracks.tsx +++ b/src/components/artistDetail/ArtistDetailTopTracks.tsx @@ -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'; diff --git a/src/components/contextMenu/SongContextItems.tsx b/src/components/contextMenu/SongContextItems.tsx index 55205409..f0b7d777 100644 --- a/src/components/contextMenu/SongContextItems.tsx +++ b/src/components/contextMenu/SongContextItems.tsx @@ -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'; diff --git a/src/components/playerBar/PlayerPlaybackRate.tsx b/src/components/playerBar/PlayerPlaybackRate.tsx index 0498f20d..6a0646e6 100644 --- a/src/components/playerBar/PlayerPlaybackRate.tsx +++ b/src/components/playerBar/PlayerPlaybackRate.tsx @@ -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; diff --git a/src/components/playlist/PlaylistTracklist.tsx b/src/components/playlist/PlaylistTracklist.tsx index 4a8ab78b..886056d1 100644 --- a/src/components/playlist/PlaylistTracklist.tsx +++ b/src/components/playlist/PlaylistTracklist.tsx @@ -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'; diff --git a/src/components/queuePanel/QueueToolbar.tsx b/src/components/queuePanel/QueueToolbar.tsx index 3a1460bb..5126d5b5 100644 --- a/src/components/queuePanel/QueueToolbar.tsx +++ b/src/components/queuePanel/QueueToolbar.tsx @@ -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[]; diff --git a/src/features/favorites/components/FavoritesSongsTracklist.tsx b/src/features/favorites/components/FavoritesSongsTracklist.tsx index e0e28361..11b4adc5 100644 --- a/src/features/favorites/components/FavoritesSongsTracklist.tsx +++ b/src/features/favorites/components/FavoritesSongsTracklist.tsx @@ -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'; diff --git a/src/api/orbit.ts b/src/features/orbit/api/orbit.ts similarity index 100% rename from src/api/orbit.ts rename to src/features/orbit/api/orbit.ts diff --git a/src/components/OrbitAccountPicker.tsx b/src/features/orbit/components/OrbitAccountPicker.tsx similarity index 97% rename from src/components/OrbitAccountPicker.tsx rename to src/features/orbit/components/OrbitAccountPicker.tsx index c68a96e0..207d89d9 100644 --- a/src/components/OrbitAccountPicker.tsx +++ b/src/features/orbit/components/OrbitAccountPicker.tsx @@ -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 diff --git a/src/components/OrbitDiagnosticsPopover.tsx b/src/features/orbit/components/OrbitDiagnosticsPopover.tsx similarity index 96% rename from src/components/OrbitDiagnosticsPopover.tsx rename to src/features/orbit/components/OrbitDiagnosticsPopover.tsx index 61d71b44..754546a2 100644 --- a/src/components/OrbitDiagnosticsPopover.tsx +++ b/src/features/orbit/components/OrbitDiagnosticsPopover.tsx @@ -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; diff --git a/src/components/OrbitExitModal.tsx b/src/features/orbit/components/OrbitExitModal.tsx similarity index 96% rename from src/components/OrbitExitModal.tsx rename to src/features/orbit/components/OrbitExitModal.tsx index de57c442..9ae6ccc9 100644 --- a/src/components/OrbitExitModal.tsx +++ b/src/features/orbit/components/OrbitExitModal.tsx @@ -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. diff --git a/src/components/OrbitGuestQueue.tsx b/src/features/orbit/components/OrbitGuestQueue.tsx similarity index 96% rename from src/components/OrbitGuestQueue.tsx rename to src/features/orbit/components/OrbitGuestQueue.tsx index 4a5893ca..421e25e5 100644 --- a/src/components/OrbitGuestQueue.tsx +++ b/src/features/orbit/components/OrbitGuestQueue.tsx @@ -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; diff --git a/src/components/OrbitHelpModal.tsx b/src/features/orbit/components/OrbitHelpModal.tsx similarity index 98% rename from src/components/OrbitHelpModal.tsx rename to src/features/orbit/components/OrbitHelpModal.tsx index 1970e44c..d662b114 100644 --- a/src/components/OrbitHelpModal.tsx +++ b/src/features/orbit/components/OrbitHelpModal.tsx @@ -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'; /** diff --git a/src/components/OrbitJoinModal.tsx b/src/features/orbit/components/OrbitJoinModal.tsx similarity index 94% rename from src/components/OrbitJoinModal.tsx rename to src/features/orbit/components/OrbitJoinModal.tsx index 58f3452a..746272d1 100644 --- a/src/components/OrbitJoinModal.tsx +++ b/src/features/orbit/components/OrbitJoinModal.tsx @@ -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; diff --git a/src/components/OrbitParticipantsPopover.tsx b/src/features/orbit/components/OrbitParticipantsPopover.tsx similarity index 97% rename from src/components/OrbitParticipantsPopover.tsx rename to src/features/orbit/components/OrbitParticipantsPopover.tsx index f7add041..0ad758e4 100644 --- a/src/components/OrbitParticipantsPopover.tsx +++ b/src/features/orbit/components/OrbitParticipantsPopover.tsx @@ -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. */ diff --git a/src/components/OrbitQueueHead.tsx b/src/features/orbit/components/OrbitQueueHead.tsx similarity index 94% rename from src/components/OrbitQueueHead.tsx rename to src/features/orbit/components/OrbitQueueHead.tsx index da8f6a85..2e8e442a 100644 --- a/src/components/OrbitQueueHead.tsx +++ b/src/features/orbit/components/OrbitQueueHead.tsx @@ -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; diff --git a/src/components/OrbitSessionBar.tsx b/src/features/orbit/components/OrbitSessionBar.tsx similarity index 93% rename from src/components/OrbitSessionBar.tsx rename to src/features/orbit/components/OrbitSessionBar.tsx index a0831e71..f36e2be8 100644 --- a/src/components/OrbitSessionBar.tsx +++ b/src/features/orbit/components/OrbitSessionBar.tsx @@ -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. diff --git a/src/components/OrbitSettingsPopover.tsx b/src/features/orbit/components/OrbitSettingsPopover.tsx similarity index 94% rename from src/components/OrbitSettingsPopover.tsx rename to src/features/orbit/components/OrbitSettingsPopover.tsx index be6981f3..749d5eff 100644 --- a/src/components/OrbitSettingsPopover.tsx +++ b/src/features/orbit/components/OrbitSettingsPopover.tsx @@ -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; diff --git a/src/components/OrbitSharePopover.tsx b/src/features/orbit/components/OrbitSharePopover.tsx similarity index 92% rename from src/components/OrbitSharePopover.tsx rename to src/features/orbit/components/OrbitSharePopover.tsx index d556314a..4bffc5a3 100644 --- a/src/components/OrbitSharePopover.tsx +++ b/src/features/orbit/components/OrbitSharePopover.tsx @@ -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; diff --git a/src/components/OrbitStartModal.tsx b/src/features/orbit/components/OrbitStartModal.tsx similarity index 95% rename from src/components/OrbitStartModal.tsx rename to src/features/orbit/components/OrbitStartModal.tsx index 9a4c1d04..5bcfd902 100644 --- a/src/components/OrbitStartModal.tsx +++ b/src/features/orbit/components/OrbitStartModal.tsx @@ -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; } diff --git a/src/components/OrbitStartTrigger.tsx b/src/features/orbit/components/OrbitStartTrigger.tsx similarity index 91% rename from src/components/OrbitStartTrigger.tsx rename to src/features/orbit/components/OrbitStartTrigger.tsx index bc56f2f7..878f9bfb 100644 --- a/src/components/OrbitStartTrigger.tsx +++ b/src/features/orbit/components/OrbitStartTrigger.tsx @@ -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: diff --git a/src/components/OrbitWordmark.tsx b/src/features/orbit/components/OrbitWordmark.tsx similarity index 100% rename from src/components/OrbitWordmark.tsx rename to src/features/orbit/components/OrbitWordmark.tsx diff --git a/src/hooks/useOrbitBodyAttrs.ts b/src/features/orbit/hooks/useOrbitBodyAttrs.ts similarity index 94% rename from src/hooks/useOrbitBodyAttrs.ts rename to src/features/orbit/hooks/useOrbitBodyAttrs.ts index af742262..7da2e962 100644 --- a/src/hooks/useOrbitBodyAttrs.ts +++ b/src/features/orbit/hooks/useOrbitBodyAttrs.ts @@ -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 `` and diff --git a/src/hooks/useOrbitGuest.ts b/src/features/orbit/hooks/useOrbitGuest.ts similarity index 97% rename from src/hooks/useOrbitGuest.ts rename to src/features/orbit/hooks/useOrbitGuest.ts index a596a88a..126f53b7 100644 --- a/src/hooks/useOrbitGuest.ts +++ b/src/features/orbit/hooks/useOrbitGuest.ts @@ -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. diff --git a/src/hooks/useOrbitHost.ts b/src/features/orbit/hooks/useOrbitHost.ts similarity index 95% rename from src/hooks/useOrbitHost.ts rename to src/features/orbit/hooks/useOrbitHost.ts index 7dad6380..d28cdaaa 100644 --- a/src/hooks/useOrbitHost.ts +++ b/src/features/orbit/hooks/useOrbitHost.ts @@ -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. diff --git a/src/hooks/useOrbitOutboxHeartbeat.ts b/src/features/orbit/hooks/useOrbitOutboxHeartbeat.ts similarity index 90% rename from src/hooks/useOrbitOutboxHeartbeat.ts rename to src/features/orbit/hooks/useOrbitOutboxHeartbeat.ts index 23d76d91..8337ddd0 100644 --- a/src/hooks/useOrbitOutboxHeartbeat.ts +++ b/src/features/orbit/hooks/useOrbitOutboxHeartbeat.ts @@ -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; diff --git a/src/hooks/useOrbitSongRowBehavior.ts b/src/features/orbit/hooks/useOrbitSongRowBehavior.ts similarity index 94% rename from src/hooks/useOrbitSongRowBehavior.ts rename to src/features/orbit/hooks/useOrbitSongRowBehavior.ts index e8279782..982f9755 100644 --- a/src/hooks/useOrbitSongRowBehavior.ts +++ b/src/features/orbit/hooks/useOrbitSongRowBehavior.ts @@ -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 diff --git a/src/hooks/usePlaybackRateOrbitSync.ts b/src/features/orbit/hooks/usePlaybackRateOrbitSync.ts similarity index 72% rename from src/hooks/usePlaybackRateOrbitSync.ts rename to src/features/orbit/hooks/usePlaybackRateOrbitSync.ts index 5beb2d6a..f1cfd5a6 100644 --- a/src/hooks/usePlaybackRateOrbitSync.ts +++ b/src/features/orbit/hooks/usePlaybackRateOrbitSync.ts @@ -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() { diff --git a/src/features/orbit/index.ts b/src/features/orbit/index.ts new file mode 100644 index 00000000..e21f736f --- /dev/null +++ b/src/features/orbit/index.ts @@ -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'; diff --git a/src/store/orbitAccountPickerStore.ts b/src/features/orbit/store/orbitAccountPickerStore.ts similarity index 94% rename from src/store/orbitAccountPickerStore.ts rename to src/features/orbit/store/orbitAccountPickerStore.ts index 9ce6a3ad..d9d22655 100644 --- a/src/store/orbitAccountPickerStore.ts +++ b/src/features/orbit/store/orbitAccountPickerStore.ts @@ -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; diff --git a/src/store/orbitSession.test.ts b/src/features/orbit/store/orbitSession.test.ts similarity index 91% rename from src/store/orbitSession.test.ts rename to src/features/orbit/store/orbitSession.test.ts index cc5ae624..24cbacb1 100644 --- a/src/store/orbitSession.test.ts +++ b/src/features/orbit/store/orbitSession.test.ts @@ -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; diff --git a/src/store/orbitSession.ts b/src/features/orbit/store/orbitSession.ts similarity index 91% rename from src/store/orbitSession.ts rename to src/features/orbit/store/orbitSession.ts index 8f8d2fea..6afced72 100644 --- a/src/store/orbitSession.ts +++ b/src/features/orbit/store/orbitSession.ts @@ -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 diff --git a/src/store/orbitStore.ts b/src/features/orbit/store/orbitStore.ts similarity index 98% rename from src/store/orbitStore.ts rename to src/features/orbit/store/orbitStore.ts index 7c0a09ac..3fafb0bc 100644 --- a/src/store/orbitStore.ts +++ b/src/features/orbit/store/orbitStore.ts @@ -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. diff --git a/src/utils/orbit/cleanup.test.ts b/src/features/orbit/utils/cleanup.test.ts similarity index 92% rename from src/utils/orbit/cleanup.test.ts rename to src/features/orbit/utils/cleanup.test.ts index c94f4b81..0f26d778 100644 --- a/src/utils/orbit/cleanup.test.ts +++ b/src/features/orbit/utils/cleanup.test.ts @@ -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; diff --git a/src/utils/orbit/cleanup.ts b/src/features/orbit/utils/cleanup.ts similarity index 89% rename from src/utils/orbit/cleanup.ts rename to src/features/orbit/utils/cleanup.ts index b4fd81bd..6cdb6f64 100644 --- a/src/utils/orbit/cleanup.ts +++ b/src/features/orbit/utils/cleanup.ts @@ -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 diff --git a/src/utils/orbit/constants.ts b/src/features/orbit/utils/constants.ts similarity index 100% rename from src/utils/orbit/constants.ts rename to src/features/orbit/utils/constants.ts diff --git a/src/utils/orbit/guest.ts b/src/features/orbit/utils/guest.ts similarity index 93% rename from src/utils/orbit/guest.ts rename to src/features/orbit/utils/guest.ts index 7e3f1143..0f5e41c7 100644 --- a/src/utils/orbit/guest.ts +++ b/src/features/orbit/utils/guest.ts @@ -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( diff --git a/src/utils/orbit/helpers.test.ts b/src/features/orbit/utils/helpers.test.ts similarity index 97% rename from src/utils/orbit/helpers.test.ts rename to src/features/orbit/utils/helpers.test.ts index 65787d92..8caa8d53 100644 --- a/src/utils/orbit/helpers.test.ts +++ b/src/features/orbit/utils/helpers.test.ts @@ -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' }); diff --git a/src/utils/orbit/helpers.ts b/src/features/orbit/utils/helpers.ts similarity index 99% rename from src/utils/orbit/helpers.ts rename to src/features/orbit/utils/helpers.ts index 9b6a52c3..0a78263c 100644 --- a/src/utils/orbit/helpers.ts +++ b/src/features/orbit/utils/helpers.ts @@ -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 { diff --git a/src/utils/orbit/host.test.ts b/src/features/orbit/utils/host.test.ts similarity index 79% rename from src/utils/orbit/host.test.ts rename to src/features/orbit/utils/host.test.ts index ee431f7c..54f46d65 100644 --- a/src/utils/orbit/host.test.ts +++ b/src/features/orbit/utils/host.test.ts @@ -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' }); diff --git a/src/utils/orbit/host.ts b/src/features/orbit/utils/host.ts similarity index 92% rename from src/utils/orbit/host.ts rename to src/features/orbit/utils/host.ts index e7a81a0f..4937967f 100644 --- a/src/utils/orbit/host.ts +++ b/src/features/orbit/utils/host.ts @@ -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. */ diff --git a/src/utils/orbit/moderation.ts b/src/features/orbit/utils/moderation.ts similarity index 94% rename from src/utils/orbit/moderation.ts rename to src/features/orbit/utils/moderation.ts index 319674d6..464706ce 100644 --- a/src/utils/orbit/moderation.ts +++ b/src/features/orbit/utils/moderation.ts @@ -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. diff --git a/src/utils/orbit.ts b/src/features/orbit/utils/orbit.ts similarity index 73% rename from src/utils/orbit.ts rename to src/features/orbit/utils/orbit.ts index 70b7f9ca..37e3745c 100644 --- a/src/utils/orbit.ts +++ b/src/features/orbit/utils/orbit.ts @@ -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'; diff --git a/src/utils/orbitBulkGuard.ts b/src/features/orbit/utils/orbitBulkGuard.ts similarity index 85% rename from src/utils/orbitBulkGuard.ts rename to src/features/orbit/utils/orbitBulkGuard.ts index 0881fe4c..fa5517e6 100644 --- a/src/utils/orbitBulkGuard.ts +++ b/src/features/orbit/utils/orbitBulkGuard.ts @@ -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. diff --git a/src/utils/orbitDiag.ts b/src/features/orbit/utils/orbitDiag.ts similarity index 98% rename from src/utils/orbitDiag.ts rename to src/features/orbit/utils/orbitDiag.ts index 18bbc9a3..e9cb8da3 100644 --- a/src/utils/orbitDiag.ts +++ b/src/features/orbit/utils/orbitDiag.ts @@ -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; diff --git a/src/utils/orbitNames.ts b/src/features/orbit/utils/orbitNames.ts similarity index 100% rename from src/utils/orbitNames.ts rename to src/features/orbit/utils/orbitNames.ts diff --git a/src/utils/orbit/pendingResend.test.ts b/src/features/orbit/utils/pendingResend.test.ts similarity index 98% rename from src/utils/orbit/pendingResend.test.ts rename to src/features/orbit/utils/pendingResend.test.ts index 16064a1d..c21893cd 100644 --- a/src/utils/orbit/pendingResend.test.ts +++ b/src/features/orbit/utils/pendingResend.test.ts @@ -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; diff --git a/src/utils/orbit/pendingResend.ts b/src/features/orbit/utils/pendingResend.ts similarity index 100% rename from src/utils/orbit/pendingResend.ts rename to src/features/orbit/utils/pendingResend.ts diff --git a/src/utils/orbit/remote.ts b/src/features/orbit/utils/remote.ts similarity index 93% rename from src/utils/orbit/remote.ts rename to src/features/orbit/utils/remote.ts index 90d37967..bcce728d 100644 --- a/src/utils/orbit/remote.ts +++ b/src/features/orbit/utils/remote.ts @@ -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 { diff --git a/src/utils/orbit/sessionActive.ts b/src/features/orbit/utils/sessionActive.ts similarity index 76% rename from src/utils/orbit/sessionActive.ts rename to src/features/orbit/utils/sessionActive.ts index f40fc7c6..c7b8b349 100644 --- a/src/utils/orbit/sessionActive.ts +++ b/src/features/orbit/utils/sessionActive.ts @@ -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 diff --git a/src/utils/orbit/shareLink.test.ts b/src/features/orbit/utils/shareLink.test.ts similarity index 93% rename from src/utils/orbit/shareLink.test.ts rename to src/features/orbit/utils/shareLink.test.ts index 80676ac9..0c6cafbb 100644 --- a/src/utils/orbit/shareLink.test.ts +++ b/src/features/orbit/utils/shareLink.test.ts @@ -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', () => { diff --git a/src/utils/orbit/shareLink.ts b/src/features/orbit/utils/shareLink.ts similarity index 97% rename from src/utils/orbit/shareLink.ts rename to src/features/orbit/utils/shareLink.ts index a0cbc5c1..4c847f67 100644 --- a/src/utils/orbit/shareLink.ts +++ b/src/features/orbit/utils/shareLink.ts @@ -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). */ diff --git a/src/utils/orbit/stateMath.test.ts b/src/features/orbit/utils/stateMath.test.ts similarity index 95% rename from src/utils/orbit/stateMath.test.ts rename to src/features/orbit/utils/stateMath.test.ts index 70dc2aea..7c7e24c8 100644 --- a/src/utils/orbit/stateMath.test.ts +++ b/src/features/orbit/utils/stateMath.test.ts @@ -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 }; diff --git a/src/utils/orbit/stateMath.ts b/src/features/orbit/utils/stateMath.ts similarity index 98% rename from src/utils/orbit/stateMath.ts rename to src/features/orbit/utils/stateMath.ts index 6cfed6de..e181df23 100644 --- a/src/utils/orbit/stateMath.ts +++ b/src/features/orbit/utils/stateMath.ts @@ -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`) diff --git a/src/utils/orbit/sweep.ts b/src/features/orbit/utils/sweep.ts similarity index 89% rename from src/utils/orbit/sweep.ts rename to src/features/orbit/utils/sweep.ts index e1355f84..ccbf599f 100644 --- a/src/utils/orbit/sweep.ts +++ b/src/features/orbit/utils/sweep.ts @@ -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. diff --git a/src/utils/orbit/transitions.test.ts b/src/features/orbit/utils/transitions.test.ts similarity index 94% rename from src/utils/orbit/transitions.test.ts rename to src/features/orbit/utils/transitions.test.ts index 3870a309..74efe492 100644 --- a/src/utils/orbit/transitions.test.ts +++ b/src/features/orbit/utils/transitions.test.ts @@ -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, diff --git a/src/utils/orbit/transitions.ts b/src/features/orbit/utils/transitions.ts similarity index 95% rename from src/utils/orbit/transitions.ts rename to src/features/orbit/utils/transitions.ts index 3dab092c..5edb30a0 100644 --- a/src/utils/orbit/transitions.ts +++ b/src/features/orbit/utils/transitions.ts @@ -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 diff --git a/src/features/settings/components/audio/PlaybackRateBlock.tsx b/src/features/settings/components/audio/PlaybackRateBlock.tsx index 8bf46fce..806b71ee 100644 --- a/src/features/settings/components/audio/PlaybackRateBlock.tsx +++ b/src/features/settings/components/audio/PlaybackRateBlock.tsx @@ -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'; diff --git a/src/features/settings/components/audio/TrackTransitionsBlock.tsx b/src/features/settings/components/audio/TrackTransitionsBlock.tsx index fce6731c..6387adba 100644 --- a/src/features/settings/components/audio/TrackTransitionsBlock.tsx +++ b/src/features/settings/components/audio/TrackTransitionsBlock.tsx @@ -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, diff --git a/src/hooks/tauriBridge/useMediaAndWindowBridge.ts b/src/hooks/tauriBridge/useMediaAndWindowBridge.ts index 6be04a76..dccbb4e1 100644 --- a/src/hooks/tauriBridge/useMediaAndWindowBridge.ts +++ b/src/hooks/tauriBridge/useMediaAndWindowBridge.ts @@ -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, diff --git a/src/hooks/useIdlePlayQueuePull.ts b/src/hooks/useIdlePlayQueuePull.ts index 221104f4..7add1499 100644 --- a/src/hooks/useIdlePlayQueuePull.ts +++ b/src/hooks/useIdlePlayQueuePull.ts @@ -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, diff --git a/src/hooks/usePlayQueueSyncLedState.ts b/src/hooks/usePlayQueueSyncLedState.ts index c9e94f0c..b58a39d2 100644 --- a/src/hooks/usePlayQueueSyncLedState.ts +++ b/src/hooks/usePlayQueueSyncLedState.ts @@ -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 { diff --git a/src/hooks/useServerCapabilitiesProbe.ts b/src/hooks/useServerCapabilitiesProbe.ts index e14b2f14..c662fbd5 100644 --- a/src/hooks/useServerCapabilitiesProbe.ts +++ b/src/hooks/useServerCapabilitiesProbe.ts @@ -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: diff --git a/src/pages/AlbumDetail.tsx b/src/pages/AlbumDetail.tsx index 979f0f86..7ea86709 100644 --- a/src/pages/AlbumDetail.tsx +++ b/src/pages/AlbumDetail.tsx @@ -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'; diff --git a/src/pages/RandomMix.tsx b/src/pages/RandomMix.tsx index 2cbcae44..b1eb01c6 100644 --- a/src/pages/RandomMix.tsx +++ b/src/pages/RandomMix.tsx @@ -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, diff --git a/src/store/nextAction.ts b/src/store/nextAction.ts index c4195b5d..7ce2c777 100644 --- a/src/store/nextAction.ts +++ b/src/store/nextAction.ts @@ -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'; diff --git a/src/store/nextActionOrbitRadio.test.ts b/src/store/nextActionOrbitRadio.test.ts index 4bdd173c..660e1503 100644 --- a/src/store/nextActionOrbitRadio.test.ts +++ b/src/store/nextActionOrbitRadio.test.ts @@ -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 }) }, })); diff --git a/src/store/playTrackAction.ts b/src/store/playTrackAction.ts index f7daccc0..486835a8 100644 --- a/src/store/playTrackAction.ts +++ b/src/store/playTrackAction.ts @@ -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, diff --git a/src/store/playbackRateStore.ts b/src/store/playbackRateStore.ts index c2712652..72e0e006 100644 --- a/src/store/playbackRateStore.ts +++ b/src/store/playbackRateStore.ts @@ -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. */ diff --git a/src/store/playbackReportSession.test.ts b/src/store/playbackReportSession.test.ts index ae176224..4eea5869 100644 --- a/src/store/playbackReportSession.test.ts +++ b/src/store/playbackReportSession.test.ts @@ -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'; diff --git a/src/store/playbackReportSession.ts b/src/store/playbackReportSession.ts index cceb6b79..139d962e 100644 --- a/src/store/playbackReportSession.ts +++ b/src/store/playbackReportSession.ts @@ -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'; diff --git a/src/store/playerStore.events.test.ts b/src/store/playerStore.events.test.ts index a1361f09..c0250762 100644 --- a/src/store/playerStore.events.test.ts +++ b/src/store/playerStore.events.test.ts @@ -44,7 +44,7 @@ vi.mock('@/music-network', () => { }; }); -vi.mock('@/utils/orbitBulkGuard', () => ({ +vi.mock('@/features/orbit', () => ({ orbitBulkGuard: vi.fn(async () => true), })); diff --git a/src/store/playerStore.misc.test.ts b/src/store/playerStore.misc.test.ts index 376ee933..6ada644c 100644 --- a/src/store/playerStore.misc.test.ts +++ b/src/store/playerStore.misc.test.ts @@ -42,7 +42,7 @@ vi.mock('@/music-network', () => ({ getMusicNetworkRuntimeOrNull: () => runtimeMock, })); -vi.mock('@/utils/orbitBulkGuard', () => ({ +vi.mock('@/features/orbit', () => ({ orbitBulkGuard: vi.fn(async () => true), })); diff --git a/src/store/playerStore.playbackActions.test.ts b/src/store/playerStore.playbackActions.test.ts index 6fdcdf28..315be45a 100644 --- a/src/store/playerStore.playbackActions.test.ts +++ b/src/store/playerStore.playbackActions.test.ts @@ -47,7 +47,7 @@ vi.mock('@/music-network', () => { }; }); -vi.mock('@/utils/orbitBulkGuard', () => ({ +vi.mock('@/features/orbit', () => ({ orbitBulkGuard: vi.fn(async () => true), })); diff --git a/src/store/playerStore.queue.test.ts b/src/store/playerStore.queue.test.ts index 98916c36..7c12c579 100644 --- a/src/store/playerStore.queue.test.ts +++ b/src/store/playerStore.queue.test.ts @@ -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), })); diff --git a/src/store/previewStore.test.ts b/src/store/previewStore.test.ts index c220f3ad..4a552dd8 100644 --- a/src/store/previewStore.test.ts +++ b/src/store/previewStore.test.ts @@ -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'; diff --git a/src/store/previewStore.ts b/src/store/previewStore.ts index 339e8259..96e8b472 100644 --- a/src/store/previewStore.ts +++ b/src/store/previewStore.ts @@ -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 { diff --git a/src/store/queueMutationActions.ts b/src/store/queueMutationActions.ts index 9c566047..7c2be7d6 100644 --- a/src/store/queueMutationActions.ts +++ b/src/store/queueMutationActions.ts @@ -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'; diff --git a/src/store/resumeAction.ts b/src/store/resumeAction.ts index 06e61b94..509c8561 100644 --- a/src/store/resumeAction.ts +++ b/src/store/resumeAction.ts @@ -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, diff --git a/src/test/helpers/storeReset.ts b/src/test/helpers/storeReset.ts index beeeed1e..c3dfc408 100644 --- a/src/test/helpers/storeReset.ts +++ b/src/test/helpers/storeReset.ts @@ -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(); diff --git a/src/utils/playback/playAlbum.test.ts b/src/utils/playback/playAlbum.test.ts index df9ab0c8..da4111bc 100644 --- a/src/utils/playback/playAlbum.test.ts +++ b/src/utils/playback/playAlbum.test.ts @@ -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'; diff --git a/src/utils/playback/playAlbum.ts b/src/utils/playback/playAlbum.ts index 12343f56..fd2a8c4c 100644 --- a/src/utils/playback/playAlbum.ts +++ b/src/utils/playback/playAlbum.ts @@ -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'; diff --git a/src/utils/server/switchActiveServer.ts b/src/utils/server/switchActiveServer.ts index 712fbaf9..1a404c0b 100644 --- a/src/utils/server/switchActiveServer.ts +++ b/src/utils/server/switchActiveServer.ts @@ -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'; diff --git a/src/utils/share/enqueueShareSearchPayload.test.ts b/src/utils/share/enqueueShareSearchPayload.test.ts index 5e37ab1f..f4e917d3 100644 --- a/src/utils/share/enqueueShareSearchPayload.test.ts +++ b/src/utils/share/enqueueShareSearchPayload.test.ts @@ -53,7 +53,7 @@ vi.mock('../playback/songToTrack', () => ({ songToTrack: mocks.songToTrack, })); -vi.mock('../orbitBulkGuard', () => ({ +vi.mock('@/features/orbit', () => ({ orbitBulkGuard: mocks.orbitBulkGuard, })); diff --git a/src/utils/share/enqueueShareSearchPayload.ts b/src/utils/share/enqueueShareSearchPayload.ts index 92a8eeba..bc2ced0c 100644 --- a/src/utils/share/enqueueShareSearchPayload.ts +++ b/src/utils/share/enqueueShareSearchPayload.ts @@ -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';