Add sync settings

This commit is contained in:
schroda
2026-06-30 15:08:17 +02:00
parent 43fef26b89
commit 816409fcb8
14 changed files with 612 additions and 24 deletions

View File

@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Migration**) Add option to abort entries that are searching or are waiting to get migrated
- (**Migration**) Add "in library" indicator
- (**Settings/WebView**) Add setting to enable/disable WebView
- (**Settings/Sync**) Add sync settings
### Changed

View File

@@ -13,6 +13,7 @@ import type {
BackupFlagInclusionState,
} from '@/features/backup/Backup.types.ts';
import { BACKUP_FLAGS_TO_TRANSLATION } from '@/features/backup/Backup.constants.ts';
import omit from 'lodash/fp/omit';
export const convertToAutoBackupFlags = (flags: BackupFlagInclusionState): AutoBackupFlagInclusionState => ({
autoBackupIncludeCategories: flags.includeCategories,
@@ -46,11 +47,14 @@ const getIncludeExcludeText = (count: number, allCount: number, specificText: st
return specificText;
};
export const getAutoBackupFlagsInfo = (autoFlags: AutoBackupFlagInclusionState): Record<`${boolean}`, string> => {
const flags = convertToBackupFlags(autoFlags);
export const getAutoBackupFlagsInfo = (
autoFlags: AutoBackupFlagInclusionState,
hiddenFlags: BackupFlag[] = [],
): Record<`${boolean}`, string> => {
const flags = omit(hiddenFlags, convertToBackupFlags(autoFlags));
const totalFlags = Object.keys(flags).length;
const flagsByState = Object.groupBy(Object.entries(flags), ([, value]) => value.toString());
const flagsByState = Object.groupBy(Object.entries(flags), ([, value]) => (value as boolean).toString());
const includedFlagsString =
flagsByState.true?.map(([key]) => t(BACKUP_FLAGS_TO_TRANSLATION[key as BackupFlag])).join(', ') ?? '';

View File

@@ -24,7 +24,7 @@ import {
BACKUP_FLAGS_BY_GROUP,
BACKUP_FLAGS_TO_TRANSLATION,
} from '@/features/backup/Backup.constants.ts';
import type { BackupFlagGroup, BackupFlagInclusionState } from '@/features/backup/Backup.types.ts';
import type { BackupFlag, BackupFlagGroup, BackupFlagInclusionState } from '@/features/backup/Backup.types.ts';
export const BackupFlagInclusionDialog = ({
onDismiss,
@@ -33,11 +33,18 @@ export const BackupFlagInclusionDialog = ({
onExitComplete,
title,
flags,
}: AwaitableComponentProps<BackupFlagInclusionState> & { title: string; flags?: BackupFlagInclusionState }) => {
hiddenFlags,
}: AwaitableComponentProps<BackupFlagInclusionState> & {
title: string;
flags?: BackupFlagInclusionState;
hiddenFlags?: BackupFlag[];
}) => {
const { t } = useLingui();
const [includeStateByFlag, setIncludeStateByFlag] = useState(
Object.fromEntries(BACKUP_FLAGS.map((flag) => [flag, flags?.[flag] ?? true])) as BackupFlagInclusionState,
Object.fromEntries(
BACKUP_FLAGS.map((flag) => [flag, flags?.[flag] ?? !hiddenFlags?.includes(flag)]),
) as BackupFlagInclusionState,
);
return (
@@ -45,24 +52,28 @@ export const BackupFlagInclusionDialog = ({
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<FormGroup sx={{ gap: 2 }}>
{Object.entries(BACKUP_FLAGS_BY_GROUP).map(([group, groupFlags]) => (
<Stack key={group}>
<Typography>{t(BACKUP_FLAG_GROUP_TO_TRANSLATION[group as BackupFlagGroup])}</Typography>
{groupFlags.map((flag) => (
<CheckboxInput
key={flag}
label={t(BACKUP_FLAGS_TO_TRANSLATION[flag])}
checked={includeStateByFlag[flag]}
onChange={(_, checked) => {
setIncludeStateByFlag({
...includeStateByFlag,
[flag]: checked,
});
}}
/>
))}
</Stack>
))}
{Object.entries(BACKUP_FLAGS_BY_GROUP)
.filter(([_group, groupFlags]) => groupFlags.some((flag) => !hiddenFlags?.includes(flag)))
.map(([group, groupFlags]) => (
<Stack key={group}>
<Typography>{t(BACKUP_FLAG_GROUP_TO_TRANSLATION[group as BackupFlagGroup])}</Typography>
{groupFlags
.filter((flag) => !hiddenFlags?.includes(flag))
.map((flag) => (
<CheckboxInput
key={flag}
label={t(BACKUP_FLAGS_TO_TRANSLATION[flag])}
checked={includeStateByFlag[flag]}
onChange={(_, checked) => {
setIncludeStateByFlag({
...includeStateByFlag,
[flag]: checked,
});
}}
/>
))}
</Stack>
))}
</FormGroup>
</DialogContent>
<DialogActions>

View File

@@ -30,6 +30,7 @@ import {
} from '@/lib/graphql/generated/graphql-base.types.ts';
import { ThemeMode } from '@/features/theme/AppTheme.types.ts';
import { getPreferredISOLanguageCodes } from '@/lib/ISOLanguageUtil.ts';
import type { BackupFlag } from '@/features/backup/Backup.types.ts';
export const MANGA_GRID_WIDTH = {
min: 100,
@@ -317,3 +318,45 @@ export const IMAGE_PROCESSING_TYPE_TO_SETTING: Record<
[ImageProcessingType.DOWNLOAD]: 'downloadConversions',
[ImageProcessingType.SERVE]: 'serveConversions',
};
const SYNC_INTERVAL_TO_TRANSLATION: Record<string, SelectSettingValueDisplayInfo> = {
[d(0).seconds.asWholeMinutes.toISOString()]: {
text: msg`Disabled`,
},
[d(30).minutes.asWholeMinutes.toISOString()]: {
text: msg`Every 30 minutes`,
},
[d(1).hours.asWholeMinutes.toISOString()]: {
text: msg`Every hour`,
},
[d(3).hours.asWholeMinutes.toISOString()]: {
text: msg`Every 3 hours`,
},
[d(6).hours.asWholeMinutes.toISOString()]: {
text: msg`Every 6 hours`,
},
[d(12).hours.asWholeMinutes.toISOString()]: {
text: msg`Every 12 hours`,
},
[d(1).days.asWholeMinutes.toISOString()]: {
text: msg`Daily`,
},
[d(7).days.asWholeMinutes.toISOString()]: {
text: msg`Weekly`,
},
};
export const SYNC_INTERVAL_CUSTOM_VALUE = d(10).days.asWholeMinutes.toISOString();
export const SYNC_INTERVAL_VALUES = Object.keys(SYNC_INTERVAL_TO_TRANSLATION);
export const SYNC_INTERVAL_SELECT_VALUES: SelectSettingValue<string>[] = SYNC_INTERVAL_VALUES.map((value) => [
value,
SYNC_INTERVAL_TO_TRANSLATION[value],
]);
export const SYNC_INTERVAL_SELECT_VALUES_WITH_CUSTOM: SelectSettingValue<string>[] = [
...SYNC_INTERVAL_SELECT_VALUES,
[SYNC_INTERVAL_CUSTOM_VALUE, { text: msg`Custom` }],
];
export const SYNC_SETTINGS_HIDDEN_BACKUP_FLAGS = [
'includeServerSettings',
'includeClientData',
] as const satisfies BackupFlag[];

View File

@@ -31,17 +31,46 @@ import { makeToast } from '@/base/utils/Toast.ts';
import type { MetadataUpdateSettings } from '@/features/app-updates/AppUpdateChecker.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
import type { PartialSettingsTypeInput } from '@/lib/graphql/generated/graphql-base.types.ts';
import { AuthMode, CbzMediaType, DatabaseType, SortOrder } from '@/lib/graphql/generated/graphql-base.types.ts';
import {
AUTH_MODES_SELECT_VALUES,
JWT_ACCESS_TOKEN_EXPIRY,
JWT_REFRESH_TOKEN_EXPIRY,
SYNC_INTERVAL_CUSTOM_VALUE,
SYNC_INTERVAL_SELECT_VALUES,
SYNC_INTERVAL_SELECT_VALUES_WITH_CUSTOM,
SYNC_INTERVAL_VALUES,
SYNC_SETTINGS_HIDDEN_BACKUP_FLAGS,
} from '@/features/settings/Settings.constants.ts';
import { ServerAddressSetting } from '@/features/settings/components/ServerAddressSetting.tsx';
import { AuthManager } from '@/features/authentication/AuthManager.ts';
import type { ServerSettings as ServerSettingsType } from '@/features/settings/Settings.types.ts';
import { KoreaderSyncSettings } from '@/features/settings/components/koreaderSync/KoreaderSyncSettings.tsx';
import { Confirmation } from '@/base/AppAwaitableComponent.ts';
import { BackupFlagInclusionDialog } from '@/features/backup/component/BackupFlagInclusionDialog.tsx';
import ListItemButton from '@mui/material/ListItemButton';
import { AwaitableComponent } from 'awaitable-component';
import { getAutoBackupFlagsInfo } from '@/features/backup/Backup.utils.ts';
import type { BackupFlagInclusionState } from '@/features/backup/Backup.types.ts';
const convertSyncDataToBackupFlags = (settings: ServerSettingsType): BackupFlagInclusionState => ({
includeManga: settings.syncDataManga,
includeChapters: settings.syncDataChapters,
includeCategories: settings.syncDataCategories,
includeHistory: settings.syncDataHistory,
includeTracking: settings.syncDataTracking,
includeClientData: false,
includeServerSettings: false,
});
const convertBackupFlagsToSyncData = (flags: BackupFlagInclusionState): PartialSettingsTypeInput => ({
syncDataManga: flags.includeManga,
syncDataChapters: flags.includeChapters,
syncDataCategories: flags.includeCategories,
syncDataHistory: flags.includeHistory,
syncDataTracking: flags.includeTracking,
});
const getLogFilesCleanupDisplayValue = (ttl: number): string => {
if (ttl === 0) {
@@ -91,6 +120,15 @@ export const ServerSettings = () => {
onCompletion?.(false);
}
};
const updateSettings = async (settings: PartialSettingsTypeInput, onCompletion?: (success: boolean) => void) => {
try {
await mutateSettings({ variables: { input: { settings } } });
onCompletion?.(true);
} catch (e) {
makeToast(t`Failed to save changes`, 'error', getErrorMessage(e));
onCompletion?.(false);
}
};
const localSettings = useMemo(
() => (
@@ -176,6 +214,13 @@ export const ServerSettings = () => {
const authModeDisabled = !serverSettings.authUsername?.trim() || !serverSettings.authPassword?.trim();
const isH2Database = serverSettings.databaseType === DatabaseType.H2;
const isCustomSyncInterval = !SYNC_INTERVAL_VALUES.includes(
d(serverSettings.syncInterval).minutes.asWholeMinutes.toISOString(),
);
const syncDataFlagsInfo = getAutoBackupFlagsInfo(serverSettings);
const includedSyncDataText = syncDataFlagsInfo.true;
const excludedSyncDataText = syncDataFlagsInfo.false;
return (
<List sx={{ pt: 0 }}>
{localSettings}
@@ -669,6 +714,81 @@ export const ServerSettings = () => {
/>
</ListItem>
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-sync">
{t`Sync`}
</ListSubheader>
}
>
<ListItemButton
onClick={async () => {
try {
const flags = await AwaitableComponent.show(BackupFlagInclusionDialog, {
title: t`Sync data`,
flags: convertSyncDataToBackupFlags(serverSettings),
hiddenFlags: SYNC_SETTINGS_HIDDEN_BACKUP_FLAGS,
});
await updateSettings(convertBackupFlagsToSyncData(flags));
} catch (e) {
// Ignore
}
}}
>
<ListItemText
primary={t`Backup data`}
secondary={
<>
<span>{t`Include: ${includedSyncDataText}`}</span>
<span>{t`Exclude: ${excludedSyncDataText}`}</span>
</>
}
slotProps={{
secondary: { sx: { display: 'flex', flexDirection: 'column' } },
}}
/>
</ListItemButton>
<SelectSetting
settingName={t`Sync interval`}
value={
isCustomSyncInterval
? SYNC_INTERVAL_CUSTOM_VALUE
: d(serverSettings.syncInterval).minutes.asWholeMinutes.toISOString()
}
values={
isCustomSyncInterval ? SYNC_INTERVAL_SELECT_VALUES_WITH_CUSTOM : SYNC_INTERVAL_SELECT_VALUES
}
handleChange={(syncInterval) => updateSetting('syncInterval', syncInterval)}
/>
<List
subheader={
<ListSubheader component="div" id="server-settings-sync-syncyomi">
{t`SyncYomi`}
</ListSubheader>
}
>
<ListItem>
<ListItemText primary={t`SyncYomi enabled`} />
<Switch
edge="end"
checked={serverSettings.syncYomiEnabled}
onChange={(e) => updateSetting('syncYomiEnabled', e.target.checked)}
/>
</ListItem>
<TextSetting
settingName={t`SyncYomi host`}
value={serverSettings.syncYomiHost}
handleChange={(host) => updateSetting('syncYomiHost', host)}
/>
<TextSetting
settingName={t`SyncYomi API key`}
value={serverSettings.syncYomiApiKey}
handleChange={(apiKey) => updateSetting('syncYomiApiKey', apiKey)}
isPassword
/>
</List>
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-misc">

View File

@@ -567,6 +567,7 @@ msgid "Backup cleanup"
msgstr "Backup cleanup"
#: src/features/backup/screens/Backup.tsx
#: src/features/settings/screens/ServerSettings.tsx
msgid "Backup data"
msgstr "Backup data"
@@ -1239,6 +1240,10 @@ msgstr "Cycle reading direction"
msgid "Cycle reading mode"
msgstr "Cycle reading mode"
#: src/features/settings/Settings.constants.ts
msgid "Daily"
msgstr "Daily"
#: src/features/settings/screens/Appearance.tsx
msgid "Dark"
msgstr "Dark"
@@ -1535,6 +1540,10 @@ msgstr "Enable debug logs"
msgid "Enable page read progress"
msgstr "Enable page read progress"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Enable the WebView via CEF (Chromium)"
msgstr "Enable the WebView via CEF (Chromium)"
#: src/features/downloads/screens/DownloadSettings.tsx
msgid "Entries in excluded categories will not be downloaded even if they are also in included categories"
msgstr "Entries in excluded categories will not be downloaded even if they are also in included categories"
@@ -1560,6 +1569,26 @@ msgstr "Entry \"{id}\" not found"
msgid "Error"
msgstr "Error"
#: src/features/settings/Settings.constants.ts
msgid "Every 12 hours"
msgstr "Every 12 hours"
#: src/features/settings/Settings.constants.ts
msgid "Every 3 hours"
msgstr "Every 3 hours"
#: src/features/settings/Settings.constants.ts
msgid "Every 30 minutes"
msgstr "Every 30 minutes"
#: src/features/settings/Settings.constants.ts
msgid "Every 6 hours"
msgstr "Every 6 hours"
#: src/features/settings/Settings.constants.ts
msgid "Every hour"
msgstr "Every hour"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Example for possible values: 1 (bytes), 1KB (kilobytes), 1MB (megabytes), 1GB (gigabytes)"
msgstr "Example for possible values: 1 (bytes), 1KB (kilobytes), 1MB (megabytes), 1GB (gigabytes)"
@@ -1577,6 +1606,10 @@ msgstr "Exclude scanlators"
msgid "Exclude: {excludedCategoriesText}"
msgstr "Exclude: {excludedCategoriesText}"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Exclude: {excludedSyncDataText}"
msgstr "Exclude: {excludedSyncDataText}"
#: src/features/migration/Migration.constants.ts
msgid "Excluded"
msgstr "Excluded"
@@ -1959,6 +1992,10 @@ msgstr "Include"
msgid "Include: {includedCategoriesText}"
msgstr "Include: {includedCategoriesText}"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Include: {includedSyncDataText}"
msgstr "Include: {includedSyncDataText}"
#: src/features/reader/hotkeys/settings/components/ReaderSettingHotkey.tsx
msgid "Increase auto scroll speed"
msgstr "Increase auto scroll speed"
@@ -3476,6 +3513,18 @@ msgstr "Successfully migrated"
msgid "Successfully migrated manga"
msgstr "Successfully migrated manga"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Sync"
msgstr "Sync"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Sync data"
msgstr "Sync data"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Sync interval"
msgstr "Sync interval"
#: src/features/settings/components/koreaderSync/KoreaderSyncSettings.tsx
msgid "Sync status"
msgstr "Sync status"
@@ -3492,6 +3541,22 @@ msgstr "Sync to an older state"
msgid "Sync will not be triggered if the progress difference is within this tolerance."
msgstr "Sync will not be triggered if the progress difference is within this tolerance."
#: src/features/settings/screens/ServerSettings.tsx
msgid "SyncYomi"
msgstr "SyncYomi"
#: src/features/settings/screens/ServerSettings.tsx
msgid "SyncYomi API key"
msgstr "SyncYomi API key"
#: src/features/settings/screens/ServerSettings.tsx
msgid "SyncYomi enabled"
msgstr "SyncYomi enabled"
#: src/features/settings/screens/ServerSettings.tsx
msgid "SyncYomi host"
msgstr "SyncYomi host"
#: src/features/settings/screens/Appearance.tsx
msgid "System"
msgstr "System"
@@ -3973,6 +4038,14 @@ msgstr "WebUI version {0} ({1}) available for download"
msgid "WebUI was updated to version {newVersion} ({0})"
msgstr "WebUI was updated to version {newVersion} ({0})"
#: src/features/settings/screens/ServerSettings.tsx
msgid "WebView"
msgstr "WebView"
#: src/features/settings/Settings.constants.ts
msgid "Weekly"
msgstr "Weekly"
#: src/features/settings/Settings.constants.ts
msgid "When you enable this, you may need to refresh this tab for the login page to appear."
msgstr "When you enable this, you may need to refresh this tab for the login page to appear."

View File

@@ -922,6 +922,7 @@ export type MutationKeySpecifier = (
| 'setSourceMeta'
| 'setSourceMetas'
| 'startDownloader'
| 'startSync'
| 'stopDownloader'
| 'trackProgress'
| 'unbindTrack'
@@ -1000,6 +1001,7 @@ export type MutationFieldPolicy = {
setSourceMeta?: FieldPolicy<any> | FieldReadFunction<any>;
setSourceMetas?: FieldPolicy<any> | FieldReadFunction<any>;
startDownloader?: FieldPolicy<any> | FieldReadFunction<any>;
startSync?: FieldPolicy<any> | FieldReadFunction<any>;
stopDownloader?: FieldPolicy<any> | FieldReadFunction<any>;
trackProgress?: FieldPolicy<any> | FieldReadFunction<any>;
unbindTrack?: FieldPolicy<any> | FieldReadFunction<any>;
@@ -1122,6 +1124,15 @@ export type PartialSettingsTypeKeySpecifier = (
| 'socksProxyPort'
| 'socksProxyUsername'
| 'socksProxyVersion'
| 'syncDataCategories'
| 'syncDataChapters'
| 'syncDataHistory'
| 'syncDataManga'
| 'syncDataTracking'
| 'syncInterval'
| 'syncYomiApiKey'
| 'syncYomiEnabled'
| 'syncYomiHost'
| 'systemTrayEnabled'
| 'updateMangas'
| 'useHikariConnectionPool'
@@ -1211,6 +1222,15 @@ export type PartialSettingsTypeFieldPolicy = {
socksProxyPort?: FieldPolicy<any> | FieldReadFunction<any>;
socksProxyUsername?: FieldPolicy<any> | FieldReadFunction<any>;
socksProxyVersion?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataCategories?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataChapters?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataHistory?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataManga?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataTracking?: FieldPolicy<any> | FieldReadFunction<any>;
syncInterval?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiApiKey?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiEnabled?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiHost?: FieldPolicy<any> | FieldReadFunction<any>;
systemTrayEnabled?: FieldPolicy<any> | FieldReadFunction<any>;
updateMangas?: FieldPolicy<any> | FieldReadFunction<any>;
useHikariConnectionPool?: FieldPolicy<any> | FieldReadFunction<any>;
@@ -1255,6 +1275,7 @@ export type QueryKeySpecifier = (
| 'extensions'
| 'getWebUIUpdateStatus'
| 'koSyncStatus'
| 'lastSyncStatus'
| 'lastUpdateTimestamp'
| 'libraryUpdateStatus'
| 'manga'
@@ -1288,6 +1309,7 @@ export type QueryFieldPolicy = {
extensions?: FieldPolicy<any> | FieldReadFunction<any>;
getWebUIUpdateStatus?: FieldPolicy<any> | FieldReadFunction<any>;
koSyncStatus?: FieldPolicy<any> | FieldReadFunction<any>;
lastSyncStatus?: FieldPolicy<any> | FieldReadFunction<any>;
lastUpdateTimestamp?: FieldPolicy<any> | FieldReadFunction<any>;
libraryUpdateStatus?: FieldPolicy<any> | FieldReadFunction<any>;
manga?: FieldPolicy<any> | FieldReadFunction<any>;
@@ -1509,6 +1531,15 @@ export type SettingsKeySpecifier = (
| 'socksProxyPort'
| 'socksProxyUsername'
| 'socksProxyVersion'
| 'syncDataCategories'
| 'syncDataChapters'
| 'syncDataHistory'
| 'syncDataManga'
| 'syncDataTracking'
| 'syncInterval'
| 'syncYomiApiKey'
| 'syncYomiEnabled'
| 'syncYomiHost'
| 'systemTrayEnabled'
| 'updateMangas'
| 'useHikariConnectionPool'
@@ -1598,6 +1629,15 @@ export type SettingsFieldPolicy = {
socksProxyPort?: FieldPolicy<any> | FieldReadFunction<any>;
socksProxyUsername?: FieldPolicy<any> | FieldReadFunction<any>;
socksProxyVersion?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataCategories?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataChapters?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataHistory?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataManga?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataTracking?: FieldPolicy<any> | FieldReadFunction<any>;
syncInterval?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiApiKey?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiEnabled?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiHost?: FieldPolicy<any> | FieldReadFunction<any>;
systemTrayEnabled?: FieldPolicy<any> | FieldReadFunction<any>;
updateMangas?: FieldPolicy<any> | FieldReadFunction<any>;
useHikariConnectionPool?: FieldPolicy<any> | FieldReadFunction<any>;
@@ -1738,6 +1778,15 @@ export type SettingsTypeKeySpecifier = (
| 'socksProxyPort'
| 'socksProxyUsername'
| 'socksProxyVersion'
| 'syncDataCategories'
| 'syncDataChapters'
| 'syncDataHistory'
| 'syncDataManga'
| 'syncDataTracking'
| 'syncInterval'
| 'syncYomiApiKey'
| 'syncYomiEnabled'
| 'syncYomiHost'
| 'systemTrayEnabled'
| 'updateMangas'
| 'useHikariConnectionPool'
@@ -1827,6 +1876,15 @@ export type SettingsTypeFieldPolicy = {
socksProxyPort?: FieldPolicy<any> | FieldReadFunction<any>;
socksProxyUsername?: FieldPolicy<any> | FieldReadFunction<any>;
socksProxyVersion?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataCategories?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataChapters?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataHistory?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataManga?: FieldPolicy<any> | FieldReadFunction<any>;
syncDataTracking?: FieldPolicy<any> | FieldReadFunction<any>;
syncInterval?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiApiKey?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiEnabled?: FieldPolicy<any> | FieldReadFunction<any>;
syncYomiHost?: FieldPolicy<any> | FieldReadFunction<any>;
systemTrayEnabled?: FieldPolicy<any> | FieldReadFunction<any>;
updateMangas?: FieldPolicy<any> | FieldReadFunction<any>;
useHikariConnectionPool?: FieldPolicy<any> | FieldReadFunction<any>;
@@ -1907,6 +1965,11 @@ export type StartDownloaderPayloadFieldPolicy = {
clientMutationId?: FieldPolicy<any> | FieldReadFunction<any>;
downloadStatus?: FieldPolicy<any> | FieldReadFunction<any>;
};
export type StartSyncPayloadKeySpecifier = ('clientMutationId' | 'result' | StartSyncPayloadKeySpecifier)[];
export type StartSyncPayloadFieldPolicy = {
clientMutationId?: FieldPolicy<any> | FieldReadFunction<any>;
result?: FieldPolicy<any> | FieldReadFunction<any>;
};
export type StopDownloaderPayloadKeySpecifier = (
| 'clientMutationId'
| 'downloadStatus'
@@ -1920,6 +1983,7 @@ export type SubscriptionKeySpecifier = (
| 'downloadChanged'
| 'downloadStatusChanged'
| 'libraryUpdateStatusChanged'
| 'syncStatusChanged'
| 'updateStatusChanged'
| 'webUIUpdateStatusChange'
| SubscriptionKeySpecifier
@@ -1928,6 +1992,7 @@ export type SubscriptionFieldPolicy = {
downloadChanged?: FieldPolicy<any> | FieldReadFunction<any>;
downloadStatusChanged?: FieldPolicy<any> | FieldReadFunction<any>;
libraryUpdateStatusChanged?: FieldPolicy<any> | FieldReadFunction<any>;
syncStatusChanged?: FieldPolicy<any> | FieldReadFunction<any>;
updateStatusChanged?: FieldPolicy<any> | FieldReadFunction<any>;
webUIUpdateStatusChange?: FieldPolicy<any> | FieldReadFunction<any>;
};
@@ -1955,6 +2020,21 @@ export type SyncConflictInfoTypeFieldPolicy = {
deviceName?: FieldPolicy<any> | FieldReadFunction<any>;
remotePage?: FieldPolicy<any> | FieldReadFunction<any>;
};
export type SyncStatusKeySpecifier = (
| 'backupRestoreId'
| 'endDate'
| 'errorMessage'
| 'startDate'
| 'state'
| SyncStatusKeySpecifier
)[];
export type SyncStatusFieldPolicy = {
backupRestoreId?: FieldPolicy<any> | FieldReadFunction<any>;
endDate?: FieldPolicy<any> | FieldReadFunction<any>;
errorMessage?: FieldPolicy<any> | FieldReadFunction<any>;
startDate?: FieldPolicy<any> | FieldReadFunction<any>;
state?: FieldPolicy<any> | FieldReadFunction<any>;
};
export type TextFilterKeySpecifier = ('default' | 'name' | TextFilterKeySpecifier)[];
export type TextFilterFieldPolicy = {
default?: FieldPolicy<any> | FieldReadFunction<any>;
@@ -2918,6 +2998,10 @@ export type StrictTypedTypePolicies = {
keyFields?: false | StartDownloaderPayloadKeySpecifier | (() => undefined | StartDownloaderPayloadKeySpecifier);
fields?: StartDownloaderPayloadFieldPolicy;
};
StartSyncPayload?: Omit<TypePolicy, 'fields' | 'keyFields'> & {
keyFields?: false | StartSyncPayloadKeySpecifier | (() => undefined | StartSyncPayloadKeySpecifier);
fields?: StartSyncPayloadFieldPolicy;
};
StopDownloaderPayload?: Omit<TypePolicy, 'fields' | 'keyFields'> & {
keyFields?: false | StopDownloaderPayloadKeySpecifier | (() => undefined | StopDownloaderPayloadKeySpecifier);
fields?: StopDownloaderPayloadFieldPolicy;
@@ -2934,6 +3018,10 @@ export type StrictTypedTypePolicies = {
keyFields?: false | SyncConflictInfoTypeKeySpecifier | (() => undefined | SyncConflictInfoTypeKeySpecifier);
fields?: SyncConflictInfoTypeFieldPolicy;
};
SyncStatus?: Omit<TypePolicy, 'fields' | 'keyFields'> & {
keyFields?: false | SyncStatusKeySpecifier | (() => undefined | SyncStatusKeySpecifier);
fields?: SyncStatusFieldPolicy;
};
TextFilter?: Omit<TypePolicy, 'fields' | 'keyFields'> & {
keyFields?: false | TextFilterKeySpecifier | (() => undefined | TextFilterKeySpecifier);
fields?: TextFilterFieldPolicy;

View File

@@ -1363,6 +1363,7 @@ export type Mutation = {
setSourceMeta?: Maybe<SetSourceMetaPayload>;
setSourceMetas?: Maybe<SetSourceMetasPayload>;
startDownloader?: Maybe<StartDownloaderPayload>;
startSync: StartSyncPayload;
stopDownloader?: Maybe<StopDownloaderPayload>;
trackProgress?: Maybe<TrackProgressPayload>;
unbindTrack: UnbindTrackPayload;
@@ -1598,6 +1599,10 @@ export type MutationStartDownloaderArgs = {
input: StartDownloaderInput;
};
export type MutationStartSyncArgs = {
input: StartSyncInput;
};
export type MutationStopDownloaderArgs = {
input: StopDownloaderInput;
};
@@ -1824,6 +1829,15 @@ export type PartialSettingsType = Settings & {
socksProxyPort?: Maybe<Scalars['String']['output']>;
socksProxyUsername?: Maybe<Scalars['String']['output']>;
socksProxyVersion?: Maybe<Scalars['Int']['output']>;
syncDataCategories?: Maybe<Scalars['Boolean']['output']>;
syncDataChapters?: Maybe<Scalars['Boolean']['output']>;
syncDataHistory?: Maybe<Scalars['Boolean']['output']>;
syncDataManga?: Maybe<Scalars['Boolean']['output']>;
syncDataTracking?: Maybe<Scalars['Boolean']['output']>;
syncInterval?: Maybe<Scalars['Duration']['output']>;
syncYomiApiKey?: Maybe<Scalars['String']['output']>;
syncYomiEnabled?: Maybe<Scalars['Boolean']['output']>;
syncYomiHost?: Maybe<Scalars['String']['output']>;
systemTrayEnabled?: Maybe<Scalars['Boolean']['output']>;
updateMangas?: Maybe<Scalars['Boolean']['output']>;
useHikariConnectionPool?: Maybe<Scalars['Boolean']['output']>;
@@ -1903,6 +1917,15 @@ export type PartialSettingsTypeInput = {
socksProxyPort?: InputMaybe<Scalars['String']['input']>;
socksProxyUsername?: InputMaybe<Scalars['String']['input']>;
socksProxyVersion?: InputMaybe<Scalars['Int']['input']>;
syncDataCategories?: InputMaybe<Scalars['Boolean']['input']>;
syncDataChapters?: InputMaybe<Scalars['Boolean']['input']>;
syncDataHistory?: InputMaybe<Scalars['Boolean']['input']>;
syncDataManga?: InputMaybe<Scalars['Boolean']['input']>;
syncDataTracking?: InputMaybe<Scalars['Boolean']['input']>;
syncInterval?: InputMaybe<Scalars['Duration']['input']>;
syncYomiApiKey?: InputMaybe<Scalars['String']['input']>;
syncYomiEnabled?: InputMaybe<Scalars['Boolean']['input']>;
syncYomiHost?: InputMaybe<Scalars['String']['input']>;
systemTrayEnabled?: InputMaybe<Scalars['Boolean']['input']>;
updateMangas?: InputMaybe<Scalars['Boolean']['input']>;
useHikariConnectionPool?: InputMaybe<Scalars['Boolean']['input']>;
@@ -1958,6 +1981,7 @@ export type Query = {
extensions: ExtensionNodeList;
getWebUIUpdateStatus: WebUiUpdateStatus;
koSyncStatus: KoSyncStatusPayload;
lastSyncStatus?: Maybe<SyncStatus>;
lastUpdateTimestamp: LastUpdateTimestampPayload;
libraryUpdateStatus: LibraryUpdateStatus;
manga: MangaType;
@@ -2412,6 +2436,15 @@ export type Settings = {
socksProxyPort?: Maybe<Scalars['String']['output']>;
socksProxyUsername?: Maybe<Scalars['String']['output']>;
socksProxyVersion?: Maybe<Scalars['Int']['output']>;
syncDataCategories?: Maybe<Scalars['Boolean']['output']>;
syncDataChapters?: Maybe<Scalars['Boolean']['output']>;
syncDataHistory?: Maybe<Scalars['Boolean']['output']>;
syncDataManga?: Maybe<Scalars['Boolean']['output']>;
syncDataTracking?: Maybe<Scalars['Boolean']['output']>;
syncInterval?: Maybe<Scalars['Duration']['output']>;
syncYomiApiKey?: Maybe<Scalars['String']['output']>;
syncYomiEnabled?: Maybe<Scalars['Boolean']['output']>;
syncYomiHost?: Maybe<Scalars['String']['output']>;
systemTrayEnabled?: Maybe<Scalars['Boolean']['output']>;
updateMangas?: Maybe<Scalars['Boolean']['output']>;
useHikariConnectionPool?: Maybe<Scalars['Boolean']['output']>;
@@ -2556,6 +2589,15 @@ export type SettingsType = Settings & {
socksProxyPort: Scalars['String']['output'];
socksProxyUsername: Scalars['String']['output'];
socksProxyVersion: Scalars['Int']['output'];
syncDataCategories: Scalars['Boolean']['output'];
syncDataChapters: Scalars['Boolean']['output'];
syncDataHistory: Scalars['Boolean']['output'];
syncDataManga: Scalars['Boolean']['output'];
syncDataTracking: Scalars['Boolean']['output'];
syncInterval: Scalars['Duration']['output'];
syncYomiApiKey: Scalars['String']['output'];
syncYomiEnabled: Scalars['Boolean']['output'];
syncYomiHost: Scalars['String']['output'];
systemTrayEnabled: Scalars['Boolean']['output'];
updateMangas: Scalars['Boolean']['output'];
useHikariConnectionPool: Scalars['Boolean']['output'];
@@ -2685,6 +2727,22 @@ export type StartDownloaderPayload = {
downloadStatus: DownloadStatus;
};
export type StartSyncInput = {
clientMutationId?: InputMaybe<Scalars['String']['input']>;
};
export type StartSyncPayload = {
__typename?: 'StartSyncPayload';
clientMutationId?: Maybe<Scalars['String']['output']>;
result: StartSyncResult;
};
export enum StartSyncResult {
Success = 'SUCCESS',
SyncDisabled = 'SYNC_DISABLED',
SyncInProgress = 'SYNC_IN_PROGRESS',
}
export type StopDownloaderInput = {
clientMutationId?: InputMaybe<Scalars['String']['input']>;
};
@@ -2777,6 +2835,7 @@ export type Subscription = {
downloadChanged: DownloadStatus;
downloadStatusChanged: DownloadUpdates;
libraryUpdateStatusChanged: UpdaterUpdates;
syncStatusChanged: SyncStatus;
/** @deprecated Replaced with updates, replace with updates(input) */
updateStatusChanged: UpdateStatus;
webUIUpdateStatusChange: WebUiUpdateStatus;
@@ -2807,6 +2866,26 @@ export type SyncConflictInfoType = {
remotePage: Scalars['Int']['output'];
};
export enum SyncState {
CreatingBackup = 'CREATING_BACKUP',
Downloading = 'DOWNLOADING',
Error = 'ERROR',
Merging = 'MERGING',
Restoring = 'RESTORING',
Started = 'STARTED',
Success = 'SUCCESS',
Uploading = 'UPLOADING',
}
export type SyncStatus = {
__typename?: 'SyncStatus';
backupRestoreId?: Maybe<Scalars['String']['output']>;
endDate?: Maybe<Scalars['LongString']['output']>;
errorMessage?: Maybe<Scalars['String']['output']>;
startDate: Scalars['LongString']['output'];
state: SyncState;
};
export type TextFilter = {
__typename?: 'TextFilter';
default: Scalars['String']['output'];

View File

@@ -2795,6 +2795,15 @@ export type ServerSettingsFragment = {
databasePassword: string;
useHikariConnectionPool: boolean;
kcefEnabled: boolean;
syncDataCategories: boolean;
syncDataChapters: boolean;
syncDataHistory: boolean;
syncDataManga: boolean;
syncDataTracking: boolean;
syncInterval: string;
syncYomiApiKey: string;
syncYomiEnabled: boolean;
syncYomiHost: string;
downloadConversions: Array<{
__typename: 'SettingsDownloadConversionType';
mimeType: string;
@@ -2899,6 +2908,15 @@ export type ResetServerSettingsMutation = {
databasePassword: string;
useHikariConnectionPool: boolean;
kcefEnabled: boolean;
syncDataCategories: boolean;
syncDataChapters: boolean;
syncDataHistory: boolean;
syncDataManga: boolean;
syncDataTracking: boolean;
syncInterval: string;
syncYomiApiKey: string;
syncYomiEnabled: boolean;
syncYomiHost: string;
downloadConversions: Array<{
__typename: 'SettingsDownloadConversionType';
mimeType: string;
@@ -3013,6 +3031,15 @@ export type UpdateServerSettingsMutation = {
databasePassword: string;
useHikariConnectionPool: boolean;
kcefEnabled: boolean;
syncDataCategories: boolean;
syncDataChapters: boolean;
syncDataHistory: boolean;
syncDataManga: boolean;
syncDataTracking: boolean;
syncInterval: string;
syncYomiApiKey: string;
syncYomiEnabled: boolean;
syncYomiHost: string;
downloadConversions: Array<{
__typename: 'SettingsDownloadConversionType';
mimeType: string;
@@ -3123,6 +3150,15 @@ export type GetServerSettingsQuery = {
databasePassword: string;
useHikariConnectionPool: boolean;
kcefEnabled: boolean;
syncDataCategories: boolean;
syncDataChapters: boolean;
syncDataHistory: boolean;
syncDataManga: boolean;
syncDataTracking: boolean;
syncInterval: string;
syncYomiApiKey: string;
syncYomiEnabled: boolean;
syncYomiHost: string;
downloadConversions: Array<{
__typename: 'SettingsDownloadConversionType';
mimeType: string;
@@ -3689,6 +3725,52 @@ export type GetMigratableSourcesQuery = {
};
};
export type SyncStatusFieldsFragment = {
__typename: 'SyncStatus';
backupRestoreId: string | null;
endDate: string | null;
errorMessage: string | null;
startDate: string;
state: Types.SyncState;
};
export type StartSyncMutationVariables = Exact<{
input?: Types.StartSyncInput | null | undefined;
}>;
export type StartSyncMutation = {
__typename: 'Mutation';
startSync: { __typename: 'StartSyncPayload'; result: Types.StartSyncResult };
};
export type GetSyncStatusQueryVariables = Exact<{ [key: string]: never }>;
export type GetSyncStatusQuery = {
__typename: 'Query';
lastSyncStatus: {
__typename: 'SyncStatus';
backupRestoreId: string | null;
endDate: string | null;
errorMessage: string | null;
startDate: string;
state: Types.SyncState;
} | null;
};
export type SyncSubscriptionVariables = Exact<{ [key: string]: never }>;
export type SyncSubscription = {
__typename: 'Subscription';
syncStatusChanged: {
__typename: 'SyncStatus';
backupRestoreId: string | null;
endDate: string | null;
errorMessage: string | null;
startDate: string;
state: Types.SyncState;
};
};
export type TrackerBaseFieldsFragment = {
__typename: 'TrackerType';
id: number;

View File

@@ -138,5 +138,16 @@ export const SERVER_SETTINGS = gql`
# WebView
kcefEnabled
# Sync
syncDataCategories
syncDataChapters
syncDataHistory
syncDataManga
syncDataTracking
syncInterval
syncYomiApiKey
syncYomiEnabled
syncYomiHost
}
`;

View File

@@ -0,0 +1,19 @@
/*
* 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 gql from 'graphql-tag';
export const SYNC_STATUS_FIELDS = gql`
fragment SYNC_STATUS_FIELDS on SyncStatus {
backupRestoreId
endDate
errorMessage
startDate
state
}
`;

View File

@@ -0,0 +1,17 @@
/*
* 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 gql from 'graphql-tag';
export const START_SYNC = gql`
mutation START_SYNC($input: StartSyncInput = {}) {
startSync(input: $input) {
result
}
}
`;

View File

@@ -0,0 +1,20 @@
/*
* 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 gql from 'graphql-tag';
import { SYNC_STATUS_FIELDS } from '@/lib/graphql/sync/SyncFragments.ts';
export const GET_SYNC_STATUS = gql`
${SYNC_STATUS_FIELDS}
query GET_SYNC_STATUS {
lastSyncStatus {
...SYNC_STATUS_FIELDS
}
}
`;

View File

@@ -0,0 +1,20 @@
/*
* 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 gql from 'graphql-tag';
import { SYNC_STATUS_FIELDS } from '@/lib/graphql/sync/SyncFragments.ts';
export const SYNC_SUBSCRIPTION = gql`
${SYNC_STATUS_FIELDS}
subscription SYNC_SUBSCRIPTION {
syncStatusChanged {
...SYNC_STATUS_FIELDS
}
}
`;