import { closeModal, ContextModalProps } from '@mantine/modals'; import { useQuery } from '@tanstack/react-query'; import { t } from 'i18next'; import { type ReactNode, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ItemImage } from '/@/renderer/components/item-image/item-image'; import { useDeletePlaylistImage } from '/@/renderer/features/playlists/mutations/delete-playlist-image-mutation'; import { useUpdatePlaylist } from '/@/renderer/features/playlists/mutations/update-playlist-mutation'; import { useUploadPlaylistImage } from '/@/renderer/features/playlists/mutations/upload-playlist-image-mutation'; import { sharedQueries } from '/@/renderer/features/shared/api/shared-api'; import { useCurrentServer, useCurrentServerId, usePermissions } from '/@/renderer/store'; import { hasFeature } from '/@/shared/api/utils'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { Box } from '/@/shared/components/box/box'; import { DragDropZone } from '/@/shared/components/drag-drop-zone/drag-drop-zone'; import { FileButton } from '/@/shared/components/file-button/file-button'; import { Flex } from '/@/shared/components/flex/flex'; import { Group } from '/@/shared/components/group/group'; import { ModalButton } from '/@/shared/components/modal/model-shared'; import { Select } from '/@/shared/components/select/select'; import { Stack } from '/@/shared/components/stack/stack'; import { Switch } from '/@/shared/components/switch/switch'; import { TextInput } from '/@/shared/components/text-input/text-input'; import { Textarea } from '/@/shared/components/textarea/textarea'; import { toast } from '/@/shared/components/toast/toast'; import { useForm } from '/@/shared/hooks/use-form'; import { LibraryItem, ServerType, SortOrder, UpdatePlaylistBody, UpdatePlaylistQuery, UserListSort, } from '/@/shared/types/domain-types'; import { ServerFeature } from '/@/shared/types/features-types'; type PlaylistImageProps = { imageId: null | string; imageUrl: null | string; uploadedImage?: string; }; export const UpdatePlaylistContextModal = ({ id, innerProps, }: ContextModalProps<{ body: Partial; playlistImage?: PlaylistImageProps; query: UpdatePlaylistQuery; }>) => { const { t } = useTranslation(); const updateMutation = useUpdatePlaylist({}); const uploadImageMutation = useUploadPlaylistImage({}); const deleteImageMutation = useDeletePlaylistImage({}); const server = useCurrentServer(); const { body, playlistImage, query } = innerProps; const [pendingFile, setPendingFile] = useState(null); const [pendingPreviewUrl, setPendingPreviewUrl] = useState(null); const [removeCustomCover, setRemoveCustomCover] = useState(false); const [isSaving, setIsSaving] = useState(false); useEffect(() => { if (!pendingFile) { setPendingPreviewUrl(null); return; } const url = URL.createObjectURL(pendingFile); setPendingPreviewUrl(url); return () => URL.revokeObjectURL(url); }, [pendingFile]); const form = useForm({ initialValues: { comment: body?.comment || '', name: body?.name || '', ownerId: body.ownerId, public: body.public, queryBuilderRules: body.queryBuilderRules, sync: body.sync, }, }); const handleSubmit = form.onSubmit(async (values) => { if (!server?.id) return; setIsSaving(true); try { await updateMutation.mutateAsync({ apiClientProps: { serverId: server.id }, body: values, query, }); if (pendingFile) { const buffer = await pendingFile.arrayBuffer(); await uploadImageMutation.mutateAsync({ apiClientProps: { serverId: server.id }, body: { image: new Uint8Array(buffer) }, query: { id: query.id }, }); } else if (removeCustomCover && playlistImage?.uploadedImage) { await deleteImageMutation.mutateAsync({ apiClientProps: { serverId: server.id }, query: { id: query.id }, }); } toast.success({ message: t('form.editPlaylist.success', { postProcess: 'sentenceCase' }), }); closeModal(id); } catch (err: any) { toast.error({ message: err?.message, title: t('error.genericError', { postProcess: 'sentenceCase' }), }); } finally { setIsSaving(false); } }); const isPublicDisplayed = hasFeature(server, ServerFeature.PUBLIC_PLAYLIST); const isOwnerDisplayed = server?.type === ServerType.NAVIDROME; const isCommentDisplayed = server?.type === ServerType.NAVIDROME; const isCoverImageDisplayed = hasFeature(server, ServerFeature.PLAYLIST_IMAGE_UPLOAD); const isSubmitDisabled = !form.values.name || isSaving; const hadUploadedCover = !!playlistImage?.uploadedImage; const fieldNodes: ReactNode[] = [ , ]; if (isCommentDisplayed) { fieldNodes.push(