Feature/automatic chapter deletion more options (#484)

* Add missing translations keys

* [Codegen] Update to latest server schema changes

* [VersionMapping] Require server version "r1431" for preview

* Rename "deleteChaptersAutoMarkedRead" metadata setting

* Add more options to auto delete chapters while reading

Makes it possible to delete the last to fifth to last read chapter instead of only being able to delete the last read chapter
This commit is contained in:
schroda
2023-12-05 22:20:13 +01:00
committed by GitHub
parent ee03c56684
commit ad0f072651
15 changed files with 230 additions and 42 deletions

View File

@@ -102,7 +102,7 @@ export const ChapterCard: React.FC<IProps> = (props: IProps) => {
requestManager.updateChapter(chapter.id, {
[key]: value,
lastPageRead: key === 'isRead' ? 0 : undefined,
deleteChapter: !!chapterIdsToDelete.length,
chapterIdToDelete: chapterIdsToDelete[0],
});
};

View File

@@ -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 (
<SelectSetting
settingName={t('download.settings.delete_chapters.while_reading.label.title')}
value={normalizedChapterToDelete}
defaultValue={0}
values={CHAPTERS_TO_DELETE_SELECT_VALUES}
handleChange={handleChange}
/>
);
};

View File

@@ -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",

View File

@@ -416,7 +416,6 @@ export const WEBUI_UPDATE_INFO = gql`
fragment WEBUI_UPDATE_INFO on WebUIUpdateInfo {
channel
tag
updateAvailable
}
`;

View File

@@ -10,6 +10,11 @@ export type AboutServerPayloadFieldPolicy = {
revision?: FieldPolicy<any> | FieldReadFunction<any>,
version?: FieldPolicy<any> | FieldReadFunction<any>
};
export type AboutWebUIKeySpecifier = ('channel' | 'tag' | AboutWebUIKeySpecifier)[];
export type AboutWebUIFieldPolicy = {
channel?: FieldPolicy<any> | FieldReadFunction<any>,
tag?: FieldPolicy<any> | FieldReadFunction<any>
};
export type BackupRestoreStatusKeySpecifier = ('mangaProgress' | 'state' | 'totalManga' | BackupRestoreStatusKeySpecifier)[];
export type BackupRestoreStatusFieldPolicy = {
mangaProgress?: FieldPolicy<any> | FieldReadFunction<any>,
@@ -105,6 +110,13 @@ export type CheckForServerUpdatesPayloadFieldPolicy = {
tag?: FieldPolicy<any> | FieldReadFunction<any>,
url?: FieldPolicy<any> | FieldReadFunction<any>
};
export type ClearCachedImagesPayloadKeySpecifier = ('cachedPages' | 'cachedThumbnails' | 'clientMutationId' | 'downloadedThumbnails' | ClearCachedImagesPayloadKeySpecifier)[];
export type ClearCachedImagesPayloadFieldPolicy = {
cachedPages?: FieldPolicy<any> | FieldReadFunction<any>,
cachedThumbnails?: FieldPolicy<any> | FieldReadFunction<any>,
clientMutationId?: FieldPolicy<any> | FieldReadFunction<any>,
downloadedThumbnails?: FieldPolicy<any> | FieldReadFunction<any>
};
export type ClearDownloaderPayloadKeySpecifier = ('clientMutationId' | 'downloadStatus' | ClearDownloaderPayloadKeySpecifier)[];
export type ClearDownloaderPayloadFieldPolicy = {
clientMutationId?: FieldPolicy<any> | FieldReadFunction<any>,
@@ -390,8 +402,9 @@ export type MultiSelectListPreferenceFieldPolicy = {
title?: FieldPolicy<any> | FieldReadFunction<any>,
visible?: FieldPolicy<any> | FieldReadFunction<any>
};
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<any> | FieldReadFunction<any>,
clearDownloader?: FieldPolicy<any> | FieldReadFunction<any>,
createBackup?: FieldPolicy<any> | FieldReadFunction<any>,
createCategory?: FieldPolicy<any> | FieldReadFunction<any>,
@@ -814,6 +827,12 @@ export type ValidateBackupSourceFieldPolicy = {
id?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>
};
export type WebUIUpdateCheckKeySpecifier = ('channel' | 'tag' | 'updateAvailable' | WebUIUpdateCheckKeySpecifier)[];
export type WebUIUpdateCheckFieldPolicy = {
channel?: FieldPolicy<any> | FieldReadFunction<any>,
tag?: FieldPolicy<any> | FieldReadFunction<any>,
updateAvailable?: FieldPolicy<any> | FieldReadFunction<any>
};
export type WebUIUpdateInfoKeySpecifier = ('channel' | 'tag' | 'updateAvailable' | WebUIUpdateInfoKeySpecifier)[];
export type WebUIUpdateInfoFieldPolicy = {
channel?: FieldPolicy<any> | FieldReadFunction<any>,
@@ -836,6 +855,10 @@ export type StrictTypedTypePolicies = {
keyFields?: false | AboutServerPayloadKeySpecifier | (() => undefined | AboutServerPayloadKeySpecifier),
fields?: AboutServerPayloadFieldPolicy,
},
AboutWebUI?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | AboutWebUIKeySpecifier | (() => undefined | AboutWebUIKeySpecifier),
fields?: AboutWebUIFieldPolicy,
},
BackupRestoreStatus?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | BackupRestoreStatusKeySpecifier | (() => undefined | BackupRestoreStatusKeySpecifier),
fields?: BackupRestoreStatusFieldPolicy,
@@ -884,6 +907,10 @@ export type StrictTypedTypePolicies = {
keyFields?: false | CheckForServerUpdatesPayloadKeySpecifier | (() => undefined | CheckForServerUpdatesPayloadKeySpecifier),
fields?: CheckForServerUpdatesPayloadFieldPolicy,
},
ClearCachedImagesPayload?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | ClearCachedImagesPayloadKeySpecifier | (() => undefined | ClearCachedImagesPayloadKeySpecifier),
fields?: ClearCachedImagesPayloadFieldPolicy,
},
ClearDownloaderPayload?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | ClearDownloaderPayloadKeySpecifier | (() => undefined | ClearDownloaderPayloadKeySpecifier),
fields?: ClearDownloaderPayloadFieldPolicy,
@@ -1248,6 +1275,10 @@ export type StrictTypedTypePolicies = {
keyFields?: false | ValidateBackupSourceKeySpecifier | (() => undefined | ValidateBackupSourceKeySpecifier),
fields?: ValidateBackupSourceFieldPolicy,
},
WebUIUpdateCheck?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | WebUIUpdateCheckKeySpecifier | (() => undefined | WebUIUpdateCheckKeySpecifier),
fields?: WebUIUpdateCheckFieldPolicy,
},
WebUIUpdateInfo?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | WebUIUpdateInfoKeySpecifier | (() => undefined | WebUIUpdateInfoKeySpecifier),
fields?: WebUIUpdateInfoFieldPolicy,

View File

@@ -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<Scalars['Boolean']['input']>;
cachedThumbnails?: InputMaybe<Scalars['Boolean']['input']>;
clientMutationId?: InputMaybe<Scalars['String']['input']>;
downloadedThumbnails?: InputMaybe<Scalars['Boolean']['input']>;
};
export type ClearCachedImagesPayload = {
__typename?: 'ClearCachedImagesPayload';
cachedPages?: Maybe<Scalars['Boolean']['output']>;
cachedThumbnails?: Maybe<Scalars['Boolean']['output']>;
clientMutationId?: Maybe<Scalars['String']['output']>;
downloadedThumbnails?: Maybe<Scalars['Boolean']['output']>;
};
export type ClearDownloaderInput = {
clientMutationId?: InputMaybe<Scalars['String']['input']>;
};
@@ -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<CheckForServerUpdatesPayload>;
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<string>, 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; }>;

View File

@@ -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

View File

@@ -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
}
}
`;

View File

@@ -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<UpdateChapterMutation, UpdateChapterMutationVariables>,
): AbortableApolloMutationResponse<UpdateChapterMutation> {
const { deleteChapter, downloadAheadMangaId = -1, ...updatePatch } = patch;
const { chapterIdToDelete = -1, downloadAheadMangaId = -1, ...updatePatch } = patch;
return this.doRequest<UpdateChapterMutation, UpdateChapterMutationVariables>(
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,
},

View File

@@ -169,6 +169,8 @@ export function Reader() {
const [pageToScrollTo, setPageToScrollTo] = useState<number | undefined>(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(() => {});

View File

@@ -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 = () => {
<List
subheader={
<ListSubheader component="div" id="download-settings-auto-download">
Delete chapters
{t('download.settings.delete_chapters.title')}
</ListSubheader>
}
>
<ListItem>
<ListItemText primary="Delete chapter after manually marking it as read" />
<ListItemText primary={t('download.settings.delete_chapters.label.manually_marked_as_read')} />
<ListItemSecondaryAction>
<Switch
edge="end"
@@ -113,18 +114,14 @@ export const DownloadSettings = () => {
/>
</ListItemSecondaryAction>
</ListItem>
<DeleteChaptersWhileReadingSetting
chapterToDelete={metadataSettings.deleteChaptersWhileReading}
handleChange={(chapterToDelete) =>
updateMetadataSetting('deleteChaptersWhileReading', chapterToDelete)
}
/>
<ListItem>
<ListItemText primary="Delete finished chapters while reading" />
<ListItemSecondaryAction>
<Switch
edge="end"
checked={metadataSettings.deleteChaptersAutoMarkedRead}
onChange={(e) => updateMetadataSetting('deleteChaptersAutoMarkedRead', e.target.checked)}
/>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemText primary="Allow deleting bookmarked chapters" />
<ListItemText primary={t('download.settings.delete_chapters.label.allow_deletion_of_bookmarked')} />
<ListItemSecondaryAction>
<Switch
edge="end"

View File

@@ -230,7 +230,7 @@ export enum ChapterOffset {
export type MetadataServerSettings = {
deleteChaptersManuallyMarkedRead: boolean;
deleteChaptersAutoMarkedRead: boolean;
deleteChaptersWhileReading: number;
deleteChaptersWithBookmark: boolean;
};

View File

@@ -83,6 +83,9 @@ const migrations: IMetadataMigration[] = [
{
keys: [{ oldKey: 'loadNextonEnding', newKey: 'loadNextOnEnding' }],
},
{
keys: [{ oldKey: 'deleteChaptersAutoMarkedRead', newKey: 'deleteChaptersWhileReading' }],
},
];
const getAppKeyPrefixForMigration = (migrationId: number): string => {

View File

@@ -12,7 +12,7 @@ import { convertFromGqlMeta, getMetadataFrom } from '@/util/metadata';
export const getDefaultSettings = (): MetadataServerSettings => ({
deleteChaptersManuallyMarkedRead: false,
deleteChaptersAutoMarkedRead: false,
deleteChaptersWhileReading: 0,
deleteChaptersWithBookmark: false,
});

View File

@@ -1,6 +1,6 @@
[
{
"uiVersion": "PREVIEW",
"serverVersion": "r1427"
"serverVersion": "r1431"
}
]