diff --git a/src/components/manga/ChapterCard.tsx b/src/components/manga/ChapterCard.tsx index 3847f690..a4a403b7 100644 --- a/src/components/manga/ChapterCard.tsx +++ b/src/components/manga/ChapterCard.tsx @@ -102,7 +102,7 @@ export const ChapterCard: React.FC = (props: IProps) => { requestManager.updateChapter(chapter.id, { [key]: value, lastPageRead: key === 'isRead' ? 0 : undefined, - deleteChapter: !!chapterIdsToDelete.length, + chapterIdToDelete: chapterIdsToDelete[0], }); }; diff --git a/src/components/settings/downloads/DeleteChaptersWhileReadingSetting.tsx b/src/components/settings/downloads/DeleteChaptersWhileReadingSetting.tsx new file mode 100644 index 00000000..740bb342 --- /dev/null +++ b/src/components/settings/downloads/DeleteChaptersWhileReadingSetting.tsx @@ -0,0 +1,78 @@ +/* + * 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 { + SelectSetting, + SelectSettingValue, + SelectSettingValueDisplayInfo, +} from '@/components/settings/SelectSetting.tsx'; + +const CHAPTERS_TO_DELETE = [0, 1, 2, 3, 4, 5] as const; +const CHAPTERS_TO_DELETE_TO_TRANSLATION_KEY: { + [flavor in (typeof CHAPTERS_TO_DELETE)[number]]: SelectSettingValueDisplayInfo; +} = { + 0: { + text: 'global.label.disabled', + }, + 1: { + text: 'download.settings.delete_chapters.while_reading.option.label.first', + }, + 2: { + text: 'download.settings.delete_chapters.while_reading.option.label.second', + }, + 3: { + text: 'download.settings.delete_chapters.while_reading.option.label.third', + }, + 4: { + text: 'download.settings.delete_chapters.while_reading.option.label.fourth', + }, + 5: { + text: 'download.settings.delete_chapters.while_reading.option.label.fifth', + }, +}; +const CHAPTERS_TO_DELETE_SELECT_VALUES: SelectSettingValue<(typeof CHAPTERS_TO_DELETE)[number]>[] = + CHAPTERS_TO_DELETE.map((chapterToDelete) => [ + chapterToDelete, + CHAPTERS_TO_DELETE_TO_TRANSLATION_KEY[chapterToDelete], + ]); + +const getNormalizedChapterToDelete = (chapterToDelete?: number | boolean) => { + if (!chapterToDelete) { + return undefined; + } + + const isMigrationVersion0 = typeof chapterToDelete === 'boolean'; + if (isMigrationVersion0) { + return Number(chapterToDelete); + } + + return chapterToDelete; +}; + +export const DeleteChaptersWhileReadingSetting = ({ + chapterToDelete, + handleChange, +}: { + chapterToDelete?: number; + handleChange: (chapterToDelete: number) => void; +}) => { + const { t } = useTranslation(); + + const normalizedChapterToDelete = getNormalizedChapterToDelete(chapterToDelete); + + return ( + + ); +}; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 65b94b62..a95c788f 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -157,6 +157,27 @@ }, "title": "Auto-download" }, + "delete_chapters": { + "label": { + "allow_deletion_of_bookmarked": "Allow deleting bookmarked chapters", + "manually_marked_as_read": "Delete chapter after manually marking it as read" + }, + "title": "Delete chapters", + "while_reading": { + "label": { + "title": "Delete finished chapters while reading" + }, + "option": { + "label": { + "fifth": "Fifth to last read chapter", + "first": "Last read chapter", + "fourth": "Fourth to last read chapter", + "second": "Second to last read chapter", + "third": "Third to last read chapter" + } + } + } + }, "download_ahead": { "label": { "description": "How many chapters should get downloaded when marking a chapter as read while reading.", @@ -286,6 +307,7 @@ "label": { "browse": "Browse", "client": "Client", + "disabled": "Disabled", "discord": "Discord", "display": "Display", "filter": "Filter", diff --git a/src/lib/graphql/Fragments.ts b/src/lib/graphql/Fragments.ts index 79d959b1..9bc436aa 100644 --- a/src/lib/graphql/Fragments.ts +++ b/src/lib/graphql/Fragments.ts @@ -416,7 +416,6 @@ export const WEBUI_UPDATE_INFO = gql` fragment WEBUI_UPDATE_INFO on WebUIUpdateInfo { channel tag - updateAvailable } `; diff --git a/src/lib/graphql/generated/apollo-helpers.ts b/src/lib/graphql/generated/apollo-helpers.ts index 6cd7aa9d..a2b028c6 100644 --- a/src/lib/graphql/generated/apollo-helpers.ts +++ b/src/lib/graphql/generated/apollo-helpers.ts @@ -10,6 +10,11 @@ export type AboutServerPayloadFieldPolicy = { revision?: FieldPolicy | FieldReadFunction, version?: FieldPolicy | FieldReadFunction }; +export type AboutWebUIKeySpecifier = ('channel' | 'tag' | AboutWebUIKeySpecifier)[]; +export type AboutWebUIFieldPolicy = { + channel?: FieldPolicy | FieldReadFunction, + tag?: FieldPolicy | FieldReadFunction +}; export type BackupRestoreStatusKeySpecifier = ('mangaProgress' | 'state' | 'totalManga' | BackupRestoreStatusKeySpecifier)[]; export type BackupRestoreStatusFieldPolicy = { mangaProgress?: FieldPolicy | FieldReadFunction, @@ -105,6 +110,13 @@ export type CheckForServerUpdatesPayloadFieldPolicy = { tag?: FieldPolicy | FieldReadFunction, url?: FieldPolicy | FieldReadFunction }; +export type ClearCachedImagesPayloadKeySpecifier = ('cachedPages' | 'cachedThumbnails' | 'clientMutationId' | 'downloadedThumbnails' | ClearCachedImagesPayloadKeySpecifier)[]; +export type ClearCachedImagesPayloadFieldPolicy = { + cachedPages?: FieldPolicy | FieldReadFunction, + cachedThumbnails?: FieldPolicy | FieldReadFunction, + clientMutationId?: FieldPolicy | FieldReadFunction, + downloadedThumbnails?: FieldPolicy | FieldReadFunction +}; export type ClearDownloaderPayloadKeySpecifier = ('clientMutationId' | 'downloadStatus' | ClearDownloaderPayloadKeySpecifier)[]; export type ClearDownloaderPayloadFieldPolicy = { clientMutationId?: FieldPolicy | FieldReadFunction, @@ -390,8 +402,9 @@ export type MultiSelectListPreferenceFieldPolicy = { title?: FieldPolicy | FieldReadFunction, visible?: FieldPolicy | FieldReadFunction }; -export type MutationKeySpecifier = ('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' | '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, createBackup?: FieldPolicy | FieldReadFunction, createCategory?: FieldPolicy | FieldReadFunction, @@ -814,6 +827,12 @@ export type ValidateBackupSourceFieldPolicy = { id?: FieldPolicy | FieldReadFunction, name?: FieldPolicy | FieldReadFunction }; +export type WebUIUpdateCheckKeySpecifier = ('channel' | 'tag' | 'updateAvailable' | WebUIUpdateCheckKeySpecifier)[]; +export type WebUIUpdateCheckFieldPolicy = { + channel?: FieldPolicy | FieldReadFunction, + tag?: FieldPolicy | FieldReadFunction, + updateAvailable?: FieldPolicy | FieldReadFunction +}; export type WebUIUpdateInfoKeySpecifier = ('channel' | 'tag' | 'updateAvailable' | WebUIUpdateInfoKeySpecifier)[]; export type WebUIUpdateInfoFieldPolicy = { channel?: FieldPolicy | FieldReadFunction, @@ -836,6 +855,10 @@ export type StrictTypedTypePolicies = { keyFields?: false | AboutServerPayloadKeySpecifier | (() => undefined | AboutServerPayloadKeySpecifier), fields?: AboutServerPayloadFieldPolicy, }, + AboutWebUI?: Omit & { + keyFields?: false | AboutWebUIKeySpecifier | (() => undefined | AboutWebUIKeySpecifier), + fields?: AboutWebUIFieldPolicy, + }, BackupRestoreStatus?: Omit & { keyFields?: false | BackupRestoreStatusKeySpecifier | (() => undefined | BackupRestoreStatusKeySpecifier), fields?: BackupRestoreStatusFieldPolicy, @@ -884,6 +907,10 @@ export type StrictTypedTypePolicies = { keyFields?: false | CheckForServerUpdatesPayloadKeySpecifier | (() => undefined | CheckForServerUpdatesPayloadKeySpecifier), fields?: CheckForServerUpdatesPayloadFieldPolicy, }, + ClearCachedImagesPayload?: Omit & { + keyFields?: false | ClearCachedImagesPayloadKeySpecifier | (() => undefined | ClearCachedImagesPayloadKeySpecifier), + fields?: ClearCachedImagesPayloadFieldPolicy, + }, ClearDownloaderPayload?: Omit & { keyFields?: false | ClearDownloaderPayloadKeySpecifier | (() => undefined | ClearDownloaderPayloadKeySpecifier), fields?: ClearDownloaderPayloadFieldPolicy, @@ -1248,6 +1275,10 @@ export type StrictTypedTypePolicies = { keyFields?: false | ValidateBackupSourceKeySpecifier | (() => undefined | ValidateBackupSourceKeySpecifier), fields?: ValidateBackupSourceFieldPolicy, }, + WebUIUpdateCheck?: Omit & { + keyFields?: false | WebUIUpdateCheckKeySpecifier | (() => undefined | WebUIUpdateCheckKeySpecifier), + fields?: WebUIUpdateCheckFieldPolicy, + }, WebUIUpdateInfo?: Omit & { keyFields?: false | WebUIUpdateInfoKeySpecifier | (() => undefined | WebUIUpdateInfoKeySpecifier), fields?: WebUIUpdateInfoFieldPolicy, diff --git a/src/lib/graphql/generated/graphql.ts b/src/lib/graphql/generated/graphql.ts index 27a5cd9a..8d137f30 100644 --- a/src/lib/graphql/generated/graphql.ts +++ b/src/lib/graphql/generated/graphql.ts @@ -28,6 +28,12 @@ export type AboutServerPayload = { version: Scalars['String']['output']; }; +export type AboutWebUi = { + __typename?: 'AboutWebUI'; + channel: Scalars['String']['output']; + tag: Scalars['String']['output']; +}; + export enum BackupRestoreState { Failure = 'FAILURE', Idle = 'IDLE', @@ -244,6 +250,21 @@ export type CheckForServerUpdatesPayload = { url: Scalars['String']['output']; }; +export type ClearCachedImagesInput = { + cachedPages?: InputMaybe; + cachedThumbnails?: InputMaybe; + clientMutationId?: InputMaybe; + downloadedThumbnails?: InputMaybe; +}; + +export type ClearCachedImagesPayload = { + __typename?: 'ClearCachedImagesPayload'; + cachedPages?: Maybe; + cachedThumbnails?: Maybe; + clientMutationId?: Maybe; + downloadedThumbnails?: Maybe; +}; + export type ClearDownloaderInput = { clientMutationId?: InputMaybe; }; @@ -907,6 +928,7 @@ export type MultiSelectListPreference = { export type Mutation = { __typename?: 'Mutation'; + clearCachedImages: ClearCachedImagesPayload; clearDownloader: ClearDownloaderPayload; createBackup: CreateBackupPayload; createCategory: CreateCategoryPayload; @@ -957,6 +979,11 @@ export type Mutation = { }; +export type MutationClearCachedImagesArgs = { + input: ClearCachedImagesInput; +}; + + export type MutationClearDownloaderArgs = { input: ClearDownloaderInput; }; @@ -1294,13 +1321,13 @@ export type Preference = CheckBoxPreference | EditTextPreference | ListPreferenc export type Query = { __typename?: 'Query'; aboutServer: AboutServerPayload; - aboutWebUI: WebUiUpdateInfo; + aboutWebUI: AboutWebUi; categories: CategoryNodeList; category: CategoryType; chapter: ChapterType; chapters: ChapterNodeList; checkForServerUpdates: Array; - checkForWebUIUpdate: WebUiUpdateInfo; + checkForWebUIUpdate: WebUiUpdateCheck; downloadStatus: DownloadStatus; extension: ExtensionType; extensions: ExtensionNodeList; @@ -2055,6 +2082,13 @@ export enum WebUiInterface { Electron = 'ELECTRON' } +export type WebUiUpdateCheck = { + __typename?: 'WebUIUpdateCheck'; + channel: Scalars['String']['output']; + tag: Scalars['String']['output']; + updateAvailable: Scalars['Boolean']['output']; +}; + export type WebUiUpdateInfo = { __typename?: 'WebUIUpdateInfo'; channel: Scalars['String']['output']; @@ -2109,9 +2143,9 @@ export type PartialUpdaterStatusFragment = { __typename?: 'UpdateStatus', isRunn export type FullUpdaterStatusFragment = { __typename?: 'UpdateStatus', isRunning: boolean, completeJobs: { __typename?: 'UpdateStatusType', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', unreadCount: number, downloadCount: number, artist?: string | null, author?: string | null, chaptersLastFetchedAt?: any | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, inLibraryAt: any, initialized: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, thumbnailUrl?: string | null, title: string, url: string, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, meta: Array<{ __typename?: 'MangaMetaType', key: string, value: string }>, source?: { __typename?: 'SourceType', displayName: string, iconUrl: string, id: any, isConfigurable: boolean, isNsfw: boolean, lang: string, name: string, supportsLatest: boolean } | null }> } }, failedJobs: { __typename?: 'UpdateStatusType', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null }> } }, pendingJobs: { __typename?: 'UpdateStatusType', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null }> } }, runningJobs: { __typename?: 'UpdateStatusType', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null }> } }, skippedJobs: { __typename?: 'UpdateStatusType', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null }> } }, updatingCategories: { __typename?: 'UpdateStatusCategoryType', categories: { __typename?: 'CategoryNodeList', totalCount: number, nodes: Array<{ __typename?: 'CategoryType', id: number, name: string, includeInUpdate: IncludeInUpdate }> } }, skippedCategories: { __typename?: 'UpdateStatusCategoryType', categories: { __typename?: 'CategoryNodeList', totalCount: number, nodes: Array<{ __typename?: 'CategoryType', id: number, name: string, includeInUpdate: IncludeInUpdate }> } } }; -export type WebuiUpdateInfoFragment = { __typename?: 'WebUIUpdateInfo', channel: string, tag: string, updateAvailable: boolean }; +export type WebuiUpdateInfoFragment = { __typename?: 'WebUIUpdateInfo', channel: string, tag: string }; -export type WebuiUpdateStatusFragment = { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string, updateAvailable: boolean } }; +export type WebuiUpdateStatusFragment = { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string } }; export type ServerSettingsFragment = { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyHost: string, socksProxyPort: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadAheadLimit: number, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, basicAuthEnabled: boolean, basicAuthUsername: string, basicAuthPassword: string, debugLogsEnabled: boolean, gqlDebugLogsEnabled: boolean, systemTrayEnabled: boolean, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string }; @@ -2218,6 +2252,7 @@ export type UpdateChapterMutationVariables = Exact<{ getRead: Scalars['Boolean']['input']; getLastPageRead: Scalars['Boolean']['input']; id: Scalars['Int']['input']; + chapterIdToDelete: Scalars['Int']['input']; deleteChapter: Scalars['Boolean']['input']; mangaId: Scalars['Int']['input']; downloadAhead: Scalars['Boolean']['input']; @@ -2414,7 +2449,7 @@ export type UpdateWebuiMutationVariables = Exact<{ }>; -export type UpdateWebuiMutation = { __typename?: 'Mutation', updateWebUI: { __typename?: 'WebUIUpdatePayload', clientMutationId?: string | null, updateStatus: { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string, updateAvailable: boolean } } } }; +export type UpdateWebuiMutation = { __typename?: 'Mutation', updateWebUI: { __typename?: 'WebUIUpdatePayload', clientMutationId?: string | null, updateStatus: { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string } } } }; export type ResetServerSettingsMutationVariables = Exact<{ input: ResetSettingsInput; @@ -2604,7 +2639,7 @@ export type GetMangasQuery = { __typename?: 'Query', mangas: { __typename?: 'Man export type GetAboutQueryVariables = Exact<{ [key: string]: never; }>; -export type GetAboutQuery = { __typename?: 'Query', aboutServer: { __typename?: 'AboutServerPayload', buildTime: any, buildType: string, discord: string, github: string, name: string, revision: string, version: string }, aboutWebUI: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string, updateAvailable: boolean } }; +export type GetAboutQuery = { __typename?: 'Query', aboutServer: { __typename?: 'AboutServerPayload', buildTime: any, buildType: string, discord: string, github: string, name: string, revision: string, version: string }, aboutWebUI: { __typename?: 'AboutWebUI', channel: string, tag: string } }; export type CheckForServerUpdatesQueryVariables = Exact<{ [key: string]: never; }>; @@ -2614,12 +2649,12 @@ export type CheckForServerUpdatesQuery = { __typename?: 'Query', checkForServerU export type CheckForWebuiUpdateQueryVariables = Exact<{ [key: string]: never; }>; -export type CheckForWebuiUpdateQuery = { __typename?: 'Query', checkForWebUIUpdate: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string, updateAvailable: boolean } }; +export type CheckForWebuiUpdateQuery = { __typename?: 'Query', checkForWebUIUpdate: { __typename?: 'WebUIUpdateCheck', channel: string, tag: string, updateAvailable: boolean } }; export type GetWebuiUpdateStatusQueryVariables = Exact<{ [key: string]: never; }>; -export type GetWebuiUpdateStatusQuery = { __typename?: 'Query', getWebUIUpdateStatus: { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string, updateAvailable: boolean } } }; +export type GetWebuiUpdateStatusQuery = { __typename?: 'Query', getWebUIUpdateStatus: { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string } } }; export type GetServerSettingsQueryVariables = Exact<{ [key: string]: never; }>; @@ -2656,7 +2691,7 @@ export type DownloadStatusSubscription = { __typename?: 'Subscription', download export type WebuiUpdateSubscriptionVariables = Exact<{ [key: string]: never; }>; -export type WebuiUpdateSubscription = { __typename?: 'Subscription', webUIUpdateStatusChange: { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string, updateAvailable: boolean } } }; +export type WebuiUpdateSubscription = { __typename?: 'Subscription', webUIUpdateStatusChange: { __typename?: 'WebUIUpdateStatus', progress: number, state: UpdateState, info: { __typename?: 'WebUIUpdateInfo', channel: string, tag: string } } }; export type UpdaterSubscriptionVariables = Exact<{ [key: string]: never; }>; diff --git a/src/lib/graphql/mutations/ChapterMutation.ts b/src/lib/graphql/mutations/ChapterMutation.ts index 06c59314..e4a9fb77 100644 --- a/src/lib/graphql/mutations/ChapterMutation.ts +++ b/src/lib/graphql/mutations/ChapterMutation.ts @@ -88,6 +88,7 @@ export const UPDATE_CHAPTER = gql` $getRead: Boolean! $getLastPageRead: Boolean! $id: Int! + $chapterIdToDelete: Int! $deleteChapter: Boolean! $mangaId: Int! $downloadAhead: Boolean! @@ -109,7 +110,7 @@ export const UPDATE_CHAPTER = gql` } } } - deleteDownloadedChapter(input: { id: $id }) @include(if: $deleteChapter) { + deleteDownloadedChapter(input: { id: $chapterIdToDelete }) @include(if: $deleteChapter) { clientMutationId chapters { id diff --git a/src/lib/graphql/queries/ServerInfoQuery.ts b/src/lib/graphql/queries/ServerInfoQuery.ts index 1e84a0d2..b1d8b9d5 100644 --- a/src/lib/graphql/queries/ServerInfoQuery.ts +++ b/src/lib/graphql/queries/ServerInfoQuery.ts @@ -7,7 +7,7 @@ */ import gql from 'graphql-tag'; -import { WEBUI_UPDATE_INFO, WEBUI_UPDATE_STATUS } from '@/lib/graphql/Fragments'; +import { WEBUI_UPDATE_STATUS } from '@/lib/graphql/Fragments'; export const GET_ABOUT = gql` query GET_ABOUT { @@ -23,7 +23,6 @@ export const GET_ABOUT = gql` aboutWebUI { channel tag - updateAvailable } } `; @@ -39,10 +38,11 @@ export const CHECK_FOR_SERVER_UPDATES = gql` `; export const CHECK_FOR_WEBUI_UPDATE = gql` - ${WEBUI_UPDATE_INFO} query CHECK_FOR_WEBUI_UPDATE { checkForWebUIUpdate { - ...WEBUI_UPDATE_INFO + channel + tag + updateAvailable } } `; diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index 86efec84..9ebb9bcc 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -1498,10 +1498,13 @@ export class RequestManager { public updateChapter( id: number, - patch: UpdateChapterPatchInput & { deleteChapter?: boolean; downloadAheadMangaId?: number }, + patch: UpdateChapterPatchInput & { + chapterIdToDelete?: number; + downloadAheadMangaId?: number; + }, options?: MutationOptions, ): AbortableApolloMutationResponse { - const { deleteChapter, downloadAheadMangaId = -1, ...updatePatch } = patch; + const { chapterIdToDelete = -1, downloadAheadMangaId = -1, ...updatePatch } = patch; return this.doRequest( GQLMethod.MUTATION, @@ -1512,7 +1515,8 @@ export class RequestManager { getBookmarked: patch.isBookmarked != null, getRead: patch.isRead != null, getLastPageRead: patch.lastPageRead != null, - deleteChapter: !!deleteChapter, + chapterIdToDelete, + deleteChapter: chapterIdToDelete >= 0, mangaId: downloadAheadMangaId, downloadAhead: downloadAheadMangaId !== -1, }, diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx index d6d801b4..77eea13e 100644 --- a/src/screens/Reader.tsx +++ b/src/screens/Reader.tsx @@ -169,6 +169,8 @@ export function Reader() { const [pageToScrollTo, setPageToScrollTo] = useState(undefined); const { setOverride, setTitle } = useContext(NavBarContext); const [retrievingNextChapter, setRetrievingNextChapter] = useState(false); + const { data: mangaChaptersData } = requestManager.useGetMangaChapters(mangaId, { nextFetchPolicy: 'standby' }); + const mangaChapters = mangaChaptersData?.chapters.nodes; const { settings: defaultSettings, loading: areDefaultSettingsLoading } = useDefaultReaderSettings(); const [settings, setSettings] = useState(getReaderSettingsFor(manga, defaultSettings)); @@ -176,11 +178,27 @@ export function Reader() { const { settings: metadataSettings } = useMetadataServerSettings(); const updateChapter = (patch: UpdateChapterPatchInput) => { - const shouldDeleteChapter = - !!patch.isRead && - metadataSettings.deleteChaptersAutoMarkedRead && - chapter.isDownloaded && - (!chapter.isBookmarked || metadataSettings.deleteChaptersWithBookmark); + const isAutoDeletionEnabled = !!patch.isRead && !!metadataSettings.deleteChaptersWhileReading; + + const getChapterIdToDelete = () => { + if (!isAutoDeletionEnabled || !mangaChapters) { + return -1; + } + + const chapterToDeleteSourceOrder = Number(chapterIndex) - (metadataSettings.deleteChaptersWhileReading - 1); + const chapterToDelete = mangaChapters.find( + (mangaChapter) => mangaChapter.sourceOrder === chapterToDeleteSourceOrder, + ); + + const shouldDeleteChapter = + chapterToDelete?.isDownloaded && + (!chapterToDelete?.isBookmarked || metadataSettings.deleteChaptersWithBookmark); + if (!shouldDeleteChapter) { + return -1; + } + + return chapterToDelete.id; + }; const shouldDownloadAhead = chapter.manga.inLibrary && !chapter.isRead && !!patch.isRead && isDownloadAheadEnabled; @@ -188,7 +206,7 @@ export function Reader() { requestManager .updateChapter(chapter.id, { ...patch, - deleteChapter: shouldDeleteChapter, + chapterIdToDelete: getChapterIdToDelete(), downloadAheadMangaId: shouldDownloadAhead ? chapter.manga.id : undefined, }) .response.catch(() => {}); diff --git a/src/screens/settings/DownloadSettings.tsx b/src/screens/settings/DownloadSettings.tsx index 14105c04..0d03be8e 100644 --- a/src/screens/settings/DownloadSettings.tsx +++ b/src/screens/settings/DownloadSettings.tsx @@ -20,6 +20,7 @@ import { DownloadAheadSetting } from '@/components/settings/downloads/DownloadAh import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts'; import { convertToGqlMeta, requestUpdateServerMetadata } from '@/util/metadata.ts'; import { makeToast } from '@/components/util/Toast.tsx'; +import { DeleteChaptersWhileReadingSetting } from '@/components/settings/downloads/DeleteChaptersWhileReadingSetting.tsx'; type DownloadSettingsType = Pick< ServerSettings, @@ -97,12 +98,12 @@ export const DownloadSettings = () => { - Delete chapters + {t('download.settings.delete_chapters.title')} } > - + { /> + + updateMetadataSetting('deleteChaptersWhileReading', chapterToDelete) + } + /> - - - updateMetadataSetting('deleteChaptersAutoMarkedRead', e.target.checked)} - /> - - - - + { diff --git a/src/util/metadataServerSettings.ts b/src/util/metadataServerSettings.ts index a22d8b2f..8cf80621 100644 --- a/src/util/metadataServerSettings.ts +++ b/src/util/metadataServerSettings.ts @@ -12,7 +12,7 @@ import { convertFromGqlMeta, getMetadataFrom } from '@/util/metadata'; export const getDefaultSettings = (): MetadataServerSettings => ({ deleteChaptersManuallyMarkedRead: false, - deleteChaptersAutoMarkedRead: false, + deleteChaptersWhileReading: 0, deleteChaptersWithBookmark: false, }); diff --git a/versionToServerVersionMapping.json b/versionToServerVersionMapping.json index acad1a08..44e05c95 100644 --- a/versionToServerVersionMapping.json +++ b/versionToServerVersionMapping.json @@ -1,6 +1,6 @@ [ { "uiVersion": "PREVIEW", - "serverVersion": "r1427" + "serverVersion": "r1431" } ]