Rename folder "modules" to "features"

This commit is contained in:
schroda
2025-08-15 22:02:58 +02:00
parent 7e6ced1d09
commit 1b4bf22542
415 changed files with 1859 additions and 1852 deletions

View File

@@ -0,0 +1,77 @@
/*
* 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 List from '@mui/material/List';
import ListSubheader from '@mui/material/ListSubheader';
import { useTranslation } from 'react-i18next';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Switch from '@mui/material/Switch';
import {
CategoriesInclusionSetting,
CategoriesInclusionSettingProps,
} from '@/features/category/components/CategoriesInclusionSetting.tsx';
import { GlobalUpdateSettingsEntries } from '@/features/settings/components/globalUpdate/GlobalUpdateSettingsEntries.tsx';
import { GlobalUpdateSettingsInterval } from '@/features/settings/components/globalUpdate/GlobalUpdateSettingsInterval.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { makeToast } from '@/features/core/utils/Toast.ts';
import { LibrarySettingsType, ServerSettings } from '@/features/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const GlobalUpdateSettings = ({
serverSettings,
categories,
}: {
serverSettings: ServerSettings;
categories: CategoriesInclusionSettingProps['categories'];
}) => {
const { t } = useTranslation();
const { updateMangas } = serverSettings;
const [mutateSettings] = requestManager.useUpdateServerSettings();
const updateSetting = async <Setting extends keyof LibrarySettingsType>(
setting: Setting,
value: LibrarySettingsType[Setting],
) => {
try {
await mutateSettings({ variables: { input: { settings: { [setting]: value } } } });
} catch (e) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e));
}
};
return (
<List
subheader={
<ListSubheader component="div" id="global-update-settings">
{t('library.settings.global_update.title')}
</ListSubheader>
}
>
<GlobalUpdateSettingsInterval globalUpdateInterval={serverSettings.globalUpdateInterval} />
<GlobalUpdateSettingsEntries serverSettings={serverSettings} />
<CategoriesInclusionSetting
categories={categories}
includeField="includeInUpdate"
dialogText={t('library.settings.global_update.categories.label.info')}
/>
<ListItem>
<ListItemText
primary={t('library.settings.global_update.metadata.label.title')}
secondary={t('library.settings.global_update.metadata.label.description')}
/>
<Switch
edge="end"
checked={updateMangas}
onChange={(e) => updateSetting('updateMangas', e.target.checked)}
/>
</ListItem>
</List>
);
};

View File

@@ -0,0 +1,149 @@
/*
* 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 { t as translate } from 'i18next';
import { useTranslation } from 'react-i18next';
import { useEffect, useState } from 'react';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { makeToast } from '@/features/core/utils/Toast.ts';
import { CheckboxContainer } from '@/features/core/components/inputs/CheckboxContainer.ts';
import { CheckboxInput } from '@/features/core/components/inputs/CheckboxInput.tsx';
import { GlobalUpdateSkipEntriesSettings, ServerSettings } from '@/features/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import { GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION } from '@/features/settings/Settings.constants.ts';
const getSkipMangasText = (settings: GlobalUpdateSkipEntriesSettings) => {
const skipSettings: string[] = [];
if (settings.excludeUnreadChapters) {
skipSettings.push(translate(GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION.excludeUnreadChapters) as string);
}
if (settings.excludeNotStarted) {
skipSettings.push(translate(GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION.excludeNotStarted) as string);
}
if (settings.excludeCompleted) {
skipSettings.push(translate(GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION.excludeCompleted) as string);
}
const isNothingExcluded = !skipSettings.length;
if (isNothingExcluded) {
skipSettings.push(translate('global.label.none'));
}
return skipSettings.join(', ');
};
const extractSkipEntriesSettings = (serverSettings: ServerSettings): GlobalUpdateSkipEntriesSettings => ({
excludeCompleted: serverSettings.excludeCompleted,
excludeNotStarted: serverSettings.excludeNotStarted,
excludeUnreadChapters: serverSettings.excludeUnreadChapters,
});
export const GlobalUpdateSettingsEntries = ({ serverSettings }: { serverSettings: ServerSettings }) => {
const { t } = useTranslation();
const globalUpdateSettings = extractSkipEntriesSettings(serverSettings);
const [mutateSettings] = requestManager.useUpdateServerSettings();
const [dialogSettings, setDialogSettings] = useState<GlobalUpdateSkipEntriesSettings>(
globalUpdateSettings ?? ({} as GlobalUpdateSkipEntriesSettings),
);
const [isDialogOpen, setIsDialogOpen] = useState(false);
const skipEntriesText = getSkipMangasText(globalUpdateSettings);
const updateSettings = async () => {
const didSettingsChange =
globalUpdateSettings?.excludeCompleted !== dialogSettings.excludeCompleted ||
globalUpdateSettings.excludeNotStarted !== dialogSettings.excludeNotStarted ||
globalUpdateSettings.excludeUnreadChapters !== dialogSettings.excludeUnreadChapters;
setIsDialogOpen(false);
if (!didSettingsChange) {
return;
}
try {
await mutateSettings({ variables: { input: { settings: dialogSettings } } });
} catch (e) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e));
}
};
const closeDialog = () => {
setDialogSettings(globalUpdateSettings ?? ({} as GlobalUpdateSkipEntriesSettings));
setIsDialogOpen(false);
};
useEffect(() => {
if (!globalUpdateSettings) {
return;
}
setDialogSettings(globalUpdateSettings);
}, [
globalUpdateSettings?.excludeCompleted,
globalUpdateSettings?.excludeNotStarted,
globalUpdateSettings?.excludeUnreadChapters,
]);
return (
<>
<ListItemButton onClick={() => setIsDialogOpen(true)}>
<ListItemText
primary={t('library.settings.global_update.entries.title')}
secondary={skipEntriesText}
onClick={() => setIsDialogOpen(true)}
/>
</ListItemButton>
<Dialog open={isDialogOpen} onClose={closeDialog}>
<DialogTitle>{t('library.settings.global_update.entries.title')}</DialogTitle>
<DialogContent>
<CheckboxContainer>
{Object.entries(dialogSettings).map(([setting, value]) => (
<CheckboxInput
key={setting}
label={t(
GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION[
setting as keyof GlobalUpdateSkipEntriesSettings
],
)}
checked={value}
onChange={(_, checked) => {
setDialogSettings({
...dialogSettings,
[setting]: checked,
});
}}
/>
))}
</CheckboxContainer>
</DialogContent>
<DialogActions>
<Button onClick={closeDialog} color="primary">
{t('global.button.cancel')}
</Button>
<Button onClick={updateSettings} color="primary">
{t('global.button.ok')}
</Button>
</DialogActions>
</Dialog>
</>
);
};

View File

@@ -0,0 +1,76 @@
/*
* 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 { useTranslation } from 'react-i18next';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Switch from '@mui/material/Switch';
import { useCallback } from 'react';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { NumberSetting } from '@/features/core/components/settings/NumberSetting.tsx';
import { getPersistedServerSetting, usePersistedValue } from '@/features/core/hooks/usePersistedValue.tsx';
import { ServerSettings } from '@/features/settings/Settings.types.ts';
import { GLOBAL_UPDATE_INTERVAL } from '@/features/settings/Settings.constants.ts';
export const GlobalUpdateSettingsInterval = ({
globalUpdateInterval,
}: {
globalUpdateInterval: ServerSettings['globalUpdateInterval'];
}) => {
const { t } = useTranslation();
const autoUpdateIntervalHours = globalUpdateInterval;
const doAutoUpdates = !!autoUpdateIntervalHours;
const [mutateSettings] = requestManager.useUpdateServerSettings();
const [currentAutoUpdateIntervalHours, persistAutoUpdateIntervalHours] = usePersistedValue(
'lastGlobalUpdateInterval',
GLOBAL_UPDATE_INTERVAL.default,
autoUpdateIntervalHours,
getPersistedServerSetting,
);
const updateSetting = useCallback(
(newGlobalUpdateInterval: number) => {
persistAutoUpdateIntervalHours(
newGlobalUpdateInterval === 0 ? currentAutoUpdateIntervalHours : newGlobalUpdateInterval,
);
mutateSettings({ variables: { input: { settings: { globalUpdateInterval: newGlobalUpdateInterval } } } });
},
[currentAutoUpdateIntervalHours],
);
const setDoAutoUpdates = (enable: boolean) => {
const newGlobalUpdateInterval = enable ? currentAutoUpdateIntervalHours : 0;
updateSetting(newGlobalUpdateInterval);
};
return (
<List>
<ListItem>
<ListItemText primary={t('library.settings.global_update.auto_update.label.title')} />
<Switch edge="end" checked={doAutoUpdates} onChange={(e) => setDoAutoUpdates(e.target.checked)} />
</ListItem>
<NumberSetting
settingTitle={t('library.settings.global_update.auto_update.interval.label.title')}
settingValue={t('library.settings.global_update.auto_update.interval.label.value', {
hours: currentAutoUpdateIntervalHours,
})}
value={currentAutoUpdateIntervalHours}
minValue={GLOBAL_UPDATE_INTERVAL.min}
maxValue={GLOBAL_UPDATE_INTERVAL.max}
defaultValue={GLOBAL_UPDATE_INTERVAL.default}
showSlider
valueUnit={t('global.time.hour_short')}
handleUpdate={updateSetting}
disabled={!doAutoUpdates}
/>
</List>
);
};

View File

@@ -0,0 +1,85 @@
/*
* 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 { useTranslation } from 'react-i18next';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Switch from '@mui/material/Switch';
import { useCallback } from 'react';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { NumberSetting } from '@/features/core/components/settings/NumberSetting.tsx';
import { getPersistedServerSetting, usePersistedValue } from '@/features/core/hooks/usePersistedValue.tsx';
import { makeToast } from '@/features/core/utils/Toast.ts';
import { ServerSettings } from '@/features/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import { WEB_UI_UPDATE_INTERVAL } from '@/features/settings/Settings.constants.ts';
export const WebUIUpdateIntervalSetting = ({
disabled = false,
updateCheckInterval,
}: {
disabled?: boolean;
updateCheckInterval: ServerSettings['webUIUpdateCheckInterval'];
}) => {
const { t } = useTranslation();
const shouldAutoUpdate = !!updateCheckInterval;
const [mutateSettings] = requestManager.useUpdateServerSettings();
const [currentUpdateCheckInterval, persistUpdateCheckInterval] = usePersistedValue(
'lastUpdateCheckInterval',
WEB_UI_UPDATE_INTERVAL.default,
updateCheckInterval,
getPersistedServerSetting,
);
const updateSetting = useCallback(
(webUIUpdateCheckInterval: number) => {
persistUpdateCheckInterval(
webUIUpdateCheckInterval === 0 ? currentUpdateCheckInterval : webUIUpdateCheckInterval,
);
mutateSettings({ variables: { input: { settings: { webUIUpdateCheckInterval } } } }).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
},
[currentUpdateCheckInterval],
);
const setDoAutoUpdates = (enable: boolean) => {
const globalUpdateInterval = enable ? currentUpdateCheckInterval : 0;
updateSetting(globalUpdateInterval);
};
return (
<List>
<ListItem>
<ListItemText primary={t('settings.webui.auto_update.label.title')} />
<Switch
disabled={disabled}
edge="end"
checked={shouldAutoUpdate}
onChange={(e) => setDoAutoUpdates(e.target.checked)}
/>
</ListItem>
<NumberSetting
settingTitle={t('settings.webui.auto_update.label.interval')}
settingValue={t('library.settings.global_update.auto_update.interval.label.value', {
hours: currentUpdateCheckInterval,
})}
value={currentUpdateCheckInterval}
minValue={WEB_UI_UPDATE_INTERVAL.min}
maxValue={WEB_UI_UPDATE_INTERVAL.max}
defaultValue={WEB_UI_UPDATE_INTERVAL.default}
showSlider
valueUnit={t('global.time.hour_short')}
handleUpdate={updateSetting}
disabled={disabled || !shouldAutoUpdate}
/>
</List>
);
};