Add option to update tracking on manual mark as read

This commit is contained in:
schroda
2024-04-20 23:15:02 +02:00
parent d35ef4fa7f
commit 1a16dba7bd
12 changed files with 66 additions and 20 deletions

View File

@@ -1127,7 +1127,9 @@
} }
}, },
"label": { "label": {
"update_progress_reading": "Update progress after reading" "update_progress_manual": "Update progress after manually marking as read",
"update_progress_reading": "Update progress after reading",
"update_progress_reading_description": "Does not work when marking multiple manga as read at once"
}, },
"title": { "title": {
"settings": "Tracking settings", "settings": "Tracking settings",

View File

@@ -23,6 +23,8 @@ import {
ChapterAction, ChapterAction,
ChapterBookmarkInfo, ChapterBookmarkInfo,
ChapterDownloadInfo, ChapterDownloadInfo,
ChapterIdInfo,
ChapterMangaInfo,
ChapterReadInfo, ChapterReadInfo,
ChapterRealUrlInfo, ChapterRealUrlInfo,
Chapters, Chapters,
@@ -38,7 +40,12 @@ import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings
type BaseProps = { onClose: () => void }; type BaseProps = { onClose: () => void };
type SingleModeProps = { type SingleModeProps = {
chapter: ChapterDownloadInfo & ChapterBookmarkInfo & ChapterReadInfo & ChapterRealUrlInfo; chapter: ChapterIdInfo &
ChapterMangaInfo &
ChapterDownloadInfo &
ChapterBookmarkInfo &
ChapterReadInfo &
ChapterRealUrlInfo;
allChapters: TChapter[]; allChapters: TChapter[];
handleSelection?: SelectableCollectionReturnType<TChapter['id']>['handleSelection']; handleSelection?: SelectableCollectionReturnType<TChapter['id']>['handleSelection'];
canBeDownloaded: boolean; canBeDownloaded: boolean;
@@ -109,7 +116,7 @@ export const ChapterActionMenuItems = ({
} }
} }
const getChapters = (): (ChapterDownloadInfo & ChapterBookmarkInfo & ChapterReadInfo)[] => { const getChapters = (): SingleModeProps['chapter'][] => {
// select mode // select mode
if (!chapter) { if (!chapter) {
return ChaptersWithMeta.getChapters(chaptersWithMeta); return ChaptersWithMeta.getChapters(chaptersWithMeta);
@@ -139,6 +146,7 @@ export const ChapterActionMenuItems = ({
Chapters.performAction(actualAction, Chapters.getIds(chapters), { Chapters.performAction(actualAction, Chapters.getIds(chapters), {
chapters, chapters,
wasManuallyMarkedAsRead: true, wasManuallyMarkedAsRead: true,
trackProgressMangaId: chapters[0]?.mangaId,
}).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction')); }).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction'));
onClose(); onClose();
}; };

View File

@@ -9,7 +9,7 @@
import { t as translate } from 'i18next'; import { t as translate } from 'i18next';
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { DocumentNode } from '@apollo/client'; import { DocumentNode } from '@apollo/client';
import { ChapterOffset, TChapter, TranslationKey } from '@/typings.ts'; import { ChapterOffset, TChapter, TManga, TranslationKey } from '@/typings.ts';
import { makeToast } from '@/components/util/Toast.tsx'; import { makeToast } from '@/components/util/Toast.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { getMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts'; import { getMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
@@ -78,6 +78,7 @@ export const actionToTranslationKey: {
}; };
export type ChapterIdInfo = Pick<TChapter, 'id'>; export type ChapterIdInfo = Pick<TChapter, 'id'>;
export type ChapterMangaInfo = Pick<TChapter, 'mangaId'>;
export type ChapterDownloadInfo = ChapterIdInfo & Pick<TChapter, 'isDownloaded'>; export type ChapterDownloadInfo = ChapterIdInfo & Pick<TChapter, 'isDownloaded'>;
export type ChapterBookmarkInfo = ChapterIdInfo & Pick<TChapter, 'isBookmarked'>; export type ChapterBookmarkInfo = ChapterIdInfo & Pick<TChapter, 'isBookmarked'>;
export type ChapterReadInfo = ChapterIdInfo & Pick<TChapter, 'isRead'>; export type ChapterReadInfo = ChapterIdInfo & Pick<TChapter, 'isRead'>;
@@ -207,8 +208,10 @@ export class Chapters {
static async markAsRead( static async markAsRead(
chapters: (ChapterDownloadInfo & ChapterBookmarkInfo)[], chapters: (ChapterDownloadInfo & ChapterBookmarkInfo)[],
wasManuallyMarkedAsRead: boolean = false, wasManuallyMarkedAsRead: boolean = false,
trackProgressMangaId?: TManga['id'],
): Promise<void> { ): Promise<void> {
const { deleteChaptersManuallyMarkedRead, deleteChaptersWithBookmark } = await getMetadataServerSettings(); const { deleteChaptersManuallyMarkedRead, deleteChaptersWithBookmark, updateProgressManualMarkRead } =
await getMetadataServerSettings();
const chapterIdsToDelete = const chapterIdsToDelete =
deleteChaptersManuallyMarkedRead && wasManuallyMarkedAsRead deleteChaptersManuallyMarkedRead && wasManuallyMarkedAsRead
? Chapters.getIds(Chapters.getDeletable(chapters, deleteChaptersWithBookmark)) ? Chapters.getIds(Chapters.getDeletable(chapters, deleteChaptersWithBookmark))
@@ -221,6 +224,8 @@ export class Chapters {
isRead: true, isRead: true,
lastPageRead: 0, lastPageRead: 0,
chapterIdsToDelete, chapterIdsToDelete,
trackProgressMangaId:
updateProgressManualMarkRead && wasManuallyMarkedAsRead ? trackProgressMangaId : undefined,
}).response, }).response,
); );
} }
@@ -268,14 +273,17 @@ export class Chapters {
chapterIds: number[], chapterIds: number[],
{ {
wasManuallyMarkedAsRead, wasManuallyMarkedAsRead,
trackProgressMangaId,
chapters, chapters,
}: Action extends 'mark_as_read' }: Action extends 'mark_as_read'
? { ? {
wasManuallyMarkedAsRead: boolean; wasManuallyMarkedAsRead: boolean;
trackProgressMangaId?: TManga['id'];
chapters: (ChapterDownloadInfo & ChapterBookmarkInfo & ChapterReadInfo)[]; chapters: (ChapterDownloadInfo & ChapterBookmarkInfo & ChapterReadInfo)[];
} }
: { : {
wasManuallyMarkedAsRead?: never; wasManuallyMarkedAsRead?: never;
trackProgressMangaId?: never;
chapters?: never; chapters?: never;
}, },
): Promise<void> { ): Promise<void> {
@@ -285,7 +293,7 @@ export class Chapters {
case 'delete': case 'delete':
return Chapters.delete(chapterIds); return Chapters.delete(chapterIds);
case 'mark_as_read': case 'mark_as_read':
return Chapters.markAsRead(chapters!, wasManuallyMarkedAsRead!); return Chapters.markAsRead(chapters!, wasManuallyMarkedAsRead!, trackProgressMangaId);
case 'mark_as_unread': case 'mark_as_unread':
return Chapters.markAsUnread(chapterIds); return Chapters.markAsUnread(chapterIds);
case 'bookmark': case 'bookmark':

View File

@@ -300,7 +300,7 @@ export class Mangas {
static async markAsRead(mangaIds: number[], wasManuallyMarkedAsRead: boolean = false): Promise<void> { static async markAsRead(mangaIds: number[], wasManuallyMarkedAsRead: boolean = false): Promise<void> {
const chapters = await Mangas.getChapterIdsWithState(mangaIds, { isRead: false }); const chapters = await Mangas.getChapterIdsWithState(mangaIds, { isRead: false });
return Chapters.markAsRead(chapters, wasManuallyMarkedAsRead); return Chapters.markAsRead(chapters, wasManuallyMarkedAsRead, mangaIds.length === 1 ? mangaIds[0] : undefined);
} }
static async markAsUnread(mangaIds: number[]): Promise<void> { static async markAsUnread(mangaIds: number[]): Promise<void> {

View File

@@ -328,6 +328,7 @@ export const FULL_CHAPTER_FIELDS = gql`
isRead isRead
lastPageRead lastPageRead
lastReadAt lastReadAt
mangaId
manga { manga {
id id
title title

File diff suppressed because one or more lines are too long

View File

@@ -146,6 +146,7 @@ export const UPDATE_CHAPTER = gql`
`; `;
export const UPDATE_CHAPTERS = gql` export const UPDATE_CHAPTERS = gql`
${BASE_TRACK_RECORD_FIELDS}
mutation UPDATE_CHAPTERS( mutation UPDATE_CHAPTERS(
$input: UpdateChaptersInput! $input: UpdateChaptersInput!
$getBookmarked: Boolean! $getBookmarked: Boolean!
@@ -153,6 +154,8 @@ export const UPDATE_CHAPTERS = gql`
$getLastPageRead: Boolean! $getLastPageRead: Boolean!
$chapterIdsToDelete: [Int!]! $chapterIdsToDelete: [Int!]!
$deleteChapters: Boolean! $deleteChapters: Boolean!
$mangaId: Int!
$trackProgress: Boolean!
) { ) {
updateChapters(input: $input) { updateChapters(input: $input) {
clientMutationId clientMutationId
@@ -192,5 +195,10 @@ export const UPDATE_CHAPTERS = gql`
} }
} }
} }
trackProgress(input: { mangaId: $mangaId }) @include(if: $trackProgress) {
trackRecords {
...BASE_TRACK_RECORD_FIELDS
}
}
} }
`; `;

View File

@@ -48,6 +48,7 @@ const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
// tracking // tracking
'updateProgressAfterReading', 'updateProgressAfterReading',
'updateProgressManualMarkRead',
]; ];
/** /**

View File

@@ -44,6 +44,7 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
// tracking // tracking
updateProgressAfterReading: true, updateProgressAfterReading: true,
updateProgressManualMarkRead: false,
}); });
export const convertSettingsToMetadata = ( export const convertSettingsToMetadata = (

View File

@@ -287,6 +287,7 @@ import {
TRACKER_UPDATE_BIND, TRACKER_UPDATE_BIND,
} from '@/lib/graphql/mutations/TrackerMutation.ts'; } from '@/lib/graphql/mutations/TrackerMutation.ts';
import { ControlledPromise } from '@/lib/ControlledPromise.ts'; import { ControlledPromise } from '@/lib/ControlledPromise.ts';
import { TManga } from '@/typings.ts';
enum GQLMethod { enum GQLMethod {
QUERY = 'QUERY', QUERY = 'QUERY',
@@ -1942,10 +1943,10 @@ export class RequestManager {
public updateChapters( public updateChapters(
ids: number[], ids: number[],
patch: UpdateChapterPatchInput & { chapterIdsToDelete?: number[] }, patch: UpdateChapterPatchInput & { chapterIdsToDelete?: number[]; trackProgressMangaId?: TManga['id'] },
options?: MutationOptions<UpdateChaptersMutation, UpdateChaptersMutationVariables>, options?: MutationOptions<UpdateChaptersMutation, UpdateChaptersMutationVariables>,
): AbortableApolloMutationResponse<UpdateChaptersMutation> { ): AbortableApolloMutationResponse<UpdateChaptersMutation> {
const { chapterIdsToDelete = [], ...updatePatch } = patch; const { chapterIdsToDelete = [], trackProgressMangaId = -1, ...updatePatch } = patch;
return this.doRequest<UpdateChaptersMutation, UpdateChaptersMutationVariables>( return this.doRequest<UpdateChaptersMutation, UpdateChaptersMutationVariables>(
GQLMethod.MUTATION, GQLMethod.MUTATION,
@@ -1957,6 +1958,8 @@ export class RequestManager {
getLastPageRead: patch.lastPageRead != null, getLastPageRead: patch.lastPageRead != null,
chapterIdsToDelete, chapterIdsToDelete,
deleteChapters: !!chapterIdsToDelete.length, deleteChapters: !!chapterIdsToDelete.length,
mangaId: trackProgressMangaId,
trackProgress: trackProgressMangaId >= 0,
}, },
options, options,
); );

View File

@@ -35,7 +35,7 @@ export const TrackingSettings = () => {
}, [t]); }, [t]);
const { const {
settings: { updateProgressAfterReading }, settings: { updateProgressAfterReading, updateProgressManualMarkRead },
} = useMetadataServerSettings(); } = useMetadataServerSettings();
const updateTrackingSettings = createUpdateMetadataServerSettings<keyof MetadataTrackingSettings>(() => const updateTrackingSettings = createUpdateMetadataServerSettings<keyof MetadataTrackingSettings>(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'), makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
@@ -63,6 +63,17 @@ export const TrackingSettings = () => {
onChange={(e) => updateTrackingSettings('updateProgressAfterReading', e.target.checked)} onChange={(e) => updateTrackingSettings('updateProgressAfterReading', e.target.checked)}
/> />
</ListItem> </ListItem>
<ListItem>
<ListItemText
primary={t('tracking.settings.label.update_progress_manual')}
secondary={t('tracking.setting.label.update_progress_reading_description')}
/>
<Switch
edge="end"
checked={updateProgressManualMarkRead}
onChange={(e) => updateTrackingSettings('updateProgressManualMarkRead', e.target.checked)}
/>
</ListItem>
</List> </List>
<List <List
subheader={ subheader={

View File

@@ -251,6 +251,7 @@ export type MetadataBrowseSettings = {
export type MetadataTrackingSettings = { export type MetadataTrackingSettings = {
updateProgressAfterReading: boolean; updateProgressAfterReading: boolean;
updateProgressManualMarkRead: boolean;
}; };
export type MetadataServerSettings = MetadataDownloadSettings & export type MetadataServerSettings = MetadataDownloadSettings &