Add sync state to settings

This commit is contained in:
schroda
2026-07-02 22:44:10 +02:00
parent 18d926bac4
commit 65ddc24449
7 changed files with 159 additions and 5 deletions

View File

@@ -199,6 +199,7 @@ const BackgroundSubscriptions = () => {
requestManager.useDownloadSubscription({ skip: skipConnection });
requestManager.useUpdaterSubscription({ skip: skipConnection });
requestManager.useWebUIUpdateSubscription({ skip: skipConnection });
requestManager.useSyncSubscription({ skip: skipConnection });
return null;
};

View File

@@ -48,7 +48,7 @@ export const isSameDay = (first: Dayjs, second: Dayjs): boolean => first.isSame(
* @param date
* @param withTime
*/
export const getDateString = (date: Dayjs | number, withTime: boolean = false) => {
export const getDateString = (date: Dayjs | number, withTime: boolean = false, dateTime: boolean = false) => {
const actualDate = date instanceof dayjs ? date : dayjs(date);
const timeString = timeFormatter.format(actualDate.toDate());
@@ -68,5 +68,9 @@ export const getDateString = (date: Dayjs | number, withTime: boolean = false) =
return t`Yesterday`;
}
if (dateTime) {
return dateTimeFormatter.format(actualDate.toDate());
}
return dateFormatter.format(actualDate.toDate());
};

View File

@@ -24,6 +24,8 @@ import {
AuthMode,
KoreaderSyncChecksumMethod,
KoreaderSyncConflictStrategy,
StartSyncResult,
SyncState,
WebUiChannel,
WebUiFlavor,
WebUiInterface,
@@ -360,3 +362,20 @@ export const SYNC_SETTINGS_HIDDEN_BACKUP_FLAGS = [
'includeServerSettings',
'includeClientData',
] as const satisfies BackupFlag[];
export const SYNC_START_RESULT_TRANSLATION: Record<StartSyncResult, MessageDescriptor> = {
[StartSyncResult.Success]: msg`Sync started`,
[StartSyncResult.SyncDisabled]: msg`Sync disabled`,
[StartSyncResult.SyncInProgress]: msg`Sync in progress`,
};
export const SYNC_STATE_TRANSLATION: Record<SyncState, MessageDescriptor> = {
[SyncState.CreatingBackup]: msg`Creating backup`,
[SyncState.Downloading]: msg`Downloading`,
[SyncState.Error]: msg`Error`,
[SyncState.Merging]: msg`Merging`,
[SyncState.Restoring]: msg`Restoring`,
[SyncState.Started]: msg`Started`,
[SyncState.Success]: msg`Success`,
[SyncState.Uploading]: msg`Uploading`,
};

View File

@@ -32,7 +32,14 @@ import type { MetadataUpdateSettings } from '@/features/app-updates/AppUpdateChe
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 {
AuthMode,
CbzMediaType,
DatabaseType,
SortOrder,
StartSyncResult,
SyncState,
} from '@/lib/graphql/generated/graphql-base.types.ts';
import {
AUTH_MODES_SELECT_VALUES,
JWT_ACCESS_TOKEN_EXPIRY,
@@ -42,6 +49,8 @@ import {
SYNC_INTERVAL_SELECT_VALUES_WITH_CUSTOM,
SYNC_INTERVAL_VALUES,
SYNC_SETTINGS_HIDDEN_BACKUP_FLAGS,
SYNC_START_RESULT_TRANSLATION,
SYNC_STATE_TRANSLATION,
} from '@/features/settings/Settings.constants.ts';
import { ServerAddressSetting } from '@/features/settings/components/ServerAddressSetting.tsx';
import { AuthManager } from '@/features/authentication/AuthManager.ts';
@@ -53,6 +62,7 @@ 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';
import { epochToDate, getDateString } from '@/base/utils/DateHelper.ts';
const convertSyncDataToBackupFlags = (settings: ServerSettingsType): BackupFlagInclusionState => ({
includeManga: settings.syncDataManga,
@@ -88,6 +98,7 @@ export const ServerSettings = () => {
useAppTitle(t`Server`);
const syncStatusRequest = requestManager.useGetSyncStatus();
const {
settings: { serverInformAvailableUpdate, serverInformVersionUpdated },
loading: areMetadataServerSettingsLoading,
@@ -221,6 +232,11 @@ export const ServerSettings = () => {
const includedSyncDataText = syncDataFlagsInfo.true;
const excludedSyncDataText = syncDataFlagsInfo.false;
const syncStatus = syncStatusRequest.data?.lastSyncStatus;
const syncDate = syncStatus?.endDate ?? syncStatus?.startDate;
const syncState = syncStatus?.state;
const isSyncing = !!syncState && ![SyncState.Success, SyncState.Error].includes(syncState);
return (
<List sx={{ pt: 0 }}>
{localSettings}
@@ -732,6 +748,33 @@ export const ServerSettings = () => {
</ListSubheader>
}
>
<ListItemButton
disabled={isSyncing}
onClick={() =>
requestManager
.startSync()
.response.then((response) => {
const startResult = response.data?.startSync.result;
makeToast(
t(SYNC_START_RESULT_TRANSLATION[startResult!]),
startResult === StartSyncResult.Success ? 'success' : 'error',
);
})
.catch((e) => makeToast(t`Could not start sync`, 'error', getErrorMessage(e)))
}
>
<ListItemText
primary={isSyncing ? t`Sync status` : t`Start sync`}
secondary={
<>
{syncDate && t`Last sync: ${getDateString(epochToDate(Number(syncDate)), true, true)}`}{' '}
{syncState && t`- State: ${t(SYNC_STATE_TRANSLATION[syncState])}`}
{!!syncStatus?.errorMessage && `\nError: ${syncStatus.errorMessage}`}
</>
}
/>
</ListItemButton>
<ListItemButton
onClick={async () => {
try {
@@ -748,7 +791,7 @@ export const ServerSettings = () => {
}}
>
<ListItemText
primary={t`Backup data`}
primary={t`Sync data`}
secondary={
<>
<span>{t`Include: ${includedSyncDataText}`}</span>

View File

@@ -14,6 +14,11 @@ msgstr ""
"Plural-Forms: \n"
"X-Message-Format: ICU\n"
#. placeholder {0}: t(SYNC_STATE_TRANSLATION[syncState])
#: src/features/settings/screens/ServerSettings.tsx
msgid "- State: {0}"
msgstr "- State: {0}"
#. placeholder {0}: selectedChapters.length
#. placeholder {0}: visibleChapters.length
#: src/features/chapter/components/ChapterList.tsx
@@ -574,7 +579,6 @@ msgid "Backup cleanup"
msgstr "Backup cleanup"
#: src/features/backup/screens/Backup.tsx
#: src/features/settings/screens/ServerSettings.tsx
msgid "Backup data"
msgstr "Backup data"
@@ -1152,6 +1156,10 @@ msgstr "Could not save theme changes"
msgid "Could not search source"
msgstr "Could not search source"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Could not start sync"
msgstr "Could not start sync"
#: src/features/updates/components/UpdateChecker.tsx
msgid "Could not stop global update"
msgstr "Could not stop global update"
@@ -1192,6 +1200,10 @@ msgstr "Create theme"
msgid "Created theme"
msgstr "Created theme"
#: src/features/settings/Settings.constants.ts
msgid "Creating backup"
msgstr "Creating backup"
#: src/features/backup/screens/Backup.tsx
msgid "Creating backup…"
msgstr "Creating backup…"
@@ -1488,6 +1500,7 @@ msgid "Downloaded"
msgstr "Downloaded"
#: src/base/components/downloads/DownloadStateIndicator.tsx
#: src/features/settings/Settings.constants.ts
msgid "Downloading"
msgstr "Downloading"
@@ -1587,6 +1600,7 @@ msgstr "Entry \"{id}\" not found"
#: src/base/components/downloads/DownloadStateIndicator.tsx
#: src/base/components/feedback/SnackbarWithDescription.tsx
#: src/features/settings/Settings.constants.ts
msgid "Error"
msgstr "Error"
@@ -2168,6 +2182,11 @@ msgstr "Large"
msgid "Last read chapter"
msgstr "Last read chapter"
#. placeholder {0}: getDateString(epochToDate(Number(syncDate)), true, true)
#: src/features/settings/screens/ServerSettings.tsx
msgid "Last sync: {0}"
msgstr "Last sync: {0}"
#: src/features/updates/screens/Updates.tsx
msgid "Last update: {date}"
msgstr "Last update: {date}"
@@ -2420,6 +2439,10 @@ msgstr "Medium"
msgid "Menu"
msgstr "Menu"
#: src/features/settings/Settings.constants.ts
msgid "Merging"
msgstr "Merging"
#: src/features/browse/screens/Browse.tsx
#: src/features/manga/components/MangaToolbarMenu.tsx
#: src/features/manga/Manga.constants.ts
@@ -3008,6 +3031,10 @@ msgstr "Restore"
msgid "Restore Backup"
msgstr "Restore Backup"
#: src/features/settings/Settings.constants.ts
msgid "Restoring"
msgstr "Restoring"
#: src/features/backup/screens/Backup.tsx
msgid "Restoring backup…"
msgstr "Restoring backup…"
@@ -3496,7 +3523,12 @@ msgstr ""
msgid "Start Search"
msgstr "Start Search"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Start sync"
msgstr "Start sync"
#: src/features/library/components/LibraryOptionsPanel.tsx
#: src/features/settings/Settings.constants.ts
#: src/features/tracker/components/cards/TrackerMangaCard.tsx
msgid "Started"
msgstr "Started"
@@ -3531,6 +3563,7 @@ msgid "Submit"
msgstr "Submit"
#: src/base/components/feedback/SnackbarWithDescription.tsx
#: src/features/settings/Settings.constants.ts
msgid "Success"
msgstr "Success"
@@ -3550,11 +3583,24 @@ msgstr "Sync"
msgid "Sync data"
msgstr "Sync data"
#: src/features/settings/Settings.constants.ts
msgid "Sync disabled"
msgstr "Sync disabled"
#: src/features/settings/Settings.constants.ts
msgid "Sync in progress"
msgstr "Sync in progress"
#: src/features/settings/screens/ServerSettings.tsx
msgid "Sync interval"
msgstr "Sync interval"
#: src/features/settings/Settings.constants.ts
msgid "Sync started"
msgstr "Sync started"
#: src/features/settings/components/koreaderSync/KoreaderSyncSettings.tsx
#: src/features/settings/screens/ServerSettings.tsx
msgid "Sync status"
msgstr "Sync status"
@@ -3966,6 +4012,10 @@ msgstr "Updates"
msgid "Updating"
msgstr "Updating"
#: src/features/settings/Settings.constants.ts
msgid "Uploading"
msgstr "Uploading"
#: src/features/settings/Settings.constants.ts
msgid "URL"
msgstr "URL"

View File

@@ -102,6 +102,8 @@ import type {
GetSourceMangasFetchMutationVariables,
GetSourcesListQuery,
GetSourcesListQueryVariables,
GetSyncStatusQuery,
GetSyncStatusQueryVariables,
GetUpdateStatusQuery,
GetUpdateStatusQueryVariables,
GetWebuiUpdateStatusQuery,
@@ -124,10 +126,14 @@ import type {
RestoreBackupMutationVariables,
StartDownloaderMutation,
StartDownloaderMutationVariables,
StartSyncMutation,
StartSyncMutationVariables,
StopDownloaderMutation,
StopDownloaderMutationVariables,
StopUpdaterMutation,
StopUpdaterMutationVariables,
SyncSubscription,
SyncSubscriptionVariables,
TrackerBindMutation,
TrackerBindMutationVariables,
TrackerBindTrackRecordMutation,
@@ -193,6 +199,7 @@ import type {
ValidateBackupQuery,
ValidateBackupQueryVariables,
WebuiUpdateSubscription,
WebuiUpdateSubscriptionVariables,
} from '@/lib/graphql/generated/graphql.ts';
import type {
CreateBackupInput,
@@ -345,6 +352,9 @@ import { EXTENSION_STORE_FIELDS } from '@/lib/graphql/extension/store/ExtensionS
import { ADD_EXTENSION_STORE, REMOVE_EXTENSION_STORE } from '@/lib/graphql/extension/store/ExtensionStoreMutation.ts';
import { assertIsDefined } from '@/base/Asserts.ts';
import { GET_EXTENSION_STORE, GET_EXTENSION_STORES } from '@/lib/graphql/extension/store/ExtensionStoreQuery.ts';
import { SYNC_SUBSCRIPTION } from '@/lib/graphql/sync/SyncSubscription.ts';
import { START_SYNC } from '@/lib/graphql/sync/SyncMutation.ts';
import { GET_SYNC_STATUS } from '@/lib/graphql/sync/SyncQuery.ts';
enum GQLMethod {
QUERY = 'QUERY',
@@ -3717,7 +3727,7 @@ export class RequestManager {
}
public useWebUIUpdateSubscription(
options?: SubscriptionHookOptions<WebuiUpdateSubscription, WebuiUpdateSubscription>,
options?: SubscriptionHookOptions<WebuiUpdateSubscription, WebuiUpdateSubscriptionVariables>,
): useSubscription.Result<WebuiUpdateSubscription> {
return this.doRequest(GQLMethod.USE_SUBSCRIPTION, WEBUI_UPDATE_SUBSCRIPTION, undefined, options);
}
@@ -3860,6 +3870,29 @@ export class RequestManager {
return this.doRequest(GQLMethod.USE_MUTATION, USER_LOGIN, undefined, options);
}
public startSync(
options?: MutationOptions<StartSyncMutation, StartSyncMutationVariables>,
): AbortableApolloMutationResponse<StartSyncMutation> {
return this.doRequest<StartSyncMutation, StartSyncMutationVariables>(
GQLMethod.MUTATION,
START_SYNC,
{},
options,
);
}
public useGetSyncStatus(
options?: QueryHookOptions<GetSyncStatusQuery, GetSyncStatusQueryVariables>,
): AbortableApolloUseQueryResponse<GetSyncStatusQuery, GetSyncStatusQueryVariables> {
return this.doRequest(GQLMethod.USE_QUERY, GET_SYNC_STATUS, {}, options);
}
public useSyncSubscription(
options?: SubscriptionHookOptions<SyncSubscription, SyncSubscriptionVariables>,
): useSubscription.Result<SyncSubscription> {
return this.doRequest(GQLMethod.USE_SUBSCRIPTION, SYNC_SUBSCRIPTION, undefined, options);
}
public useKoSyncStatus(
options?: QueryHookOptions<GetKoSyncStatusQuery, GetKoSyncStatusQueryVariables>,
): AbortableApolloUseQueryResponse<GetKoSyncStatusQuery, GetKoSyncStatusQueryVariables> {

View File

@@ -74,6 +74,7 @@ const typePolicies: TypedTypePolicies = {
WebUIUpdateStatus: { keyFields: [] },
UpdateStatus: { keyFields: [] },
KoSyncStatusPayload: { keyFields: [] },
SyncStatus: { keyFields: [] },
Query: {
fields: {
manga(_, { args, toReference }) {
@@ -132,6 +133,9 @@ const typePolicies: TypedTypePolicies = {
updateStatus(_, { toReference }) {
return toReference({ __typename: 'UpdateStatus', key: {} });
},
lastSyncStatus(_, { toReference }) {
return toReference({ __typename: 'SyncStatus', key: {} });
},
chapters: {
keyArgs: ['condition', 'filter', 'orderBy', 'orderByType', 'order'],
merge(existing, incoming) {