mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
refactor(deviceSync): co-locate device sync feature into features/deviceSync
This commit is contained in:
@@ -31,7 +31,7 @@ const Settings = lazy(() => import('../pages/Settings'));
|
||||
const Statistics = lazy(() => import('@/features/stats/pages/Statistics'));
|
||||
const Help = lazy(() => import('../pages/Help'));
|
||||
const WhatsNew = lazy(() => import('../pages/WhatsNew'));
|
||||
const DeviceSync = lazy(() => import('../pages/DeviceSync'));
|
||||
const DeviceSync = lazy(() => import('@/features/deviceSync/pages/DeviceSync'));
|
||||
const OfflineLibrary = lazy(() => import('../pages/OfflineLibrary'));
|
||||
const LabelAlbums = lazy(() => import('../pages/LabelAlbums'));
|
||||
const SearchBrowsePage = lazy(() => import('../pages/SearchBrowsePage'));
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createPortal } from 'react-dom';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { useOfflineJobStore } from '../store/offlineJobStore';
|
||||
import { clearOfflinePinTasks } from '../utils/offline/offlinePinQueue';
|
||||
import { useDeviceSyncJobStore } from '../store/deviceSyncJobStore';
|
||||
import { useDeviceSyncJobStore } from '@/features/deviceSync';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useSidebarStore } from '../store/sidebarStore';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
+4
-4
@@ -6,10 +6,10 @@ import {
|
||||
} from 'lucide-react';
|
||||
import type {
|
||||
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
|
||||
} from '../../api/subsonicTypes';
|
||||
import type { DeviceSyncSource } from '../../store/deviceSyncStore';
|
||||
import type { SourceTab } from '../../utils/deviceSync/deviceSyncHelpers';
|
||||
import BrowserRow from './BrowserRow';
|
||||
} from '@/api/subsonicTypes';
|
||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import BrowserRow from '@/features/deviceSync/components/BrowserRow';
|
||||
|
||||
interface Props {
|
||||
activeTab: SourceTab;
|
||||
+3
-3
@@ -5,9 +5,9 @@ import {
|
||||
AlertCircle, CheckCircle2, Clock, HardDriveUpload, Loader2,
|
||||
Trash2, Undo2,
|
||||
} from 'lucide-react';
|
||||
import { useDeviceSyncJobStore } from '../../store/deviceSyncJobStore';
|
||||
import type { DeviceSyncSource } from '../../store/deviceSyncStore';
|
||||
import type { SyncStatus } from '../../utils/deviceSync/deviceSyncHelpers';
|
||||
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
|
||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import type { SyncStatus } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
|
||||
interface Props {
|
||||
sources: DeviceSyncSource[];
|
||||
+3
-3
@@ -4,9 +4,9 @@ import {
|
||||
AlertCircle, FolderOpen, HardDriveUpload, RefreshCw, Usb,
|
||||
} from 'lucide-react';
|
||||
import CustomSelect from '@/ui/CustomSelect';
|
||||
import type { RemovableDrive } from '../../utils/deviceSync/deviceSyncHelpers';
|
||||
import { formatBytes } from '../../utils/deviceSync/deviceSyncHelpers';
|
||||
import type { DeviceSyncSource } from '../../store/deviceSyncStore';
|
||||
import type { RemovableDrive } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import { formatBytes } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
|
||||
interface Props {
|
||||
targetDir: string | null;
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { AlertCircle, CheckCircle2, Loader2 } from 'lucide-react';
|
||||
import type {
|
||||
MigrationPair, MigrationPhase, MigrationResult,
|
||||
} from '../../utils/deviceSync/runDeviceSyncMigration';
|
||||
} from '@/features/deviceSync/utils/runDeviceSyncMigration';
|
||||
|
||||
interface Props {
|
||||
migrationPhase: MigrationPhase;
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AlertCircle, Loader2 } from 'lucide-react';
|
||||
import type { SyncDelta } from '../../utils/deviceSync/runDeviceSyncExecution';
|
||||
import { formatMb } from '../../utils/format/formatBytes';
|
||||
import type { SyncDelta } from '@/features/deviceSync/utils/runDeviceSyncExecution';
|
||||
import { formatMb } from '@/utils/format/formatBytes';
|
||||
|
||||
interface Props {
|
||||
preSyncOpen: boolean;
|
||||
+6
-6
@@ -1,12 +1,12 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { getPlaylists } from '../api/subsonicPlaylists';
|
||||
import { getArtists, getArtist } from '../api/subsonicArtists';
|
||||
import { getAlbumList } from '../api/subsonicLibrary';
|
||||
import { search as searchSubsonic } from '../api/subsonicSearch';
|
||||
import { getPlaylists } from '@/api/subsonicPlaylists';
|
||||
import { getArtists, getArtist } from '@/api/subsonicArtists';
|
||||
import { getAlbumList } from '@/api/subsonicLibrary';
|
||||
import { search as searchSubsonic } from '@/api/subsonicSearch';
|
||||
import type {
|
||||
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
|
||||
} from '../api/subsonicTypes';
|
||||
import type { SourceTab } from '../utils/deviceSync/deviceSyncHelpers';
|
||||
} from '@/api/subsonicTypes';
|
||||
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
|
||||
export interface DeviceSyncBrowserResult {
|
||||
playlists: SubsonicPlaylist[];
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useDeviceSyncStore, type DeviceSyncSource } from '../store/deviceSyncStore';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import { useDeviceSyncStore, type DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
|
||||
export interface DeviceSyncDeviceScanResult {
|
||||
scanDevice: () => Promise<void>;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { RemovableDrive } from '../utils/deviceSync/deviceSyncHelpers';
|
||||
import type { RemovableDrive } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
|
||||
export interface DeviceSyncDrivesResult {
|
||||
drives: RemovableDrive[];
|
||||
+5
-5
@@ -2,11 +2,11 @@ import { useEffect } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useDeviceSyncJobStore } from '../store/deviceSyncJobStore';
|
||||
import { useDeviceSyncStore } from '../store/deviceSyncStore';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import { trackToSyncInfo } from '../utils/deviceSync/deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '../utils/playback/fetchTracksForSource';
|
||||
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
|
||||
import { useDeviceSyncStore } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { trackToSyncInfo } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||
|
||||
export function useDeviceSyncJobEvents(
|
||||
t: TFunction,
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { fetchTracksForSource } from '../utils/playback/fetchTracksForSource';
|
||||
import { trackToSyncInfo, type SyncStatus } from '../utils/deviceSync/deviceSyncHelpers';
|
||||
import type { DeviceSyncSource } from '../store/deviceSyncStore';
|
||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||
import { trackToSyncInfo, type SyncStatus } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
|
||||
export interface DeviceSyncSourceStatusesResult {
|
||||
sourcePathsMap: Map<string, string[]>;
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Device Sync feature — copies/transcodes library tracks onto external devices
|
||||
* (USB drives, players). The `DeviceSync` page is lazy-loaded by the router via
|
||||
* its deep path, so it is intentionally not re-exported here.
|
||||
*
|
||||
* Note: `audioListenerSetup/eqDeviceSync` is NOT part of this feature — it is the
|
||||
* audio-core EQ-per-output-device profile sync, kept in the audio core.
|
||||
*/
|
||||
export { useDeviceSyncJobStore } from './store/deviceSyncJobStore';
|
||||
export type { DeviceSyncSource } from './store/deviceSyncStore';
|
||||
@@ -1,33 +1,33 @@
|
||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
||||
import type { SubsonicSong } from '@/api/subsonicTypes';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDeviceSyncStore, DeviceSyncSource } from '../store/deviceSyncStore';
|
||||
import { useDeviceSyncJobStore } from '../store/deviceSyncJobStore';
|
||||
import { useDeviceSyncStore, DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
|
||||
|
||||
import {
|
||||
type SourceTab,
|
||||
} from '../utils/deviceSync/deviceSyncHelpers';
|
||||
import { useDeviceSyncDrives } from '../hooks/useDeviceSyncDrives';
|
||||
import { useDeviceSyncSourceStatuses } from '../hooks/useDeviceSyncSourceStatuses';
|
||||
import { useDeviceSyncBrowser } from '../hooks/useDeviceSyncBrowser';
|
||||
import { useDeviceSyncDeviceScan } from '../hooks/useDeviceSyncDeviceScan';
|
||||
import { useDeviceSyncJobEvents } from '../hooks/useDeviceSyncJobEvents';
|
||||
} from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import { useDeviceSyncDrives } from '@/features/deviceSync/hooks/useDeviceSyncDrives';
|
||||
import { useDeviceSyncSourceStatuses } from '@/features/deviceSync/hooks/useDeviceSyncSourceStatuses';
|
||||
import { useDeviceSyncBrowser } from '@/features/deviceSync/hooks/useDeviceSyncBrowser';
|
||||
import { useDeviceSyncDeviceScan } from '@/features/deviceSync/hooks/useDeviceSyncDeviceScan';
|
||||
import { useDeviceSyncJobEvents } from '@/features/deviceSync/hooks/useDeviceSyncJobEvents';
|
||||
import {
|
||||
runDeviceSyncMigrationPreview,
|
||||
runDeviceSyncMigrationExecute,
|
||||
type MigrationPhase, type MigrationPair, type MigrationResult,
|
||||
} from '../utils/deviceSync/runDeviceSyncMigration';
|
||||
} from '@/features/deviceSync/utils/runDeviceSyncMigration';
|
||||
import {
|
||||
runDeviceSyncSummaryPrompt,
|
||||
runDeviceSyncExecute,
|
||||
type SyncDelta,
|
||||
} from '../utils/deviceSync/runDeviceSyncExecution';
|
||||
import { runDeviceSyncChooseFolder } from '../utils/deviceSync/runDeviceSyncChooseFolder';
|
||||
import DeviceSyncHeader from '../components/deviceSync/DeviceSyncHeader';
|
||||
import DeviceSyncPreSyncModal from '../components/deviceSync/DeviceSyncPreSyncModal';
|
||||
import DeviceSyncMigrationModal from '../components/deviceSync/DeviceSyncMigrationModal';
|
||||
import DeviceSyncBrowserPanel from '../components/deviceSync/DeviceSyncBrowserPanel';
|
||||
import DeviceSyncDevicePanel from '../components/deviceSync/DeviceSyncDevicePanel';
|
||||
} from '@/features/deviceSync/utils/runDeviceSyncExecution';
|
||||
import { runDeviceSyncChooseFolder } from '@/features/deviceSync/utils/runDeviceSyncChooseFolder';
|
||||
import DeviceSyncHeader from '@/features/deviceSync/components/DeviceSyncHeader';
|
||||
import DeviceSyncPreSyncModal from '@/features/deviceSync/components/DeviceSyncPreSyncModal';
|
||||
import DeviceSyncMigrationModal from '@/features/deviceSync/components/DeviceSyncMigrationModal';
|
||||
import DeviceSyncBrowserPanel from '@/features/deviceSync/components/DeviceSyncBrowserPanel';
|
||||
import DeviceSyncDevicePanel from '@/features/deviceSync/components/DeviceSyncDevicePanel';
|
||||
|
||||
// ─── component ───────────────────────────────────────────────────────────────
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { SubsonicSong } from '../../api/subsonicTypes';
|
||||
import type { SubsonicSong } from '@/api/subsonicTypes';
|
||||
|
||||
export type SourceTab = 'playlists' | 'albums' | 'artists';
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { IS_WINDOWS } from '../platform';
|
||||
import { IS_WINDOWS } from '@/utils/platform';
|
||||
|
||||
// Same sanitize rules the Rust side uses (`sanitize_path_component`): strip
|
||||
// Windows-illegal chars and control chars, trim leading/trailing dots + spaces.
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { open as openDialog } from '@tauri-apps/plugin-dialog';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useDeviceSyncStore, type DeviceSyncSource } from '../../store/deviceSyncStore';
|
||||
import { showToast } from '../ui/toast';
|
||||
import { useDeviceSyncStore, type DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
|
||||
export interface RunDeviceSyncChooseFolderDeps {
|
||||
t: TFunction;
|
||||
+8
-8
@@ -1,12 +1,12 @@
|
||||
import type { TFunction } from 'i18next';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { buildDownloadUrl } from '../../api/subsonicStreamUrl';
|
||||
import type { SubsonicSong } from '../../api/subsonicTypes';
|
||||
import { useDeviceSyncStore, type DeviceSyncSource } from '../../store/deviceSyncStore';
|
||||
import { useDeviceSyncJobStore } from '../../store/deviceSyncJobStore';
|
||||
import { showToast } from '../ui/toast';
|
||||
import { trackToSyncInfo, uuid } from './deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '../playback/fetchTracksForSource';
|
||||
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
|
||||
import type { SubsonicSong } from '@/api/subsonicTypes';
|
||||
import { useDeviceSyncStore, type DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { trackToSyncInfo, uuid } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||
|
||||
export interface SyncDelta {
|
||||
addBytes: number;
|
||||
@@ -37,7 +37,7 @@ export async function runDeviceSyncSummaryPrompt(deps: RunDeviceSyncSummaryDeps)
|
||||
setPreSyncOpen(true);
|
||||
|
||||
try {
|
||||
const { getClient } = await import('../../api/subsonicClient');
|
||||
const { getClient } = await import('@/api/subsonicClient');
|
||||
const { baseUrl, params } = getClient();
|
||||
const payload = await invoke<SyncDelta>('calculate_sync_payload', {
|
||||
sources,
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
import type React from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { DeviceSyncSource } from '../../store/deviceSyncStore';
|
||||
import type { SubsonicSong } from '../../api/subsonicTypes';
|
||||
import { applyLegacyTemplate } from './deviceSyncLegacyTemplate';
|
||||
import { trackToSyncInfo } from './deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '../playback/fetchTracksForSource';
|
||||
import { IS_WINDOWS } from '../platform';
|
||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import type { SubsonicSong } from '@/api/subsonicTypes';
|
||||
import { applyLegacyTemplate } from '@/features/deviceSync/utils/deviceSyncLegacyTemplate';
|
||||
import { trackToSyncInfo } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||
import { IS_WINDOWS } from '@/utils/platform';
|
||||
|
||||
export type MigrationPhase = 'closed' | 'loading' | 'preview' | 'executing' | 'done' | 'nothing';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SubsonicSong } from '../../api/subsonicTypes';
|
||||
import type { DeviceSyncSource } from '../../store/deviceSyncStore';
|
||||
import type { DeviceSyncSource } from '@/features/deviceSync';
|
||||
import {
|
||||
resolveAlbum,
|
||||
resolveArtist,
|
||||
|
||||
Reference in New Issue
Block a user