From 8917a5d076abb4f994a6cd38900569f8893d079d Mon Sep 17 00:00:00 2001 From: ChrisScott9456 Date: Sun, 28 Jun 2026 20:46:15 -0400 Subject: [PATCH] Add global context modal for song edit and move album fetch to it --- .../actions/edit-metadata-action.tsx | 41 +++------------ .../components/song-edit-context-modal.tsx | 52 +++++++++++++++++++ src/renderer/router/app-router.tsx | 13 +++++ 3 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 src/renderer/features/tag-editor/components/song-edit-context-modal.tsx diff --git a/src/renderer/features/context-menu/actions/edit-metadata-action.tsx b/src/renderer/features/context-menu/actions/edit-metadata-action.tsx index 7dc40b02d..123427ed5 100644 --- a/src/renderer/features/context-menu/actions/edit-metadata-action.tsx +++ b/src/renderer/features/context-menu/actions/edit-metadata-action.tsx @@ -1,11 +1,8 @@ -import { openModal } from '@mantine/modals'; +import { openContextModal } from '@mantine/modals'; import isElectron from 'is-electron'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { controller } from '/@/renderer/api/controller'; -import { SongEditModal } from '/@/renderer/features/tag-editor/components/song-edit-modal'; -import { useCurrentServer } from '/@/renderer/store'; import { ContextMenu } from '/@/shared/components/context-menu/context-menu'; import { Song } from '/@/shared/types/domain-types'; @@ -16,44 +13,20 @@ interface EditMetadataActionProps { const utils = isElectron() ? window.api.utils : null; -const getAlbumSongs = async (albumIds: string[], serverId: string): Promise => { - const albumDetails = await Promise.all( - albumIds.map((id) => - controller.getAlbumDetail({ - apiClientProps: { serverId }, - query: { id }, - }), - ), - ); - return albumDetails.flatMap((album) => album?.songs ?? []).filter((s) => s.path); -}; - export const EditMetadataAction = ({ albumIds, songs: songItems }: EditMetadataActionProps) => { const { t } = useTranslation(); - const server = useCurrentServer(); const songs = useMemo(() => songItems?.filter((s) => s.path) ?? [], [songItems]); const count = albumIds?.length ?? songs.length; - const onSelect = useCallback(async () => { - let resolvedSongs: Song[]; - - if (albumIds) { - resolvedSongs = server?.id ? await getAlbumSongs(albumIds, server.id) : []; - } else { - resolvedSongs = songs; - } - - const trackCount = resolvedSongs.length; - openModal({ - children: , + const onSelect = useCallback(() => { + openContextModal({ + innerProps: { albumIds, songs }, + modal: 'editMetadata', size: 'xl', styles: { body: { paddingBottom: 'var(--theme-spacing-xl)' } }, - title: - trackCount > 1 - ? `${t('page.contextMenu.editMetadata')} (${trackCount} ${t('common.tracks', 'tracks')})` - : t('page.contextMenu.editMetadata'), + title: t('page.contextMenu.editMetadata'), }); - }, [albumIds, server, songs, t]); + }, [albumIds, songs, t]); if (!utils) return null; diff --git a/src/renderer/features/tag-editor/components/song-edit-context-modal.tsx b/src/renderer/features/tag-editor/components/song-edit-context-modal.tsx new file mode 100644 index 000000000..ef0e76056 --- /dev/null +++ b/src/renderer/features/tag-editor/components/song-edit-context-modal.tsx @@ -0,0 +1,52 @@ +import { ContextModalProps } from '@mantine/modals'; +import { useEffect, useState } from 'react'; + +import { controller } from '/@/renderer/api/controller'; +import { SongEditModal } from '/@/renderer/features/tag-editor/components/song-edit-modal'; +import { useCurrentServer } from '/@/renderer/store'; +import { Spinner } from '/@/shared/components/spinner/spinner'; +import { Stack } from '/@/shared/components/stack/stack'; +import { Song } from '/@/shared/types/domain-types'; + +type SongEditInnerProps = { + albumIds?: string[]; + songs?: Song[]; +}; + +export const SongEditContextModal = ({ innerProps }: ContextModalProps) => { + const server = useCurrentServer(); + const [resolvedSongs, setResolvedSongs] = useState(null); + + useEffect(() => { + if (innerProps.albumIds) { + if (!server?.id) { + setResolvedSongs([]); + return; + } + Promise.all( + innerProps.albumIds.map((id) => + controller.getAlbumDetail({ + apiClientProps: { serverId: server.id }, + query: { id }, + }), + ), + ).then((albums) => { + setResolvedSongs( + albums.flatMap((album) => album?.songs ?? []).filter((s) => s.path), + ); + }); + } else { + setResolvedSongs((innerProps.songs ?? []).filter((s) => s.path)); + } + }, [innerProps.albumIds, innerProps.songs, server?.id]); + + if (resolvedSongs === null) { + return ( + + + + ); + } + + return ; +}; diff --git a/src/renderer/router/app-router.tsx b/src/renderer/router/app-router.tsx index 1ca94adac..bffcc4263 100644 --- a/src/renderer/router/app-router.tsx +++ b/src/renderer/router/app-router.tsx @@ -175,9 +175,22 @@ const VisualizerSettingsContextModal = (props: any) => ( ); +const LazySongEditContextModal = lazy(() => + import('/@/renderer/features/tag-editor/components/song-edit-context-modal').then((module) => ({ + default: module.SongEditContextModal, + })), +); + +const SongEditContextModal = (props: any) => ( + }> + + +); + const appRouterModals = { addToPlaylist: AddToPlaylistContextModal, base: BaseContextModal, + editMetadata: SongEditContextModal, lyricsSettings: LyricsSettingsContextModal, saveAndReplace: SaveAndReplaceContextModal, settings: SettingsContextModal,