diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index a95c788f..3c813714 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -566,6 +566,14 @@ }, "title": "Backup" }, + "clear_cache": { + "label": { + "description": "The cache of the client (browser, electron) should get cleared alongside it, otherwise, the client cache will keep getting used", + "failure": "Could not clear the server cache", + "success": "Cleared the server cache", + "title": "Clear server cache" + } + }, "label": { "dark_theme": "Dark theme", "language_description": "Feel free to translate the project on", diff --git a/src/lib/graphql/generated/apollo-helpers.ts b/src/lib/graphql/generated/apollo-helpers.ts index a2b028c6..370765ac 100644 --- a/src/lib/graphql/generated/apollo-helpers.ts +++ b/src/lib/graphql/generated/apollo-helpers.ts @@ -402,7 +402,7 @@ export type MultiSelectListPreferenceFieldPolicy = { title?: FieldPolicy | FieldReadFunction, visible?: FieldPolicy | FieldReadFunction }; -export type MutationKeySpecifier = ('clearCachedImages' | 'clearDownloader' | 'createBackup' | 'createCategory' | 'deleteCategory' | 'deleteCategoryMeta' | 'deleteChapterMeta' | 'deleteDownloadedChapter' | 'deleteDownloadedChapters' | 'deleteGlobalMeta' | 'deleteMangaMeta' | 'dequeueChapterDownload' | 'dequeueChapterDownloads' | 'downloadAhead' | 'enqueueChapterDownload' | 'enqueueChapterDownloads' | 'fetchChapterPages' | 'fetchChapters' | 'fetchExtensions' | 'fetchManga' | 'fetchSourceManga' | 'installExternalExtension' | 'reorderChapterDownload' | 'resetSettings' | 'restoreBackup' | 'setCategoryMeta' | 'setChapterMeta' | 'setGlobalMeta' | 'setMangaMeta' | 'setSettings' | 'startDownloader' | 'stopDownloader' | 'updateCategories' | 'updateCategory' | 'updateCategoryManga' | 'updateCategoryOrder' | 'updateChapter' | 'updateChapters' | 'updateExtension' | 'updateExtensions' | 'updateLibraryManga' | 'updateManga' | 'updateMangaCategories' | 'updateMangas' | 'updateMangasCategories' | 'updateSourcePreference' | 'updateStop' | 'updateWebUI' | MutationKeySpecifier)[]; +export type MutationKeySpecifier = ('clearCachedImages' | 'clearDownloader' | 'createBackup' | 'createCategory' | 'deleteCategory' | 'deleteCategoryMeta' | 'deleteChapterMeta' | 'deleteDownloadedChapter' | 'deleteDownloadedChapters' | 'deleteGlobalMeta' | 'deleteMangaMeta' | 'dequeueChapterDownload' | 'dequeueChapterDownloads' | 'downloadAhead' | 'enqueueChapterDownload' | 'enqueueChapterDownloads' | 'fetchChapterPages' | 'fetchChapters' | 'fetchExtensions' | 'fetchManga' | 'fetchSourceManga' | 'installExternalExtension' | 'reorderChapterDownload' | 'resetSettings' | 'resetWebUIUpdateStatus' | 'restoreBackup' | 'setCategoryMeta' | 'setChapterMeta' | 'setGlobalMeta' | 'setMangaMeta' | 'setSettings' | 'startDownloader' | 'stopDownloader' | 'updateCategories' | 'updateCategory' | 'updateCategoryManga' | 'updateCategoryOrder' | 'updateChapter' | 'updateChapters' | 'updateExtension' | 'updateExtensions' | 'updateLibraryManga' | 'updateManga' | 'updateMangaCategories' | 'updateMangas' | 'updateMangasCategories' | 'updateSourcePreference' | 'updateStop' | 'updateWebUI' | MutationKeySpecifier)[]; export type MutationFieldPolicy = { clearCachedImages?: FieldPolicy | FieldReadFunction, clearDownloader?: FieldPolicy | FieldReadFunction, @@ -428,6 +428,7 @@ export type MutationFieldPolicy = { installExternalExtension?: FieldPolicy | FieldReadFunction, reorderChapterDownload?: FieldPolicy | FieldReadFunction, resetSettings?: FieldPolicy | FieldReadFunction, + resetWebUIUpdateStatus?: FieldPolicy | FieldReadFunction, restoreBackup?: FieldPolicy | FieldReadFunction, setCategoryMeta?: FieldPolicy | FieldReadFunction, setChapterMeta?: FieldPolicy | FieldReadFunction, @@ -833,11 +834,10 @@ export type WebUIUpdateCheckFieldPolicy = { tag?: FieldPolicy | FieldReadFunction, updateAvailable?: FieldPolicy | FieldReadFunction }; -export type WebUIUpdateInfoKeySpecifier = ('channel' | 'tag' | 'updateAvailable' | WebUIUpdateInfoKeySpecifier)[]; +export type WebUIUpdateInfoKeySpecifier = ('channel' | 'tag' | WebUIUpdateInfoKeySpecifier)[]; export type WebUIUpdateInfoFieldPolicy = { channel?: FieldPolicy | FieldReadFunction, - tag?: FieldPolicy | FieldReadFunction, - updateAvailable?: FieldPolicy | FieldReadFunction + tag?: FieldPolicy | FieldReadFunction }; export type WebUIUpdatePayloadKeySpecifier = ('clientMutationId' | 'updateStatus' | WebUIUpdatePayloadKeySpecifier)[]; export type WebUIUpdatePayloadFieldPolicy = { diff --git a/src/lib/graphql/generated/graphql.ts b/src/lib/graphql/generated/graphql.ts index 8d137f30..b3b2f2dc 100644 --- a/src/lib/graphql/generated/graphql.ts +++ b/src/lib/graphql/generated/graphql.ts @@ -952,6 +952,7 @@ export type Mutation = { installExternalExtension: InstallExternalExtensionPayload; reorderChapterDownload: ReorderChapterDownloadPayload; resetSettings: ResetSettingsPayload; + resetWebUIUpdateStatus: WebUiUpdateStatus; restoreBackup: RestoreBackupPayload; setCategoryMeta: SetCategoryMetaPayload; setChapterMeta: SetChapterMetaPayload; @@ -2017,7 +2018,7 @@ export enum UpdateState { Downloading = 'DOWNLOADING', Error = 'ERROR', Finished = 'FINISHED', - Stopped = 'STOPPED' + Idle = 'IDLE' } export type UpdateStatus = { @@ -2093,7 +2094,6 @@ export type WebUiUpdateInfo = { __typename?: 'WebUIUpdateInfo'; channel: Scalars['String']['output']; tag: Scalars['String']['output']; - updateAvailable: Scalars['Boolean']['output']; }; export type WebUiUpdateInput = { @@ -2395,6 +2395,13 @@ export type SetGlobalMetadataMutationVariables = Exact<{ export type SetGlobalMetadataMutation = { __typename?: 'Mutation', setGlobalMeta: { __typename?: 'SetGlobalMetaPayload', clientMutationId?: string | null, meta: { __typename?: 'GlobalMetaType', key: string, value: string } } }; +export type ClearServerCacheMutationVariables = Exact<{ + input: ClearCachedImagesInput; +}>; + + +export type ClearServerCacheMutation = { __typename?: 'Mutation', clearCachedImages: { __typename?: 'ClearCachedImagesPayload', clientMutationId?: string | null, cachedPages?: boolean | null, cachedThumbnails?: boolean | null, downloadedThumbnails?: boolean | null } }; + export type DeleteMangaMetadataMutationVariables = Exact<{ input: DeleteMangaMetaInput; }>; diff --git a/src/lib/graphql/mutations/ImageMutation.ts b/src/lib/graphql/mutations/ImageMutation.ts new file mode 100644 index 00000000..05eeeb9b --- /dev/null +++ b/src/lib/graphql/mutations/ImageMutation.ts @@ -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'; + +export const CLEAR_SERVER_CACHE = gql` + mutation CLEAR_SERVER_CACHE($input: ClearCachedImagesInput!) { + clearCachedImages(input: $input) { + clientMutationId + cachedPages + cachedThumbnails + downloadedThumbnails + } + } +`; diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index 9ebb9bcc..656a4e6a 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -35,8 +35,11 @@ import { ChapterOrderBy, CheckForServerUpdatesQuery, CheckForServerUpdatesQueryVariables, + ClearCachedImagesInput, ClearDownloaderMutation, ClearDownloaderMutationVariables, + ClearServerCacheMutation, + ClearServerCacheMutationVariables, CreateCategoryInput, CreateCategoryMutation, CreateCategoryMutationVariables, @@ -212,6 +215,7 @@ import { UPDATER_SUBSCRIPTION } from '@/lib/graphql/subscriptions/UpdaterSubscri import { GET_SERVER_SETTINGS } from '@/lib/graphql/queries/SettingsQuery.ts'; import { UPDATE_SERVER_SETTINGS } from '@/lib/graphql/mutations/SettingsMutation.ts'; import { BASE_MANGA_FIELDS, FULL_EXTENSION_FIELDS } from '@/lib/graphql/Fragments.ts'; +import { CLEAR_SERVER_CACHE } from '@/lib/graphql/mutations/ImageMutation.ts'; enum GQLMethod { QUERY = 'QUERY', @@ -1993,6 +1997,13 @@ export class RequestManager { ): AbortableApolloUseQueryResponse { return this.doRequest(GQLMethod.USE_QUERY, GET_LAST_UPDATE_TIMESTAMP, {}, options); } + + public useClearServerCache( + input: ClearCachedImagesInput = { cachedPages: true, cachedThumbnails: true, downloadedThumbnails: true }, + options?: MutationHookOptions, + ): AbortableApolloUseMutationResponse { + return this.doRequest(GQLMethod.USE_MUTATION, CLEAR_SERVER_CACHE, { input }, options); + } } export const requestManager = new RequestManager(); diff --git a/src/screens/Settings.tsx b/src/screens/Settings.tsx index 78f9a26c..22a268e4 100644 --- a/src/screens/Settings.tsx +++ b/src/screens/Settings.tsx @@ -19,7 +19,7 @@ import ListItemText from '@mui/material/ListItemText'; import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; import Switch from '@mui/material/Switch'; import FavoriteIcon from '@mui/icons-material/Favorite'; -import { Link, MenuItem, Select } from '@mui/material'; +import { Link, ListItemButton, MenuItem, Select } from '@mui/material'; import { useTranslation } from 'react-i18next'; import LanguageIcon from '@mui/icons-material/Language'; import CollectionsOutlinedBookmarkIcon from '@mui/icons-material/CollectionsBookmarkOutlined'; @@ -27,12 +27,15 @@ import GetAppOutlinedIcon from '@mui/icons-material/GetAppOutlined'; import ViewModuleIcon from '@mui/icons-material/ViewModule'; import DnsIcon from '@mui/icons-material/Dns'; import WebIcon from '@mui/icons-material/Web'; +import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; import { langCodeToName } from '@/util/language'; import { useLocalStorage } from '@/util/useLocalStorage'; import { ListItemLink } from '@/components/util/ListItemLink'; import { DarkTheme } from '@/components/context/DarkTheme'; import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { NumberSetting } from '@/components/settings/NumberSetting.tsx'; +import { requestManager } from '@/lib/requests/RequestManager.ts'; +import { makeToast } from '@/components/util/Toast.tsx'; export function Settings() { const { t, i18n } = useTranslation(); @@ -50,6 +53,17 @@ export function Settings() { const itemWidthIcon = useMemo(() => , []); const [itemWidth, setItemWidth] = useLocalStorage('ItemWidth', DEFAULT_ITEM_WIDTH); + const [triggerClearServerCache, { loading: isClearingServerCache }] = requestManager.useClearServerCache(); + + const clearServerCache = async () => { + try { + await triggerClearServerCache(); + makeToast(t('settings.clear_cache.label.success'), 'success'); + } catch (e) { + makeToast(t('settings.clear_cache.label.failure'), 'error'); + } + }; + return ( @@ -146,6 +160,15 @@ export function Settings() { + + + + + +