2025-07-20 17:37:39 +02:00
/ *
* Copyright ( C ) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at https : //mozilla.org/MPL/2.0/.
* /
import { useState } from 'react' ;
import Stack from '@mui/material/Stack' ;
2025-12-07 03:32:28 +01:00
import Typography from '@mui/material/Typography' ;
2025-07-20 17:37:39 +02:00
import Button from '@mui/material/Button' ;
2026-01-10 19:45:02 +01:00
import { useLingui } from '@lingui/react/macro' ;
2025-12-22 22:04:05 +01:00
import {
IMAGE_PROCESSING_TYPE_TO_SETTING ,
IMAGE_PROCESSING_TYPE_TO_TRANSLATION ,
} from '@/features/settings/Settings.constants.ts' ;
2025-12-22 21:11:12 +01:00
import { requestManager } from '@/lib/requests/RequestManager.ts' ;
2026-03-11 00:11:29 +01:00
import type { ImageProcessingType , ServerSettings } from '@/features/settings/Settings.types.ts' ;
2025-12-22 21:11:12 +01:00
import { makeToast } from '@/base/utils/Toast.ts' ;
import { getErrorMessage } from '@/lib/HelperFunctions.ts' ;
import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx' ;
import { EmptyViewAbsoluteCentered } from '@/base/components/feedback/EmptyViewAbsoluteCentered.tsx' ;
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts' ;
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts' ;
2025-12-22 21:33:23 +01:00
import {
addStableIdToConversions ,
containsInvalidConversion ,
didUpdateConversions ,
isDuplicateConversion ,
maybeAddDefault ,
normalizeConversions ,
toValidServerConversions ,
} from '@/features/settings/ImageProcessing.utils.ts' ;
import { Processing } from '@/features/settings/components/images/Processing.tsx' ;
2026-03-20 03:29:45 +01:00
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts' ;
2025-12-22 16:49:10 +01:00
2025-12-22 22:04:05 +01:00
export const ImageProcessingSetting = ( { type } : { type : ImageProcessingType } ) = > {
2026-01-10 19:45:02 +01:00
const { t } = useLingui ( ) ;
2025-07-20 17:37:39 +02:00
2025-12-22 22:04:05 +01:00
useAppTitle ( t ( IMAGE_PROCESSING_TYPE_TO_TRANSLATION [ type ] ) ) ;
2025-12-22 21:11:12 +01:00
2026-05-02 17:37:38 +02:00
const { data , loading , error , refetch } = requestManager . useGetServerSettings ( ) ;
2025-12-22 21:11:12 +01:00
const [ mutateSettings ] = requestManager . useUpdateServerSettings ( ) ;
2025-12-22 22:04:05 +01:00
const settingKey = IMAGE_PROCESSING_TYPE_TO_SETTING [ type ] ;
2026-03-20 03:29:45 +01:00
const conversions = data ? . settings ? . [ settingKey ] ? ? STABLE_EMPTY_ARRAY ;
2025-12-22 21:11:12 +01:00
2025-12-22 16:49:10 +01:00
const [ tmpConversions , setTmpConversions ] = useState (
normalizeConversions ( maybeAddDefault ( addStableIdToConversions ( conversions ) ) ) ,
) ;
2025-07-20 17:37:39 +02:00
const hasInvalidConversion = containsInvalidConversion ( tmpConversions ) ;
2025-12-22 16:49:10 +01:00
const hasChanged = didUpdateConversions (
normalizeConversions ( maybeAddDefault ( addStableIdToConversions ( conversions ) ) ) ,
tmpConversions ,
) ;
2025-07-20 17:37:39 +02:00
2025-12-22 22:04:05 +01:00
const updateSetting = ( value : ServerSettings [ typeof settingKey ] ) : Promise < any > = > {
const mutation = mutateSettings ( { variables : { input : { settings : { [ settingKey ] : value } } } } ) ;
2026-01-10 19:45:02 +01:00
mutation . catch ( ( e ) = > makeToast ( t ` Failed to save changes ` , 'error' , getErrorMessage ( e ) ) ) ;
2025-12-22 21:11:12 +01:00
return mutation ;
} ;
2025-09-13 14:13:32 +02:00
const onSubmit = async ( ) = > {
try {
await updateSetting ( toValidServerConversions ( tmpConversions ) ) ;
} catch ( e ) {
// ignore error
}
} ;
2026-03-15 01:42:37 +01:00
if ( loading ) {
return < LoadingPlaceholder / > ;
}
if ( error ) {
return (
< EmptyViewAbsoluteCentered
message = { t ` Unable to load data ` }
messageExtra = { getErrorMessage ( error ) }
retry = { ( ) = > refetch ( ) . catch ( defaultPromiseErrorHandler ( 'ImageProcessingSetting::refetch' ) ) }
/ >
) ;
}
2025-07-20 17:37:39 +02:00
return (
2025-12-28 02:07:52 +01:00
< Stack sx = { { p : 2 , gap : 5 } } >
2026-01-10 19:45:02 +01:00
< Typography >
{ t ` In case no MIME-Type is defined, the "default" one will be used for the processing. \ nSet the target mode to disabled to prevent processing for a MIME-Type ` }
< / Typography >
2025-12-07 03:32:28 +01:00
< Stack sx = { { flexDirection : 'column' , gap : 5 } } >
{ tmpConversions . map ( ( conversion , index ) = > {
const { mimeType } = conversion ;
const isDuplicate = isDuplicateConversion ( mimeType , index , tmpConversions ) ;
return (
2025-12-22 21:33:23 +01:00
< Processing
2025-12-22 16:49:10 +01:00
key = { conversion . id }
2025-12-07 03:32:28 +01:00
conversion = { conversion }
isDuplicate = { isDuplicate }
onChange = { ( newConversion ) = > {
setTmpConversions ( ( prev ) = >
maybeAddDefault (
prev . toSpliced ( index , 1 , . . . ( newConversion ? [ newConversion ] : [ ] ) ) ,
) ,
) ;
2025-07-20 17:37:39 +02:00
} }
2025-12-07 03:32:28 +01:00
/ >
) ;
} ) }
< / Stack >
< Stack
direction = "row"
sx = { {
2025-12-22 16:49:10 +01:00
gap : 1 ,
2025-12-07 03:32:28 +01:00
} }
>
< Button
variant = "outlined"
onClick = { ( ) = > {
2025-12-22 16:49:10 +01:00
setTmpConversions ( ( prev ) = > [
. . . prev ,
2025-12-22 21:33:23 +01:00
. . . addStableIdToConversions ( [
{
mimeType : '' ,
target : '' ,
compressionLevel : null ,
headers : null ,
callTimeout : null ,
connectTimeout : null ,
} ,
] ) ,
2025-12-22 16:49:10 +01:00
] ) ;
2025-12-07 03:32:28 +01:00
} }
>
2026-01-10 19:45:02 +01:00
{ t ` Add ` }
2025-12-07 03:32:28 +01:00
< / Button >
< Button variant = "contained" disabled = { hasInvalidConversion || ! hasChanged } onClick = { onSubmit } >
2026-01-10 19:45:02 +01:00
{ t ` Save ` }
2025-12-07 03:32:28 +01:00
< / Button >
< / Stack >
< / Stack >
2025-07-20 17:37:39 +02:00
) ;
} ;