import { t } from 'i18next'; import { MouseEvent, type ReactNode, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ItemImage } from '/@/renderer/components/item-image/item-image'; import { useDeleteInternetRadioStationImage } from '/@/renderer/features/radio/mutations/delete-internet-radio-station-image-mutation'; import { useUpdateRadioStation } from '/@/renderer/features/radio/mutations/update-radio-station-mutation'; import { useUploadInternetRadioStationImage } from '/@/renderer/features/radio/mutations/upload-internet-radio-station-image-mutation'; import { useCurrentServer } from '/@/renderer/store'; import { logFn } from '/@/renderer/utils/logger'; import { logMsg } from '/@/renderer/utils/logger-message'; import { hasFeature } from '/@/shared/api/utils'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { Box } from '/@/shared/components/box/box'; import { FileButton } from '/@/shared/components/file-button/file-button'; import { Flex } from '/@/shared/components/flex/flex'; import { Group } from '/@/shared/components/group/group'; import { closeAllModals, openModal } from '/@/shared/components/modal/modal'; import { ModalButton } from '/@/shared/components/modal/model-shared'; import { Stack } from '/@/shared/components/stack/stack'; import { TextInput } from '/@/shared/components/text-input/text-input'; import { toast } from '/@/shared/components/toast/toast'; import { useForm } from '/@/shared/hooks/use-form'; import { InternetRadioStation, LibraryItem, ServerListItem, UpdateInternetRadioStationBody, } from '/@/shared/types/domain-types'; import { ServerFeature } from '/@/shared/types/features-types'; interface EditRadioStationFormProps { onCancel: () => void; station: InternetRadioStation; } type RadioStationImageProps = { imageId: null | string; imageUrl: null | string; uploadedImage?: string; }; export const EditRadioStationForm = ({ onCancel, station }: EditRadioStationFormProps) => { const { t } = useTranslation(); const updateMutation = useUpdateRadioStation({}); const uploadImageMutation = useUploadInternetRadioStationImage({}); const deleteImageMutation = useDeleteInternetRadioStationImage({}); const server = useCurrentServer(); const isCoverImageDisplayed = hasFeature(server, ServerFeature.INTERNET_RADIO_IMAGE_UPLOAD); const stationImage: RadioStationImageProps = { imageId: station.imageId ?? null, imageUrl: station.imageUrl ?? null, uploadedImage: station.uploadedImage ?? undefined, }; 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: { homepageUrl: station.homepageUrl || '', name: station.name, streamUrl: station.streamUrl, }, }); const handleSubmit = form.onSubmit(async (values) => { if (!server?.id) return; setIsSaving(true); try { await updateMutation.mutateAsync({ apiClientProps: { serverId: server.id }, body: values, query: { id: station.id }, }); if (pendingFile) { const buffer = await pendingFile.arrayBuffer(); await uploadImageMutation.mutateAsync({ apiClientProps: { serverId: server.id }, body: { image: new Uint8Array(buffer) }, query: { id: station.id }, }); } else if (removeCustomCover && stationImage.uploadedImage) { await deleteImageMutation.mutateAsync({ apiClientProps: { serverId: server.id }, query: { id: station.id }, }); } toast.success({ message: t('form.editRadioStation.success', { postProcess: 'sentenceCase', }) as string, }); closeAllModals(); } catch (err: unknown) { logFn.error(logMsg.other.error, { meta: { error: err as Error }, }); toast.error({ message: (err as Error)?.message, title: t('error.genericError', { postProcess: 'sentenceCase' }) as string, }); } finally { setIsSaving(false); } }); const isSubmitDisabled = !form.values.name || !form.values.streamUrl || isSaving; const hadUploadedCover = !!stationImage.uploadedImage; const fieldNodes: ReactNode[] = [ , , , {t('common.cancel')} {t('common.save')} , ]; return (
{isCoverImageDisplayed && server?.id ? ( setPendingFile(null)} onFileSelect={(file) => { if (!file) return; setRemoveCustomCover(false); setPendingFile(file); }} onToggleRemoveCover={() => setRemoveCustomCover((v) => !v)} pendingFile={pendingFile} pendingPreviewUrl={pendingPreviewUrl} removeCustomCover={removeCustomCover} stationImage={stationImage} /> {fieldNodes} ) : ( {fieldNodes} )}
); }; const COVER_SIZE = 240; function RadioStationCoverField({ hadUploadedCover, onClearPending, onFileSelect, onToggleRemoveCover, pendingFile, pendingPreviewUrl, removeCustomCover, stationImage, }: { hadUploadedCover: boolean; onClearPending: () => void; onFileSelect: (file: File | null) => void; onToggleRemoveCover: () => void; pendingFile: File | null; pendingPreviewUrl: null | string; removeCustomCover: boolean; stationImage: RadioStationImageProps; }) { const server = useCurrentServer(); const showServerCover = !pendingPreviewUrl && !removeCustomCover; const previewId = showServerCover ? stationImage.imageId || undefined : undefined; const previewSrc = pendingPreviewUrl || (showServerCover ? stationImage.imageUrl || '' : ''); const secondaryAction = () => { if (pendingFile) { onClearPending(); return; } if (hadUploadedCover) { onToggleRemoveCover(); } }; const secondaryDisabled = !pendingFile && !hadUploadedCover; const secondaryIcon = pendingFile ? 'x' : removeCustomCover ? 'arrowLeft' : 'delete'; const iconControls = ( <> {(props) => ( )} ); const coverArt = ( ); return ( {coverArt} {iconControls} ); } export const openEditRadioStationModal = ( station: InternetRadioStation, server: null | ServerListItem, e?: MouseEvent, ) => { e?.stopPropagation(); if (!server) { toast.error({ message: t('common.error.noServer', { postProcess: 'sentenceCase' }) as string, }); return; } const hasImageUpload = hasFeature(server, ServerFeature.INTERNET_RADIO_IMAGE_UPLOAD); openModal({ children: , size: hasImageUpload ? 'lg' : 'md', title: t('common.edit', { postProcess: 'titleCase' }) as string, }); };