-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/mark-chapters-as-read-based-on-tracker-history #708
Changes from all commits
4434467
fee6af5
17ed5f7
7018000
6303749
7e2dacb
d518d8f
d673e50
ffd6f82
90308e6
112857c
7b0ed6e
1a7a8d0
80527ce
9b8f66e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,8 +16,9 @@ import { requestManager } from '@/lib/requests/RequestManager.ts'; | |
import { makeToast } from '@/components/util/Toast.tsx'; | ||
import { TrackManga } from '@/components/tracker/TrackManga.tsx'; | ||
import { Trackers } from '@/lib/data/Trackers.ts'; | ||
import { TManga } from '@/typings.ts'; | ||
import { TChapter, TManga } from '@/typings.ts'; | ||
import { CustomIconButton } from '@/components/atoms/CustomIconButton.tsx'; | ||
import { setChapterAsLastRead } from '@/components/chapter/util.tsx'; | ||
|
||
export const TrackMangaButton = ({ manga }: { manga: TManga }) => { | ||
const { t } = useTranslation(); | ||
|
@@ -28,8 +29,67 @@ export const TrackMangaButton = ({ manga }: { manga: TManga }) => { | |
|
||
const loggedInTrackers = Trackers.getLoggedIn(trackerList.data?.trackers.nodes ?? []); | ||
const trackersInUse = Trackers.getLoggedIn(Trackers.getTrackers(mangaTrackers)); | ||
const mangaChaptersQuery = requestManager.useGetMangaChapters(manga.id, {}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I this the line you are talking about that is fetching the chapters? @schroda There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, it's this
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. If the local manga is behind (last chapter is 100 but last read in tracker is 103) there wont be a way to find the last chapter that match the tracker. In that case, do you propose to not get the last chapter and instead mark every chapter as read?. In the example, the last local chapter is 100 and the last read in tracker is 103, every chapter before 103 should be read; in this case all the local chapters should be marked as read? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I am There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. I can do that. |
||
|
||
const handleClick = (openPopup: () => void) => { | ||
/** | ||
* @description This function fetch the last read for the loged in trackers. | ||
*/ | ||
const refreshTracker = () => | ||
mangaTrackers.map( | ||
async (trackRecord) => | ||
(await requestManager.fetchTrackBind(trackRecord.id).response).data?.fetchTrack.trackRecord | ||
.lastChapterRead, | ||
); | ||
|
||
/** | ||
* @description This function update the tracker reads, and set the local read to the higher chapter read if the local source is lower. | ||
* It will set all chapters part to read based on the tracker read, so if you have read chapter 100; parts 100.1 100.5 and 100.8 will be marked as read. | ||
*/ | ||
const updateChapterFromTracker = async () => { | ||
const updatedTrackerRecords = (await Promise.all(refreshTracker())) as number[]; | ||
|
||
const latestTrackersRead = | ||
Math.max(...updatedTrackerRecords.map((trackData) => trackData)) ?? | ||
manga.trackRecords.nodes.map((trackRecord) => trackRecord.lastChapterRead); | ||
const latestLocalRead = manga.latestReadChapter?.chapterNumber ?? 0; | ||
|
||
// Return a list of all the chapter and chapters parts that match the last chapter in the tracker | ||
const latestLocalChapterOrParts: number[] = | ||
mangaChaptersQuery.data?.chapters.nodes?.reduce((acc: number[], chapter) => { | ||
if ( | ||
chapter.chapterNumber === latestTrackersRead || | ||
Math.floor(chapter.chapterNumber) === latestTrackersRead | ||
) { | ||
acc.push(chapter.chapterNumber); | ||
} | ||
return acc; | ||
}, []) ?? []; | ||
|
||
// The last part of a chapter | ||
const lastLocalChapter = Math.max(...latestLocalChapterOrParts); | ||
|
||
// If the last chapter fetched is lower that the tracker's last read | ||
const localBehindTracker = lastLocalChapter < latestTrackersRead; | ||
|
||
// Fetch new chapters if behind tracker | ||
if (localBehindTracker) { | ||
await requestManager.getMangaChaptersFetch(manga.id, { awaitRefetchQueries: true }).response; | ||
} | ||
if (!localBehindTracker) { | ||
const chapterToBeUpdated = | ||
mangaChaptersQuery.data?.chapters.nodes?.find( | ||
(chapter) => chapter.chapterNumber === lastLocalChapter, | ||
) ?? mangaChaptersQuery.data?.chapters.nodes[0]; | ||
if ( | ||
chapterToBeUpdated && | ||
(latestLocalRead < latestTrackersRead || | ||
(Math.floor(chapterToBeUpdated.chapterNumber) === latestTrackersRead && !chapterToBeUpdated.isRead)) | ||
) { | ||
setChapterAsLastRead(chapterToBeUpdated?.id, mangaChaptersQuery.data?.chapters.nodes as TChapter[]); | ||
} | ||
} | ||
}; | ||
const handleClick = async (openPopup: () => void) => { | ||
if (trackerList.error) { | ||
makeToast(t('tracking.error.label.could_not_load_track_info'), 'error'); | ||
return; | ||
|
@@ -41,6 +101,12 @@ export const TrackMangaButton = ({ manga }: { manga: TManga }) => { | |
} | ||
|
||
openPopup(); | ||
|
||
try { | ||
await updateChapterFromTracker(); | ||
} catch (error) { | ||
makeToast(t('tracking.error.label.could_not_load_track_info'), 'error'); | ||
} | ||
}; | ||
|
||
return ( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* 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 { findIndexOfElement } from '@/components/util/findIndexOfElement.ts'; | ||
|
||
// fieldToSearch string | any[] | ||
export const findElement = <T>( | ||
elements: T[], | ||
fieldToSearch: string, | ||
fieldToMatch: unknown, | ||
isFieldToSearchArray?: boolean, | ||
): T | undefined => { | ||
const index = findIndexOfElement(elements, fieldToSearch, fieldToMatch, isFieldToSearchArray); | ||
|
||
if (!index) { | ||
return undefined; | ||
} | ||
return elements[index]; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* 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/. | ||
*/ | ||
|
||
/** | ||
* | ||
* @param elements The array that will be search | ||
* @param fieldToSearch The key of the elements that will be search | ||
* @param fieldToMatch The value the fieldToSearch key needs to match | ||
* @param isFieldToSearchArray Whether the key of the fieldToSearch is an array or not. Default to false | ||
* @example findIndexOfElement(mangas, "id", passedManga.id) | ||
* @returns The index of the element if found, or undefine if not found. | ||
*/ | ||
export const findIndexOfElement = <T>( | ||
elements: T[], | ||
fieldToSearch: string, | ||
fieldToMatch: unknown, | ||
isFieldToSearchArray: boolean = false, | ||
): number | undefined => { | ||
let elementFoundIndex: number; | ||
|
||
if (isFieldToSearchArray) { | ||
elementFoundIndex = elements.findIndex((element: T | any) => | ||
element[fieldToSearch].some((field: any) => field === fieldToMatch), | ||
); | ||
} else { | ||
// do a some() logic checking for boolean, so fieldToMatch fieldToMatch | ||
elementFoundIndex = elements.findIndex((element: T | any) => element[fieldToSearch] === fieldToMatch); | ||
} | ||
return elementFoundIndex; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* 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 { findIndexOfElement } from '@/components/util/findIndexOfElement.ts'; | ||
|
||
/** | ||
*@description This function takes a element's id and a list of the same element, and it return either the first | ||
of second half of the list using the element id as the pivot point. | ||
* @param elementId The Id of the emeent to be use as pivot. | ||
* @param allElements The list of elements to be firtered. | ||
* @param halfOfList There part of the list to be return. Either the first half or the second. | ||
* @param indexOffset The offsett to set for the index. By default set to 1, so the first have will not include the pivot element | ||
* and the second half will include the first element. | ||
* @returns The first of the second half of a list using the elementId passed as the pivots. | ||
*/ | ||
export const getPartialList = <T>( | ||
elementId: number, | ||
allElements: T[], | ||
halfOfList: 'first' | 'second' = 'first', | ||
indexOffset: number = 1, | ||
): T[] => { | ||
const index = findIndexOfElement(allElements, 'id', elementId); | ||
if (index === undefined) { | ||
return [] as T[]; | ||
} | ||
if (halfOfList === 'second') { | ||
if (index + indexOffset > allElements.length - 1) { | ||
return [] as T[]; | ||
} | ||
if (index === 0) { | ||
return allElements; | ||
} | ||
return allElements.slice(index + indexOffset); | ||
} | ||
|
||
return allElements.slice(0, index + indexOffset); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the
Chapters
class - same for all the added util functions