mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
add more dynamic imports to optimize bundle
This commit is contained in:
+10
-3
@@ -7,12 +7,11 @@ import '@mantine/core/styles.css';
|
||||
import '@mantine/dates/styles.css';
|
||||
import '@mantine/notifications/styles.css';
|
||||
import isElectron from 'is-electron';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { lazy, Suspense, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-context';
|
||||
import { useSyncSettingsToMain } from '/@/renderer/hooks/use-sync-settings-to-main';
|
||||
import { ReleaseNotesModal } from './release-notes-modal';
|
||||
import { AppRouter } from '/@/renderer/router/app-router';
|
||||
import { useCssSettings, useHotkeySettings, useLanguage } from '/@/renderer/store';
|
||||
import { useAppTheme } from '/@/renderer/themes/use-app-theme';
|
||||
@@ -22,6 +21,12 @@ import '/@/shared/styles/global.css';
|
||||
import { PlayerProvider } from '/@/renderer/features/player/context/player-context';
|
||||
import { AudioPlayers } from '/@/renderer/features/player/components/audio-players';
|
||||
|
||||
const ReleaseNotesModal = lazy(() =>
|
||||
import('./release-notes-modal').then((module) => ({
|
||||
default: module.ReleaseNotesModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const ipc = isElectron() ? window.api.ipc : null;
|
||||
|
||||
export const App = () => {
|
||||
@@ -95,7 +100,9 @@ export const App = () => {
|
||||
<AppRouter />
|
||||
</PlayerProvider>
|
||||
</WebAudioContext.Provider>
|
||||
<ReleaseNotesModal />
|
||||
<Suspense fallback={null}>
|
||||
<ReleaseNotesModal />
|
||||
</Suspense>
|
||||
</MantineProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { openUpdatePlaylistModal } from '/@/renderer/features/playlists/components/update-playlist-form';
|
||||
import { openUpdatePlaylistModal } from '/@/renderer/features/playlists/components/update-playlist-modal';
|
||||
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
|
||||
import { Playlist } from '/@/shared/types/domain-types';
|
||||
|
||||
|
||||
@@ -1,18 +1,63 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { lazy, Suspense, useEffect, useRef } from 'react';
|
||||
import { createCallable } from 'react-call';
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
import { AlbumArtistContextMenu } from '/@/renderer/features/context-menu/menus/album-artist-context-menu';
|
||||
import { AlbumContextMenu } from '/@/renderer/features/context-menu/menus/album-context-menu';
|
||||
import { ArtistContextMenu } from '/@/renderer/features/context-menu/menus/artist-context-menu';
|
||||
import { FolderContextMenu } from '/@/renderer/features/context-menu/menus/folder-context-menu';
|
||||
import { GenreContextMenu } from '/@/renderer/features/context-menu/menus/genre-context-menu';
|
||||
import { PlaylistContextMenu } from '/@/renderer/features/context-menu/menus/playlist-context-menu';
|
||||
import { PlaylistSongContextMenu } from '/@/renderer/features/context-menu/menus/playlist-song-context-menu';
|
||||
import { QueueContextMenu } from '/@/renderer/features/context-menu/menus/queue-context-menu';
|
||||
import { SongContextMenu } from '/@/renderer/features/context-menu/menus/song-context-menu';
|
||||
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
|
||||
|
||||
const AlbumArtistContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/album-artist-context-menu').then((module) => ({
|
||||
default: module.AlbumArtistContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const AlbumContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/album-context-menu').then((module) => ({
|
||||
default: module.AlbumContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const ArtistContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/artist-context-menu').then((module) => ({
|
||||
default: module.ArtistContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const FolderContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/folder-context-menu').then((module) => ({
|
||||
default: module.FolderContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const GenreContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/genre-context-menu').then((module) => ({
|
||||
default: module.GenreContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const PlaylistContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/playlist-context-menu').then((module) => ({
|
||||
default: module.PlaylistContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const PlaylistSongContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/playlist-song-context-menu').then((module) => ({
|
||||
default: module.PlaylistSongContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const QueueContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/queue-context-menu').then((module) => ({
|
||||
default: module.QueueContextMenu,
|
||||
})),
|
||||
);
|
||||
|
||||
const SongContextMenu = lazy(() =>
|
||||
import('/@/renderer/features/context-menu/menus/song-context-menu').then((module) => ({
|
||||
default: module.SongContextMenu,
|
||||
})),
|
||||
);
|
||||
import {
|
||||
Album,
|
||||
AlbumArtist,
|
||||
@@ -80,15 +125,17 @@ export const ContextMenuController = createCallable<ContextMenuControllerProps,
|
||||
}}
|
||||
/>
|
||||
</ContextMenu.Target>
|
||||
{cmd.type === LibraryItem.QUEUE_SONG && <QueueContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ALBUM && <AlbumContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ALBUM_ARTIST && <AlbumArtistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ARTIST && <ArtistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.FOLDER && <FolderContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.GENRE && <GenreContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.PLAYLIST && <PlaylistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.PLAYLIST_SONG && <PlaylistSongContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.SONG && <SongContextMenu {...cmd} />}
|
||||
<Suspense fallback={null}>
|
||||
{cmd.type === LibraryItem.QUEUE_SONG && <QueueContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ALBUM && <AlbumContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ALBUM_ARTIST && <AlbumArtistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ARTIST && <ArtistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.FOLDER && <FolderContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.GENRE && <GenreContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.PLAYLIST && <PlaylistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.PLAYLIST_SONG && <PlaylistSongContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.SONG && <SongContextMenu {...cmd} />}
|
||||
</Suspense>
|
||||
</ContextMenu>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@ import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/
|
||||
import { getSongUrl } from '/@/renderer/features/player/audio-player/hooks/use-stream-url';
|
||||
import { AudioPlayer, PlayerOnProgressProps } from '/@/renderer/features/player/audio-player/types';
|
||||
import { useRadioStore } from '/@/renderer/features/radio/hooks/use-radio-player';
|
||||
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
|
||||
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-properties';
|
||||
import {
|
||||
usePlaybackSettings,
|
||||
usePlayerActions,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { RefObject } from 'react';
|
||||
import type ReactPlayer from 'react-player';
|
||||
|
||||
import { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import ReactPlayer from 'react-player';
|
||||
|
||||
import { AudioPlayer, PlayerOnProgressProps } from '/@/renderer/features/player/audio-player/types';
|
||||
import { convertToLogVolume } from '/@/renderer/features/player/audio-player/utils/player-utils';
|
||||
@@ -69,6 +69,31 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
|
||||
|
||||
const player1Ref = useRef<null | ReactPlayer>(null);
|
||||
const player2Ref = useRef<null | ReactPlayer>(null);
|
||||
const [ReactPlayerComponent, setReactPlayerComponent] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const loadReactPlayer = async () => {
|
||||
try {
|
||||
const module = await import('react-player');
|
||||
if (isMounted) {
|
||||
setReactPlayerComponent(() => module.default);
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load react-player:', error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadReactPlayer();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [internalVolume1, setInternalVolume1] = useState(volume / 100 || 0);
|
||||
const [internalVolume2, setInternalVolume2] = useState(volume / 100 || 0);
|
||||
@@ -184,9 +209,13 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
|
||||
[onStartedPlayer2, preservesPitch],
|
||||
);
|
||||
|
||||
if (isLoading || !ReactPlayerComponent) {
|
||||
return <div id="web-player-engine" style={{ display: 'none' }} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="web-player-engine" style={{ display: 'none' }}>
|
||||
<ReactPlayer
|
||||
<ReactPlayerComponent
|
||||
config={{
|
||||
file: { attributes: { crossOrigin: 'anonymous' }, forceAudio: true },
|
||||
}}
|
||||
@@ -206,7 +235,7 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
|
||||
volume={volume1}
|
||||
width={0}
|
||||
/>
|
||||
<ReactPlayer
|
||||
<ReactPlayerComponent
|
||||
config={{
|
||||
file: { attributes: { crossOrigin: 'anonymous' }, forceAudio: true },
|
||||
}}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import formatDuration from 'format-duration';
|
||||
import { memo } from 'react';
|
||||
import { lazy, memo, Suspense } from 'react';
|
||||
|
||||
import styles from './mobile-fullscreen-player.module.css';
|
||||
|
||||
import { PlayerbarSeekSlider } from '/@/renderer/features/player/components/playerbar-seek-slider';
|
||||
import { PlayerbarWaveform } from '/@/renderer/features/player/components/playerbar-waveform';
|
||||
import { usePlayerTimestamp } from '/@/renderer/store';
|
||||
import { PlayerbarSliderType, usePlayerbarSlider } from '/@/renderer/store/settings.store';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { PlaybackSelectors } from '/@/shared/constants/playback-selectors';
|
||||
import { QueueSong } from '/@/shared/types/domain-types';
|
||||
|
||||
const PlayerbarWaveform = lazy(() =>
|
||||
import('/@/renderer/features/player/components/playerbar-waveform').then((module) => ({
|
||||
default: module.PlayerbarWaveform,
|
||||
})),
|
||||
);
|
||||
|
||||
interface MobileFullscreenPlayerProgressProps {
|
||||
currentSong?: QueueSong;
|
||||
}
|
||||
@@ -38,7 +44,9 @@ export const MobileFullscreenPlayerProgress = memo(
|
||||
</div>
|
||||
<div className={styles.sliderWrapper}>
|
||||
{isWaveform ? (
|
||||
<PlayerbarWaveform />
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<PlayerbarWaveform />
|
||||
</Suspense>
|
||||
) : (
|
||||
<PlayerbarSeekSlider max={songDuration} min={0} />
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import formatDuration from 'format-duration';
|
||||
import { lazy, Suspense } from 'react';
|
||||
|
||||
import { PlayerbarSeekSlider } from './playerbar-seek-slider';
|
||||
import styles from './playerbar-slider.module.css';
|
||||
import { PlayerbarWaveform } from './playerbar-waveform';
|
||||
|
||||
import { useRemote } from '/@/renderer/features/remote/hooks/use-remote';
|
||||
import {
|
||||
@@ -13,9 +13,16 @@ import {
|
||||
} from '/@/renderer/store';
|
||||
import { PlayerbarSliderType, usePlayerbarSlider } from '/@/renderer/store/settings.store';
|
||||
import { Slider, SliderProps } from '/@/shared/components/slider/slider';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { PlaybackSelectors } from '/@/shared/constants/playback-selectors';
|
||||
|
||||
const PlayerbarWaveform = lazy(() =>
|
||||
import('./playerbar-waveform').then((module) => ({
|
||||
default: module.PlayerbarWaveform,
|
||||
})),
|
||||
);
|
||||
|
||||
export const PlayerbarSlider = () => {
|
||||
const currentSong = usePlayerSong();
|
||||
const playerbarSlider = usePlayerbarSlider();
|
||||
@@ -51,7 +58,9 @@ export const PlayerbarSlider = () => {
|
||||
</div>
|
||||
<div className={styles.sliderWrapper}>
|
||||
{isWaveform ? (
|
||||
<PlayerbarWaveform />
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<PlayerbarWaveform />
|
||||
</Suspense>
|
||||
) : (
|
||||
<PlayerbarSeekSlider max={songDuration} min={0} />
|
||||
)}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import clsx from 'clsx';
|
||||
import { MouseEvent } from 'react';
|
||||
import { lazy, MouseEvent, Suspense } from 'react';
|
||||
|
||||
import styles from './playerbar.module.css';
|
||||
|
||||
import { CenterControls } from '/@/renderer/features/player/components/center-controls';
|
||||
import { LeftControls } from '/@/renderer/features/player/components/left-controls';
|
||||
import { MobilePlayerbar } from '/@/renderer/features/player/components/mobile-playerbar';
|
||||
import { RightControls } from '/@/renderer/features/player/components/right-controls';
|
||||
import { useIsMobile } from '/@/renderer/hooks/use-is-mobile';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
|
||||
const MobilePlayerbar = lazy(() =>
|
||||
import('./mobile-playerbar').then((module) => ({
|
||||
default: module.MobilePlayerbar,
|
||||
})),
|
||||
);
|
||||
import { useFullScreenPlayerStore, useSetFullScreenPlayerStore } from '/@/renderer/store';
|
||||
import { usePlayerbarOpenDrawer } from '/@/renderer/store';
|
||||
import { PlaybackSelectors } from '/@/shared/constants/playback-selectors';
|
||||
@@ -24,7 +30,11 @@ export const Playerbar = () => {
|
||||
};
|
||||
|
||||
if (isMobile) {
|
||||
return <MobilePlayerbar />;
|
||||
return (
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<MobilePlayerbar />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,8 +2,6 @@ import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { lazy, Suspense, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
import { PlaylistDetailSongListEditTable } from './playlist-detail-song-list-table';
|
||||
|
||||
import { ItemListHandle } from '/@/renderer/components/item-list/types';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||
@@ -21,6 +19,14 @@ const PlaylistDetailSongListTable = lazy(() =>
|
||||
),
|
||||
);
|
||||
|
||||
const PlaylistDetailSongListEditTable = lazy(() =>
|
||||
import('/@/renderer/features/playlists/components/playlist-detail-song-list-table').then(
|
||||
(module) => ({
|
||||
default: module.PlaylistDetailSongListEditTable,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const PlaylistDetailSongListGrid = lazy(() =>
|
||||
import('/@/renderer/features/playlists/components/playlist-detail-song-list-grid').then(
|
||||
(module) => ({
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { closeModal, ContextModalProps, openContextModal } from '@mantine/modals';
|
||||
import { closeModal, ContextModalProps } from '@mantine/modals';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { useUpdatePlaylist } from '/@/renderer/features/playlists/mutations/update-playlist-mutation';
|
||||
import { sharedQueries } from '/@/renderer/features/shared/api/shared-api';
|
||||
import { useCurrentServer, useCurrentServerId, usePermissions } from '/@/renderer/store';
|
||||
@@ -17,7 +16,6 @@ import { TextInput } from '/@/shared/components/text-input/text-input';
|
||||
import { toast } from '/@/shared/components/toast/toast';
|
||||
import { useForm } from '/@/shared/hooks/use-form';
|
||||
import {
|
||||
Playlist,
|
||||
ServerType,
|
||||
SortOrder,
|
||||
UpdatePlaylistBody,
|
||||
@@ -167,24 +165,3 @@ const OwnerSelect = ({ form }: { form: ReturnType<typeof useForm<UpdatePlaylistB
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const openUpdatePlaylistModal = async (args: { playlist: Playlist }) => {
|
||||
const { playlist } = args;
|
||||
|
||||
openContextModal({
|
||||
innerProps: {
|
||||
body: {
|
||||
comment: playlist?.description || undefined,
|
||||
genres: playlist?.genres,
|
||||
name: playlist?.name,
|
||||
ownerId: playlist?.ownerId || undefined,
|
||||
public: playlist?.public || false,
|
||||
queryBuilderRules: playlist?.rules || undefined,
|
||||
sync: playlist?.sync || undefined,
|
||||
},
|
||||
query: { id: playlist?.id },
|
||||
},
|
||||
modalKey: 'updatePlaylist',
|
||||
title: i18n.t('form.editPlaylist.title', { postProcess: 'titleCase' }) as string,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { openContextModal } from '@mantine/modals';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { Playlist } from '/@/shared/types/domain-types';
|
||||
|
||||
export const openUpdatePlaylistModal = async (args: { playlist: Playlist }) => {
|
||||
const { playlist } = args;
|
||||
|
||||
openContextModal({
|
||||
innerProps: {
|
||||
body: {
|
||||
comment: playlist?.description || undefined,
|
||||
genres: playlist?.genres,
|
||||
name: playlist?.name,
|
||||
ownerId: playlist?.ownerId || undefined,
|
||||
public: playlist?.public || false,
|
||||
queryBuilderRules: playlist?.rules || undefined,
|
||||
sync: playlist?.sync || undefined,
|
||||
},
|
||||
query: { id: playlist?.id },
|
||||
},
|
||||
modalKey: 'updatePlaylist',
|
||||
title: i18n.t('form.editPlaylist.title', { postProcess: 'titleCase' }) as string,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import type { SettingsState } from '/@/renderer/store/settings.store';
|
||||
|
||||
export const getMpvSetting = (
|
||||
key: keyof SettingsState['playback']['mpvProperties'],
|
||||
value: any,
|
||||
) => {
|
||||
switch (key) {
|
||||
case 'audioExclusiveMode':
|
||||
return { 'audio-exclusive': value || 'no' };
|
||||
case 'audioSampleRateHz':
|
||||
return { 'audio-samplerate': value };
|
||||
case 'gaplessAudio':
|
||||
return { 'gapless-audio': value || 'weak' };
|
||||
case 'replayGainClip':
|
||||
return { 'replaygain-clip': value || 'no' };
|
||||
case 'replayGainFallbackDB':
|
||||
return { 'replaygain-fallback': value };
|
||||
case 'replayGainMode':
|
||||
return { replaygain: value || 'no' };
|
||||
case 'replayGainPreampDB':
|
||||
return { 'replaygain-preamp': value || 0 };
|
||||
default:
|
||||
return { 'audio-format': value };
|
||||
}
|
||||
};
|
||||
|
||||
export const getMpvProperties = (settings: SettingsState['playback']['mpvProperties']) => {
|
||||
const properties: Record<string, any> = {
|
||||
'audio-exclusive': settings.audioExclusiveMode || 'no',
|
||||
'audio-samplerate':
|
||||
settings.audioSampleRateHz === 0 ? undefined : settings.audioSampleRateHz,
|
||||
'gapless-audio': settings.gaplessAudio || 'weak',
|
||||
replaygain: settings.replayGainMode || 'no',
|
||||
'replaygain-clip': settings.replayGainClip || 'no',
|
||||
'replaygain-fallback': settings.replayGainFallbackDB,
|
||||
'replaygain-preamp': settings.replayGainPreampDB || 0,
|
||||
};
|
||||
|
||||
Object.keys(properties).forEach((key) =>
|
||||
properties[key] === undefined ? delete properties[key] : {},
|
||||
);
|
||||
|
||||
return properties;
|
||||
};
|
||||
@@ -2,6 +2,8 @@ import isElectron from 'is-electron';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { getMpvSetting } from './mpv-properties';
|
||||
|
||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||
import {
|
||||
@@ -27,49 +29,6 @@ import { PlayerType } from '/@/shared/types/types';
|
||||
const localSettings = isElectron() ? window.api.localSettings : null;
|
||||
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
|
||||
|
||||
export const getMpvSetting = (
|
||||
key: keyof SettingsState['playback']['mpvProperties'],
|
||||
value: any,
|
||||
) => {
|
||||
switch (key) {
|
||||
case 'audioExclusiveMode':
|
||||
return { 'audio-exclusive': value || 'no' };
|
||||
case 'audioSampleRateHz':
|
||||
return { 'audio-samplerate': value };
|
||||
case 'gaplessAudio':
|
||||
return { 'gapless-audio': value || 'weak' };
|
||||
case 'replayGainClip':
|
||||
return { 'replaygain-clip': value || 'no' };
|
||||
case 'replayGainFallbackDB':
|
||||
return { 'replaygain-fallback': value };
|
||||
case 'replayGainMode':
|
||||
return { replaygain: value || 'no' };
|
||||
case 'replayGainPreampDB':
|
||||
return { 'replaygain-preamp': value || 0 };
|
||||
default:
|
||||
return { 'audio-format': value };
|
||||
}
|
||||
};
|
||||
|
||||
export const getMpvProperties = (settings: SettingsState['playback']['mpvProperties']) => {
|
||||
const properties: Record<string, any> = {
|
||||
'audio-exclusive': settings.audioExclusiveMode || 'no',
|
||||
'audio-samplerate':
|
||||
settings.audioSampleRateHz === 0 ? undefined : settings.audioSampleRateHz,
|
||||
'gapless-audio': settings.gaplessAudio || 'weak',
|
||||
replaygain: settings.replayGainMode || 'no',
|
||||
'replaygain-clip': settings.replayGainClip || 'no',
|
||||
'replaygain-fallback': settings.replayGainFallbackDB,
|
||||
'replaygain-preamp': settings.replayGainPreampDB || 0,
|
||||
};
|
||||
|
||||
Object.keys(properties).forEach((key) =>
|
||||
properties[key] === undefined ? delete properties[key] : {},
|
||||
);
|
||||
|
||||
return properties;
|
||||
};
|
||||
|
||||
export const MpvSettings = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const settings = usePlaybackSettings();
|
||||
|
||||
@@ -1,15 +1,41 @@
|
||||
import isElectron from 'is-electron';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { AdvancedTab } from '/@/renderer/features/settings/components/advanced/advanced-tab';
|
||||
import { GeneralTab } from '/@/renderer/features/settings/components/general/general-tab';
|
||||
import { HotkeysTab } from '/@/renderer/features/settings/components/hotkeys/hotkeys-tab';
|
||||
import { PlaybackTab } from '/@/renderer/features/settings/components/playback/playback-tab';
|
||||
import { WindowTab } from '/@/renderer/features/settings/components/window/window-tab';
|
||||
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
|
||||
import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store';
|
||||
import { Tabs } from '/@/shared/components/tabs/tabs';
|
||||
|
||||
const GeneralTab = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/general/general-tab').then((module) => ({
|
||||
default: module.GeneralTab,
|
||||
})),
|
||||
);
|
||||
|
||||
const PlaybackTab = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/playback/playback-tab').then((module) => ({
|
||||
default: module.PlaybackTab,
|
||||
})),
|
||||
);
|
||||
|
||||
const HotkeysTab = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/hotkeys/hotkeys-tab').then((module) => ({
|
||||
default: module.HotkeysTab,
|
||||
})),
|
||||
);
|
||||
|
||||
const WindowTab = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/window/window-tab').then((module) => ({
|
||||
default: module.WindowTab,
|
||||
})),
|
||||
);
|
||||
|
||||
const AdvancedTab = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/advanced/advanced-tab').then((module) => ({
|
||||
default: module.AdvancedTab,
|
||||
})),
|
||||
);
|
||||
|
||||
export const SettingsContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const currentTab = useSettingsStore((state) => state.tab);
|
||||
@@ -45,21 +71,31 @@ export const SettingsContent = () => {
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value="general">
|
||||
<GeneralTab />
|
||||
<Suspense fallback={null}>
|
||||
<GeneralTab />
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="playback">
|
||||
<PlaybackTab />
|
||||
<Suspense fallback={null}>
|
||||
<PlaybackTab />
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="hotkeys">
|
||||
<HotkeysTab />
|
||||
<Suspense fallback={null}>
|
||||
<HotkeysTab />
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
{isElectron() && (
|
||||
<Tabs.Panel value="window">
|
||||
<WindowTab />
|
||||
<Suspense fallback={null}>
|
||||
<WindowTab />
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
)}
|
||||
<Tabs.Panel value="advanced">
|
||||
<AdvancedTab />
|
||||
<Suspense fallback={null}>
|
||||
<AdvancedTab />
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { useState } from 'react';
|
||||
import { lazy, Suspense, useState } from 'react';
|
||||
|
||||
import { SettingsContent } from '/@/renderer/features/settings/components/settings-content';
|
||||
import { SettingsHeader } from '/@/renderer/features/settings/components/settings-header';
|
||||
import { SettingSearchContext } from '/@/renderer/features/settings/context/search-context';
|
||||
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
|
||||
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
|
||||
import { Flex } from '/@/shared/components/flex/flex';
|
||||
|
||||
const SettingsHeader = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/settings-header').then((module) => ({
|
||||
default: module.SettingsHeader,
|
||||
})),
|
||||
);
|
||||
|
||||
const SettingsRoute = () => {
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
@@ -15,7 +20,9 @@ const SettingsRoute = () => {
|
||||
<SettingSearchContext.Provider value={search}>
|
||||
<LibraryContainer>
|
||||
<Flex direction="column" h="100%" w="100%">
|
||||
<SettingsHeader setSearch={setSearch} />
|
||||
<Suspense fallback={<></>}>
|
||||
<SettingsHeader setSearch={setSearch} />
|
||||
</Suspense>
|
||||
<SettingsContent />
|
||||
</Flex>
|
||||
</LibraryContainer>
|
||||
|
||||
+34
-15
@@ -1,4 +1,3 @@
|
||||
import butterchurnPresets from 'butterchurn-presets';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -25,6 +24,38 @@ import { Text } from '/@/shared/components/text/text';
|
||||
import { Textarea } from '/@/shared/components/textarea/textarea';
|
||||
import { toast } from '/@/shared/components/toast/toast';
|
||||
|
||||
type ButterchurnPresetOption = { label: string; value: string };
|
||||
|
||||
let butterchurnPresetOptionsCache: ButterchurnPresetOption[] | null = null;
|
||||
|
||||
const loadButterchurnPresetOptions = async (): Promise<ButterchurnPresetOption[]> => {
|
||||
if (butterchurnPresetOptionsCache) return butterchurnPresetOptionsCache;
|
||||
|
||||
const mod = await import('butterchurn-presets');
|
||||
const presets = (mod as any).default ?? mod;
|
||||
const presetNames = Object.keys(presets);
|
||||
|
||||
butterchurnPresetOptionsCache = presetNames.map((presetName) => ({
|
||||
label: presetName,
|
||||
value: presetName,
|
||||
}));
|
||||
|
||||
return butterchurnPresetOptionsCache;
|
||||
};
|
||||
|
||||
const useButterchurnPresetOptions = () => {
|
||||
const [options, setOptions] = useState<ButterchurnPresetOption[]>(
|
||||
butterchurnPresetOptionsCache ?? [],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (butterchurnPresetOptionsCache) return;
|
||||
void loadButterchurnPresetOptions().then(setOptions);
|
||||
}, []);
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
const modeOptions: { label: string; value: string }[] = [
|
||||
{ label: i18n.t('visualizer.options.mode.0') as string, value: '0' },
|
||||
{ label: i18n.t('visualizer.options.mode.1') as string, value: '1' },
|
||||
@@ -2068,13 +2099,7 @@ const ButterchurnGeneralSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
const { updateProperty, visualizer } = useUpdateButterchurn();
|
||||
|
||||
const presetOptions = useMemo(() => {
|
||||
const presets = butterchurnPresets;
|
||||
return Object.keys(presets).map((presetName) => ({
|
||||
label: presetName,
|
||||
value: presetName,
|
||||
}));
|
||||
}, []);
|
||||
const presetOptions = useButterchurnPresetOptions();
|
||||
|
||||
return (
|
||||
<Fieldset legend={t('visualizer.general')}>
|
||||
@@ -2124,13 +2149,7 @@ const ButterChurnCycleSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
const { updateProperty, visualizer } = useUpdateButterchurn();
|
||||
|
||||
const presetOptions = useMemo(() => {
|
||||
const presets = butterchurnPresets;
|
||||
return Object.keys(presets).map((presetName) => ({
|
||||
label: presetName,
|
||||
value: presetName,
|
||||
}));
|
||||
}, []);
|
||||
const presetOptions = useButterchurnPresetOptions();
|
||||
|
||||
return (
|
||||
<Fieldset legend={t('visualizer.cyclePresets')}>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import AudioMotionAnalyzer from 'audiomotion-analyzer';
|
||||
import { createRef, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { createRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import styles from './visualizer.module.css';
|
||||
|
||||
@@ -15,7 +14,31 @@ const VisualizerInner = () => {
|
||||
const accent = useAccent();
|
||||
const visualizer = useSettingsStore((store) => store.visualizer);
|
||||
const opacity = useSettingsStore((store) => store.visualizer.audiomotionanalyzer.opacity);
|
||||
const [motion, setMotion] = useState<AudioMotionAnalyzer>();
|
||||
const [motion, setMotion] = useState<any>();
|
||||
const [libraryLoaded, setLibraryLoaded] = useState(false);
|
||||
const AudioMotionAnalyzerRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const loadLibrary = async () => {
|
||||
try {
|
||||
const module = await import('audiomotion-analyzer');
|
||||
if (isMounted) {
|
||||
AudioMotionAnalyzerRef.current = module.default;
|
||||
setLibraryLoaded(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load AudioMotionAnalyzer library:', error);
|
||||
}
|
||||
};
|
||||
|
||||
loadLibrary();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Check if a gradient name is a custom gradient
|
||||
const isCustomGradient = useCallback(
|
||||
@@ -162,7 +185,7 @@ const VisualizerInner = () => {
|
||||
);
|
||||
|
||||
const registerCustomGradients = useCallback(
|
||||
(audioMotionInstance: AudioMotionAnalyzer) => {
|
||||
(audioMotionInstance: any) => {
|
||||
if (visualizer.type !== 'audiomotionanalyzer') {
|
||||
return;
|
||||
}
|
||||
@@ -187,8 +210,11 @@ const VisualizerInner = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const { context, gains } = webAudio || {};
|
||||
let audioMotion: AudioMotionAnalyzer | undefined;
|
||||
if (gains && context && canvasRef.current && !motion) {
|
||||
let audioMotion: any | undefined;
|
||||
if (gains && context && canvasRef.current && !motion && libraryLoaded) {
|
||||
const AudioMotionAnalyzer = AudioMotionAnalyzerRef.current;
|
||||
if (!AudioMotionAnalyzer) return;
|
||||
|
||||
// Reset gradients registered flag on new instance
|
||||
setGradientsRegistered(false);
|
||||
|
||||
@@ -236,6 +262,7 @@ const VisualizerInner = () => {
|
||||
options,
|
||||
isCustomGradient,
|
||||
motion,
|
||||
libraryLoaded,
|
||||
]);
|
||||
|
||||
// Re-register custom gradients when they change
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import butterchurn from 'butterchurn';
|
||||
import butterchurnPresets from 'butterchurn-presets';
|
||||
import { createRef, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import styles from './visualizer.module.css';
|
||||
@@ -27,6 +25,9 @@ const VisualizerInner = () => {
|
||||
const visualizerRef = useRef<ButterchurnVisualizer | undefined>(undefined);
|
||||
const isInitializedRef = useRef(false);
|
||||
const [isVisualizerReady, setIsVisualizerReady] = useState(false);
|
||||
const [librariesLoaded, setLibrariesLoaded] = useState(false);
|
||||
const butterchurnRef = useRef<any>(null);
|
||||
const butterchurnPresetsRef = useRef<any>(null);
|
||||
const animationFrameRef = useRef<number | undefined>(undefined);
|
||||
const resizeObserverRef = useRef<ResizeObserver | undefined>(undefined);
|
||||
const cycleTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
@@ -39,6 +40,33 @@ const VisualizerInner = () => {
|
||||
const playerStatus = usePlayerStatus();
|
||||
const isPlaying = playerStatus === PlayerStatus.PLAYING;
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const loadLibraries = async () => {
|
||||
try {
|
||||
const [butterchurnModule, presetsModule] = await Promise.all([
|
||||
import('butterchurn'),
|
||||
import('butterchurn-presets'),
|
||||
]);
|
||||
|
||||
if (isMounted) {
|
||||
butterchurnRef.current = butterchurnModule.default;
|
||||
butterchurnPresetsRef.current = presetsModule.default;
|
||||
setLibrariesLoaded(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load butterchurn libraries:', error);
|
||||
}
|
||||
};
|
||||
|
||||
loadLibraries();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const cleanupVisualizer = () => {
|
||||
if (animationFrameRef.current) {
|
||||
cancelAnimationFrame(animationFrameRef.current);
|
||||
@@ -79,6 +107,7 @@ const VisualizerInner = () => {
|
||||
canvas &&
|
||||
container &&
|
||||
isPlaying &&
|
||||
librariesLoaded &&
|
||||
(!isInitializedRef.current || !visualizerRef.current);
|
||||
|
||||
if (!needsInitialization) {
|
||||
@@ -107,13 +136,16 @@ const VisualizerInner = () => {
|
||||
initializeVisualizer(dimensions.width, dimensions.height);
|
||||
}
|
||||
|
||||
function initializeVisualizer(width: number, height: number) {
|
||||
if (!gains || gains.length === 0 || !canvas || !context) return;
|
||||
async function initializeVisualizer(width: number, height: number) {
|
||||
if (!gains || gains.length === 0 || !canvas || !context || !librariesLoaded) return;
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
try {
|
||||
const butterchurn = butterchurnRef.current;
|
||||
if (!butterchurn) return;
|
||||
|
||||
const butterchurnInstance = butterchurn.createVisualizer(context, canvas, {
|
||||
height,
|
||||
width,
|
||||
@@ -138,7 +170,7 @@ const VisualizerInner = () => {
|
||||
cleanupVisualizer();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [webAudio, isPlaying]);
|
||||
}, [webAudio, isPlaying, librariesLoaded]);
|
||||
|
||||
// Kill visualizer after 5 seconds of pause
|
||||
useEffect(() => {
|
||||
@@ -204,9 +236,11 @@ const VisualizerInner = () => {
|
||||
// Load initial preset when visualizer is ready
|
||||
useEffect(() => {
|
||||
const visualizer = visualizerRef.current;
|
||||
if (!visualizer || !isVisualizerReady || initialPresetLoadedRef.current) return;
|
||||
if (!visualizer || !isVisualizerReady || initialPresetLoadedRef.current || !librariesLoaded)
|
||||
return;
|
||||
|
||||
const presets = butterchurnPresets;
|
||||
const presets = butterchurnPresetsRef.current;
|
||||
if (!presets) return;
|
||||
const presetNames = Object.keys(presets);
|
||||
|
||||
if (presetNames.length > 0) {
|
||||
@@ -222,14 +256,24 @@ const VisualizerInner = () => {
|
||||
initialPresetLoadedRef.current = true;
|
||||
}
|
||||
}
|
||||
}, [isVisualizerReady, butterchurnSettings.currentPreset, butterchurnSettings.blendTime]);
|
||||
}, [
|
||||
isVisualizerReady,
|
||||
butterchurnSettings.currentPreset,
|
||||
butterchurnSettings.blendTime,
|
||||
librariesLoaded,
|
||||
]);
|
||||
|
||||
// Update preset when currentPreset or blendTime changes (but not when cycling)
|
||||
const isCyclingRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const visualizer = visualizerRef.current;
|
||||
if (!visualizer || !butterchurnSettings.currentPreset || !initialPresetLoadedRef.current)
|
||||
if (
|
||||
!visualizer ||
|
||||
!butterchurnSettings.currentPreset ||
|
||||
!initialPresetLoadedRef.current ||
|
||||
!librariesLoaded
|
||||
)
|
||||
return;
|
||||
|
||||
// Skip if we're currently cycling (to avoid reloading preset)
|
||||
@@ -238,7 +282,8 @@ const VisualizerInner = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const presets = butterchurnPresets;
|
||||
const presets = butterchurnPresetsRef.current;
|
||||
if (!presets) return;
|
||||
const preset = presets[butterchurnSettings.currentPreset];
|
||||
|
||||
if (preset) {
|
||||
@@ -246,12 +291,17 @@ const VisualizerInner = () => {
|
||||
// Reset cycle timer when preset changes manually
|
||||
cycleStartTimeRef.current = Date.now();
|
||||
}
|
||||
}, [butterchurnSettings.currentPreset, butterchurnSettings.blendTime]);
|
||||
}, [butterchurnSettings.currentPreset, butterchurnSettings.blendTime, librariesLoaded]);
|
||||
|
||||
// Handle preset cycling
|
||||
useEffect(() => {
|
||||
const visualizer = visualizerRef.current;
|
||||
if (!visualizer || !butterchurnSettings.cyclePresets || !initialPresetLoadedRef.current) {
|
||||
if (
|
||||
!visualizer ||
|
||||
!butterchurnSettings.cyclePresets ||
|
||||
!initialPresetLoadedRef.current ||
|
||||
!librariesLoaded
|
||||
) {
|
||||
// Clear cycle timer if cycling is disabled or visualizer not ready
|
||||
if (cycleTimerRef.current) {
|
||||
clearInterval(cycleTimerRef.current);
|
||||
@@ -260,7 +310,8 @@ const VisualizerInner = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const presets = butterchurnPresets;
|
||||
const presets = butterchurnPresetsRef.current;
|
||||
if (!presets) return;
|
||||
const allPresetNames = Object.keys(presets);
|
||||
|
||||
// Get the list of presets to cycle through
|
||||
@@ -359,6 +410,7 @@ const VisualizerInner = () => {
|
||||
butterchurnSettings.randomizeNextPreset,
|
||||
butterchurnSettings.currentPreset,
|
||||
setSettings,
|
||||
librariesLoaded,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import { useRef } from 'react';
|
||||
import { lazy, Suspense, useRef } from 'react';
|
||||
|
||||
import styles from './left-sidebar.module.css';
|
||||
|
||||
import { ResizeHandle } from '/@/renderer/features/shared/components/resize-handle';
|
||||
import { CollapsedSidebar } from '/@/renderer/features/sidebar/components/collapsed-sidebar';
|
||||
import { Sidebar } from '/@/renderer/features/sidebar/components/sidebar';
|
||||
import { useAppStore } from '/@/renderer/store';
|
||||
|
||||
const CollapsedSidebar = lazy(() =>
|
||||
import('/@/renderer/features/sidebar/components/collapsed-sidebar').then((module) => ({
|
||||
default: module.CollapsedSidebar,
|
||||
})),
|
||||
);
|
||||
|
||||
const Sidebar = lazy(() =>
|
||||
import('/@/renderer/features/sidebar/components/sidebar').then((module) => ({
|
||||
default: module.Sidebar,
|
||||
})),
|
||||
);
|
||||
|
||||
interface LeftSidebarProps {
|
||||
isResizing: boolean;
|
||||
startResizing: (direction: 'left' | 'right', mouseEvent?: MouseEvent) => void;
|
||||
@@ -27,7 +37,7 @@ export const LeftSidebar = ({ isResizing, startResizing }: LeftSidebarProps) =>
|
||||
placement="right"
|
||||
ref={sidebarRef}
|
||||
/>
|
||||
{collapsed ? <CollapsedSidebar /> : <Sidebar />}
|
||||
<Suspense fallback={<></>}>{collapsed ? <CollapsedSidebar /> : <Sidebar />}</Suspense>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { HashRouter, Route, Routes } from 'react-router';
|
||||
|
||||
import { LyricsSettingsContextModal } from '/@/renderer/features/lyrics/components/lyrics-settings-modal';
|
||||
import { ShuffleAllContextModal } from '/@/renderer/features/player/components/shuffle-all-modal';
|
||||
import { AddToPlaylistContextModal } from '/@/renderer/features/playlists/components/add-to-playlist-context-modal';
|
||||
import { SaveAndReplaceContextModal } from '/@/renderer/features/playlists/components/save-and-replace-context-modal';
|
||||
import { UpdatePlaylistContextModal } from '/@/renderer/features/playlists/components/update-playlist-form';
|
||||
import { SettingsContextModal } from '/@/renderer/features/settings/components/settings-modal';
|
||||
import { RouterErrorBoundary } from '/@/renderer/features/shared/components/router-error-boundary';
|
||||
import { ShareItemContextModal } from '/@/renderer/features/sharing/components/share-item-context-modal';
|
||||
import { VisualizerSettingsContextModal } from '/@/renderer/features/visualizer/components/audiomotionanalyzer/visualizer-settings-modal';
|
||||
import { AuthenticationOutlet } from '/@/renderer/layouts/authentication-outlet';
|
||||
import { ResponsiveLayout } from '/@/renderer/layouts/responsive-layout';
|
||||
import { AppOutlet } from '/@/renderer/router/app-outlet';
|
||||
@@ -87,6 +79,108 @@ const FavoritesRoute = lazy(() => import('/@/renderer/features/favorites/routes/
|
||||
|
||||
const SettingsRoute = lazy(() => import('/@/renderer/features/settings/routes/settings-route'));
|
||||
|
||||
const LazyLyricsSettingsContextModal = lazy(() =>
|
||||
import('/@/renderer/features/lyrics/components/lyrics-settings-modal').then((module) => ({
|
||||
default: module.LyricsSettingsContextModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const LyricsSettingsContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazyLyricsSettingsContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazyShuffleAllContextModal = lazy(() =>
|
||||
import('/@/renderer/features/player/components/shuffle-all-modal').then((module) => ({
|
||||
default: module.ShuffleAllContextModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const ShuffleAllContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazyShuffleAllContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazyAddToPlaylistContextModal = lazy(() =>
|
||||
import('/@/renderer/features/playlists/components/add-to-playlist-context-modal').then(
|
||||
(module) => ({
|
||||
default: module.AddToPlaylistContextModal,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const AddToPlaylistContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazyAddToPlaylistContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazySaveAndReplaceContextModal = lazy(() =>
|
||||
import('/@/renderer/features/playlists/components/save-and-replace-context-modal').then(
|
||||
(module) => ({
|
||||
default: module.SaveAndReplaceContextModal,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const SaveAndReplaceContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazySaveAndReplaceContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazyUpdatePlaylistContextModal = lazy(() =>
|
||||
import('/@/renderer/features/playlists/components/update-playlist-form').then((module) => ({
|
||||
default: module.UpdatePlaylistContextModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const UpdatePlaylistContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazyUpdatePlaylistContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazySettingsContextModal = lazy(() =>
|
||||
import('/@/renderer/features/settings/components/settings-modal').then((module) => ({
|
||||
default: module.SettingsContextModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const SettingsContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazySettingsContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazyShareItemContextModal = lazy(() =>
|
||||
import('/@/renderer/features/sharing/components/share-item-context-modal').then((module) => ({
|
||||
default: module.ShareItemContextModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const ShareItemContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazyShareItemContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const LazyVisualizerSettingsContextModal = lazy(() =>
|
||||
import(
|
||||
'/@/renderer/features/visualizer/components/audiomotionanalyzer/visualizer-settings-modal'
|
||||
).then((module) => ({
|
||||
default: module.VisualizerSettingsContextModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const VisualizerSettingsContextModal = (props: any) => (
|
||||
<Suspense fallback={<></>}>
|
||||
<LazyVisualizerSettingsContextModal {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
export const AppRouter = () => {
|
||||
const router = (
|
||||
<HashRouter>
|
||||
|
||||
Reference in New Issue
Block a user