Feature/modify global update settings (#432)
* Move setting components in "settings" folder * Add setting to change global update automation
This commit is contained in:
@@ -9,8 +9,9 @@
|
|||||||
import List from '@mui/material/List';
|
import List from '@mui/material/List';
|
||||||
import ListSubheader from '@mui/material/ListSubheader';
|
import ListSubheader from '@mui/material/ListSubheader';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { GlobalUpdateSettingsCategories } from '@/components/globalUpdate/GlobalUpdateSettingsCategories.tsx';
|
import { GlobalUpdateSettingsCategories } from '@/components/settings/globalUpdate/GlobalUpdateSettingsCategories.tsx';
|
||||||
import { GlobalUpdateSettingsEntries } from '@/components/globalUpdate/GlobalUpdateSettingsEntries.tsx';
|
import { GlobalUpdateSettingsEntries } from '@/components/settings/globalUpdate/GlobalUpdateSettingsEntries.tsx';
|
||||||
|
import { GlobalUpdateSettingsInterval } from '@/components/settings/globalUpdate/GlobalUpdateSettingsInterval.tsx';
|
||||||
|
|
||||||
export const GlobalUpdateSettings = () => {
|
export const GlobalUpdateSettings = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -23,6 +24,7 @@ export const GlobalUpdateSettings = () => {
|
|||||||
</ListSubheader>
|
</ListSubheader>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<GlobalUpdateSettingsInterval />
|
||||||
<GlobalUpdateSettingsEntries />
|
<GlobalUpdateSettingsEntries />
|
||||||
<GlobalUpdateSettingsCategories />
|
<GlobalUpdateSettingsCategories />
|
||||||
</List>
|
</List>
|
||||||
@@ -22,7 +22,7 @@ import { makeToast } from '@/components/util/Toast.tsx';
|
|||||||
import { IncludeInUpdate } from '@/lib/graphql/generated/graphql.ts';
|
import { IncludeInUpdate } from '@/lib/graphql/generated/graphql.ts';
|
||||||
import { TCategory } from '@/typings.ts';
|
import { TCategory } from '@/typings.ts';
|
||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { CheckboxContainer } from '@/components/globalUpdate/CheckboxContainer.ts';
|
import { CheckboxContainer } from '@/components/settings/globalUpdate/CheckboxContainer.ts';
|
||||||
|
|
||||||
const booleanToIncludeInStatus = (status: boolean | null | undefined): IncludeInUpdate => {
|
const booleanToIncludeInStatus = (status: boolean | null | undefined): IncludeInUpdate => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -12,15 +12,14 @@ import { useEffect, useState } from 'react';
|
|||||||
import ListItemButton from '@mui/material/ListItemButton';
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
import ListItemText from '@mui/material/ListItemText';
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
|
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
|
||||||
import { GetServerSettingsQuery } from '@/lib/graphql/generated/graphql.ts';
|
import { ServerSettings, TranslationKey } from '@/typings.ts';
|
||||||
import { TranslationKey } from '@/typings.ts';
|
|
||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { makeToast } from '@/components/util/Toast.tsx';
|
import { makeToast } from '@/components/util/Toast.tsx';
|
||||||
import { CheckboxContainer } from '@/components/globalUpdate/CheckboxContainer';
|
import { CheckboxContainer } from '@/components/settings/globalUpdate/CheckboxContainer.ts';
|
||||||
import { CheckboxInput } from '@/components/atoms/CheckboxInput';
|
import { CheckboxInput } from '@/components/atoms/CheckboxInput.tsx';
|
||||||
|
|
||||||
type GlobalUpdateSkipEntriesSettings = Pick<
|
type GlobalUpdateSkipEntriesSettings = Pick<
|
||||||
GetServerSettingsQuery['settings'],
|
ServerSettings,
|
||||||
'excludeUnreadChapters' | 'excludeNotStarted' | 'excludeCompleted'
|
'excludeUnreadChapters' | 'excludeNotStarted' | 'excludeCompleted'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -61,9 +60,7 @@ const getSkipMangasText = (settings: GlobalUpdateSkipEntriesSettings | undefined
|
|||||||
return skipSettings.join(', ');
|
return skipSettings.join(', ');
|
||||||
};
|
};
|
||||||
|
|
||||||
const extractSkipEntriesSettings = (
|
const extractSkipEntriesSettings = (serverSettings: ServerSettings): GlobalUpdateSkipEntriesSettings => ({
|
||||||
serverSettings: GetServerSettingsQuery['settings'],
|
|
||||||
): GlobalUpdateSkipEntriesSettings => ({
|
|
||||||
excludeCompleted: serverSettings.excludeCompleted,
|
excludeCompleted: serverSettings.excludeCompleted,
|
||||||
excludeNotStarted: serverSettings.excludeNotStarted,
|
excludeNotStarted: serverSettings.excludeNotStarted,
|
||||||
excludeUnreadChapters: serverSettings.excludeUnreadChapters,
|
excludeUnreadChapters: serverSettings.excludeUnreadChapters,
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* 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 { InputAdornment, List, ListItem, ListItemText, Switch } from '@mui/material';
|
||||||
|
import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
|
||||||
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
|
||||||
|
const DEFAULT_INTERVAL_HOURS = 12;
|
||||||
|
const MIN_INTERVAL_HOURS = 6;
|
||||||
|
|
||||||
|
export const GlobalUpdateSettingsInterval = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { data } = requestManager.useGetServerSettings();
|
||||||
|
const autoUpdateIntervalHours = data?.settings.globalUpdateInterval ?? 0;
|
||||||
|
const doAutoUpdates = !!autoUpdateIntervalHours;
|
||||||
|
const [mutateSettings] = requestManager.useUpdateServerSettings();
|
||||||
|
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
const [dialogUpdateIntervalHours, setDialogUpdateIntervalHours] = useState(autoUpdateIntervalHours);
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSetting = (globalUpdateInterval: number) => {
|
||||||
|
closeDialog();
|
||||||
|
|
||||||
|
const didIntervalChange = autoUpdateIntervalHours !== globalUpdateInterval;
|
||||||
|
if (!didIntervalChange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutateSettings({ variables: { input: { settings: { globalUpdateInterval } } } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const setDoAutoUpdates = (enable: boolean) => {
|
||||||
|
const globalUpdateInterval = enable ? DEFAULT_INTERVAL_HOURS : 0;
|
||||||
|
updateSetting(globalUpdateInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDialogUpdateIntervalHours(autoUpdateIntervalHours);
|
||||||
|
}, [autoUpdateIntervalHours]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText primary={t('library.settings.global_update.auto_update.label.title')} />
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<Switch edge="end" checked={doAutoUpdates} onChange={(e) => setDoAutoUpdates(e.target.checked)} />
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
</ListItem>
|
||||||
|
{doAutoUpdates ? (
|
||||||
|
<>
|
||||||
|
<ListItemButton onClick={() => setIsDialogOpen(true)}>
|
||||||
|
<ListItemText
|
||||||
|
primary={t('library.settings.global_update.auto_update.interval.label.title')}
|
||||||
|
secondary={t('library.settings.global_update.auto_update.interval.label.value', {
|
||||||
|
hours: autoUpdateIntervalHours,
|
||||||
|
})}
|
||||||
|
secondaryTypographyProps={{ style: { display: 'flex', flexDirection: 'column' } }}
|
||||||
|
/>
|
||||||
|
</ListItemButton>
|
||||||
|
|
||||||
|
<Dialog open={isDialogOpen} onClose={closeDialog}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogTitle sx={{ paddingLeft: 0 }}>
|
||||||
|
{t('library.settings.global_update.auto_update.interval.label.title')}
|
||||||
|
</DialogTitle>
|
||||||
|
<TextField
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
margin: 'auto',
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
inputProps: { min: MIN_INTERVAL_HOURS },
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">{t('global.time.hour_short')}</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
autoFocus
|
||||||
|
value={dialogUpdateIntervalHours}
|
||||||
|
type="number"
|
||||||
|
onChange={(e) => setDialogUpdateIntervalHours(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={closeDialog} color="primary">
|
||||||
|
{t('global.button.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
updateSetting(dialogUpdateIntervalHours);
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{t('global.button.ok')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
2
src/i18n/i18next.d.ts
vendored
2
src/i18n/i18next.d.ts
vendored
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import 'i18next';
|
import 'i18next';
|
||||||
import resources from '@/i18n/translations';
|
import { resources } from '@/i18n/translations';
|
||||||
|
|
||||||
declare module 'i18next' {
|
declare module 'i18next' {
|
||||||
interface CustomTypeOptions {
|
interface CustomTypeOptions {
|
||||||
|
|||||||
@@ -206,6 +206,9 @@
|
|||||||
"start": "Start",
|
"start": "Start",
|
||||||
"submit": "Submit"
|
"submit": "Submit"
|
||||||
},
|
},
|
||||||
|
"time": {
|
||||||
|
"hour_short": "h"
|
||||||
|
},
|
||||||
"date": {
|
"date": {
|
||||||
"label": {
|
"label": {
|
||||||
"today": "Today",
|
"today": "Today",
|
||||||
@@ -321,6 +324,17 @@
|
|||||||
"unread_chapters": "With unread chapter(s)"
|
"unread_chapters": "With unread chapter(s)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"auto_update": {
|
||||||
|
"interval": {
|
||||||
|
"label": {
|
||||||
|
"value": "{{hours}}$t(global.time.hour_short)",
|
||||||
|
"title": "Automatic update interval"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"title": "Automatic updates"
|
||||||
|
}
|
||||||
|
},
|
||||||
"title": "Global update"
|
"title": "Global update"
|
||||||
},
|
},
|
||||||
"title": "Library Settings"
|
"title": "Library Settings"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useContext, useEffect } from 'react';
|
import { useContext, useEffect } from 'react';
|
||||||
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
||||||
import { GlobalUpdateSettings } from '@/components/globalUpdate/GlobalUpdateSettings.tsx';
|
import { GlobalUpdateSettings } from '@/components/settings/globalUpdate/GlobalUpdateSettings.tsx';
|
||||||
import { SearchSettings } from '@/screens/settings/SearchSettings.tsx';
|
import { SearchSettings } from '@/screens/settings/SearchSettings.tsx';
|
||||||
|
|
||||||
export function LibrarySettings() {
|
export function LibrarySettings() {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
GetChapterQuery,
|
GetChapterQuery,
|
||||||
GetExtensionQuery,
|
GetExtensionQuery,
|
||||||
GetMangaQuery,
|
GetMangaQuery,
|
||||||
|
GetServerSettingsQuery,
|
||||||
GetSourceQuery,
|
GetSourceQuery,
|
||||||
MetaType,
|
MetaType,
|
||||||
SourcePreferenceChangeInput,
|
SourcePreferenceChangeInput,
|
||||||
@@ -376,3 +377,5 @@ export type BackupValidationResult = {
|
|||||||
missingTrackers: string[];
|
missingTrackers: string[];
|
||||||
mangasMissingSources: string[];
|
mangasMissingSources: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ServerSettings = GetServerSettingsQuery['settings'];
|
||||||
|
|||||||
Reference in New Issue
Block a user