From 44344672f9c47a2926928f3372c610eb37d883a2 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Thu, 4 Apr 2024 20:32:10 -0400 Subject: [PATCH 01/13] refactor: creates utilities functions to work with lists --- .gitignore | 4 +++ src/components/chapter/util.tsx | 19 ++++++++++++ src/components/util/findElement.ts | 34 ++++++++++++++++++++++ src/components/util/findIndexOfElement.ts | 35 +++++++++++++++++++++++ src/components/util/getPartialList.ts | 29 +++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 src/components/util/findElement.ts create mode 100644 src/components/util/findIndexOfElement.ts create mode 100644 src/components/util/getPartialList.ts diff --git a/.gitignore b/.gitignore index fde0f2b619..73cd435e7b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ build/* tools/scripts/github_token.json src/lib/graphql/schema.json + +# local dev +pnpm-*.yaml +compose.yml diff --git a/src/components/chapter/util.tsx b/src/components/chapter/util.tsx index 70f43400de..155abf67b8 100644 --- a/src/components/chapter/util.tsx +++ b/src/components/chapter/util.tsx @@ -16,6 +16,7 @@ import { TranslationKey, } from '@/typings.ts'; import { useReducerLocalStorage } from '@/util/useLocalStorage.tsx'; +import { getPartialList } from '@/components/util/getPartialList'; const defaultChapterOptions: ChapterListOptions = { active: false, @@ -114,3 +115,21 @@ export const isFilterActive = (options: ChapterListOptions) => { const { unread, downloaded, bookmarked } = options; return unread != null || downloaded != null || bookmarked != null; }; + +/** + * @param chapterId The id of the chapter to be use as a pivot + * @param allChapters There list of chapters + * @returns The second half of the list. By the default the chapters are sorted + * in descending order, so it returns the previous chapters, not including the pivot chapter. + */ +export const getPreviousChapters = (chapterId: TChapter['id'], allChapters: TChapter[]): TChapter[] => + getPartialList(chapterId, allChapters, 'second'); + +/** + * @param chapterId The id of the chapter to be use as a pivot + * @param allChapters There list of chapters + * @returns The second half of the list. By the default the chapters are sorted + * in descending order, so it returns the previous chapters, not including the pivot chapter. + */ +export const getNextChapters = (chapterId: TChapter['id'], allChapters: TChapter[]): TChapter[] => + getPartialList(chapterId, allChapters, 'first'); diff --git a/src/components/util/findElement.ts b/src/components/util/findElement.ts new file mode 100644 index 0000000000..a792b9a22c --- /dev/null +++ b/src/components/util/findElement.ts @@ -0,0 +1,34 @@ +/* + * 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 = ( + elements: T[], + fieldToSearch: string, + fieldToMatch: unknown, + isFieldToSearchArray?: boolean, +): T | 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); + // } + const index = findIndexOfElement(elements, fieldToSearch, fieldToMatch, isFieldToSearchArray); + + if (!index) { + return undefined; + } + return elements[index]; +}; diff --git a/src/components/util/findIndexOfElement.ts b/src/components/util/findIndexOfElement.ts new file mode 100644 index 0000000000..feee240d87 --- /dev/null +++ b/src/components/util/findIndexOfElement.ts @@ -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 = ( + 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; +}; diff --git a/src/components/util/getPartialList.ts b/src/components/util/getPartialList.ts new file mode 100644 index 0000000000..36912096ae --- /dev/null +++ b/src/components/util/getPartialList.ts @@ -0,0 +1,29 @@ +/* + * 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'; + +export const getPartialList = ( + elementId: number, + allEmenets: T[], + halfOfList: 'first' | 'second' = 'first', + indexOffset: number = 1, +): T[] => { + const index = findIndexOfElement(allEmenets, 'id', elementId); + if (!index) { + return [] as T[]; + } + if (halfOfList === 'second') { + if (index + indexOffset > allEmenets.length - 1) { + return [] as T[]; + } + return allEmenets.slice(index + indexOffset); + } + + return allEmenets.slice(0, index - indexOffset); +}; From fee6af50290cd4f9a7fa1802b92dd77a3043593d Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Thu, 4 Apr 2024 20:34:50 -0400 Subject: [PATCH 02/13] refactor(chapters): removes commented out code, and implement ultil function to clean up the file --- .../chapter/ChapterActionMenuItems.tsx | 11 ++------- src/components/chapter/ChapterList.tsx | 2 +- src/components/chapter/util.tsx | 23 +++++++++++++++++-- src/components/manga/MangaDetails.tsx | 7 ------ src/components/util/getPartialList.ts | 12 +++++++++- 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/components/chapter/ChapterActionMenuItems.tsx b/src/components/chapter/ChapterActionMenuItems.tsx index ef50418a4d..708cf1266e 100644 --- a/src/components/chapter/ChapterActionMenuItems.tsx +++ b/src/components/chapter/ChapterActionMenuItems.tsx @@ -34,6 +34,7 @@ import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts'; import { createGetMenuItemTitle, createIsMenuItemDisabled, createShouldShowMenuItem } from '@/components/menu/util.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts'; +import { getPreviousChapters } from '@/components/chapter/util'; type BaseProps = { onClose: () => void }; @@ -118,15 +119,7 @@ export const ChapterActionMenuItems = ({ if (!isMarkPrevAsRead) { return [chapter]; } - - const index = allChapters.findIndex(({ id: chapterId }) => chapterId === chapter.id); - - const isFirstChapter = index + 1 > allChapters.length - 1; - if (isFirstChapter) { - return []; - } - - return allChapters.slice(index + 1); + return getPreviousChapters(chapter.id, allChapters); }; const chapters = getChapters(); diff --git a/src/components/chapter/ChapterList.tsx b/src/components/chapter/ChapterList.tsx index bc9953e698..d580a7dc87 100644 --- a/src/components/chapter/ChapterList.tsx +++ b/src/components/chapter/ChapterList.tsx @@ -20,7 +20,6 @@ import { ChapterCard } from '@/components/chapter/ChapterCard.tsx'; import { ResumeFab } from '@/components/manga/ResumeFAB.tsx'; import { filterAndSortChapters, useChapterOptions } from '@/components/chapter/util.tsx'; import { EmptyView } from '@/components/util/EmptyView.tsx'; -import { ChaptersToolbarMenu } from '@/components/chapter/ChaptersToolbarMenu.tsx'; import { SelectionFAB } from '@/components/collection/SelectionFAB.tsx'; import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab.tsx'; import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; @@ -30,6 +29,7 @@ import { Chapters } from '@/lib/data/Chapters.ts'; import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts'; import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { ChaptersToolbarMenu } from '@/components/chapter/ChaptersToolbarMenu'; const ChapterListHeader = styled(Stack)(({ theme }) => ({ margin: 8, diff --git a/src/components/chapter/util.tsx b/src/components/chapter/util.tsx index 155abf67b8..3ae132e641 100644 --- a/src/components/chapter/util.tsx +++ b/src/components/chapter/util.tsx @@ -17,6 +17,8 @@ import { } from '@/typings.ts'; import { useReducerLocalStorage } from '@/util/useLocalStorage.tsx'; import { getPartialList } from '@/components/util/getPartialList'; +import { Chapters } from '@/lib/data/Chapters.ts'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler'; const defaultChapterOptions: ChapterListOptions = { active: false, @@ -128,8 +130,25 @@ export const getPreviousChapters = (chapterId: TChapter['id'], allChapters: TCha /** * @param chapterId The id of the chapter to be use as a pivot * @param allChapters There list of chapters + * @param includePivotChapter Whether to return the chapter with the chapterId passed in the list * @returns The second half of the list. By the default the chapters are sorted * in descending order, so it returns the previous chapters, not including the pivot chapter. */ -export const getNextChapters = (chapterId: TChapter['id'], allChapters: TChapter[]): TChapter[] => - getPartialList(chapterId, allChapters, 'first'); +export const getNextChapters = ( + chapterId: TChapter['id'], + allChapters: TChapter[], + includePivotChapter: boolean = false, +): TChapter[] => { + if (includePivotChapter) { + return getPartialList(chapterId, allChapters, 'first'); + } + return getPartialList(chapterId, allChapters, 'first', 0); +}; + +export const setChapterAsLastRead = (chapterId: TChapter['id'], allChapters: TChapter[]) => { + const readChapters = getPreviousChapters(chapterId, allChapters); + const unreadChapterId = getNextChapters(chapterId, allChapters, true).map((chapter) => chapter.id); + + Chapters.markAsRead(readChapters, true).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction')); + Chapters.markAsUnread(unreadChapterId).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction')); +}; diff --git a/src/components/manga/MangaDetails.tsx b/src/components/manga/MangaDetails.tsx index 6d15384b9e..4e57767f20 100644 --- a/src/components/manga/MangaDetails.tsx +++ b/src/components/manga/MangaDetails.tsx @@ -41,9 +41,6 @@ const DetailsWrapper = styled('div')(({ theme }) => ({ const TopContentWrapper = styled('div')(() => ({ padding: '10px', - // [theme.breakpoints.up('md')]: { - // minWidth: '50%', - // }, })); const ThumbnailMetadataWrapper = styled('div')(() => ({ @@ -58,9 +55,6 @@ const Thumbnail = styled('div')(() => ({ height: 'auto', }, maxWidth: '50%', - // [theme.breakpoints.up('md')]: { - // minWidth: '100px', - // }, })); const Metadata = styled('div')(({ theme }) => ({ @@ -92,7 +86,6 @@ const BottomContentWrapper = styled('div')(({ theme }) => ({ paddingRight: '10px', [theme.breakpoints.up('md')]: { fontSize: '1.2em', - // maxWidth: '50%', }, [theme.breakpoints.up('lg')]: { fontSize: '1.3em', diff --git a/src/components/util/getPartialList.ts b/src/components/util/getPartialList.ts index 36912096ae..8df4fef7a7 100644 --- a/src/components/util/getPartialList.ts +++ b/src/components/util/getPartialList.ts @@ -8,6 +8,16 @@ 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 allEmenets 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 = ( elementId: number, allEmenets: T[], @@ -25,5 +35,5 @@ export const getPartialList = ( return allEmenets.slice(index + indexOffset); } - return allEmenets.slice(0, index - indexOffset); + return allEmenets.slice(0, index + indexOffset); }; From 17ed5f720665625d404313a63890a0b67f16aa10 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Fri, 5 Apr 2024 00:53:25 -0400 Subject: [PATCH 03/13] refactor TrackMangaButton to update local source if the tracker last read is higher than local --- src/components/chapter/util.tsx | 25 ++++++++++++++---- src/components/manga/TrackMangaButton.tsx | 31 ++++++++++++++++++++++- src/components/tracker/TrackManga.tsx | 11 +------- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/components/chapter/util.tsx b/src/components/chapter/util.tsx index 3ae132e641..149548577a 100644 --- a/src/components/chapter/util.tsx +++ b/src/components/chapter/util.tsx @@ -121,16 +121,25 @@ export const isFilterActive = (options: ChapterListOptions) => { /** * @param chapterId The id of the chapter to be use as a pivot * @param allChapters There list of chapters + * @param includePivotChapter Whether to return the chapter with the passed chapterId in the list * @returns The second half of the list. By the default the chapters are sorted * in descending order, so it returns the previous chapters, not including the pivot chapter. */ -export const getPreviousChapters = (chapterId: TChapter['id'], allChapters: TChapter[]): TChapter[] => - getPartialList(chapterId, allChapters, 'second'); +export const getPreviousChapters = ( + chapterId: TChapter['id'], + allChapters: TChapter[], + includePivotChapter: boolean = false, +): TChapter[] => { + if (includePivotChapter) { + return getPartialList(chapterId, allChapters, 'second', 0); + } + return getPartialList(chapterId, allChapters, 'second'); +}; /** * @param chapterId The id of the chapter to be use as a pivot * @param allChapters There list of chapters - * @param includePivotChapter Whether to return the chapter with the chapterId passed in the list + * @param includePivotChapter Whether to return the chapter with the passed chapterId in the list * @returns The second half of the list. By the default the chapters are sorted * in descending order, so it returns the previous chapters, not including the pivot chapter. */ @@ -145,9 +154,15 @@ export const getNextChapters = ( return getPartialList(chapterId, allChapters, 'first', 0); }; +/** + * @description This fucntion takes a chapter Id and set all chapters with index bellow the index of the chapter to that id + * to read, and the rest of the chapters as unread. Technically setting the chapter with the passed id as the current unread chapter. + * @param chapterId Chapter Id + * @param allChapters List of chapters + */ export const setChapterAsLastRead = (chapterId: TChapter['id'], allChapters: TChapter[]) => { - const readChapters = getPreviousChapters(chapterId, allChapters); - const unreadChapterId = getNextChapters(chapterId, allChapters, true).map((chapter) => chapter.id); + const readChapters = getPreviousChapters(chapterId, allChapters, true); + const unreadChapterId = getNextChapters(chapterId, allChapters).map((chapter) => chapter.id); Chapters.markAsRead(readChapters, true).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction')); Chapters.markAsUnread(unreadChapterId).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction')); diff --git a/src/components/manga/TrackMangaButton.tsx b/src/components/manga/TrackMangaButton.tsx index 1615aa4eef..8163a8739b 100644 --- a/src/components/manga/TrackMangaButton.tsx +++ b/src/components/manga/TrackMangaButton.tsx @@ -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,36 @@ 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); + const mangaChapters = mangaChaptersQuery.data?.chapters.nodes; + + const refreshTracker = () => { + mangaTrackers.map((trackRecord) => + requestManager + .fetchTrackBind(trackRecord.id) + .response.catch(() => makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error')), + ); + }; + + const updateChapterFromTracker = () => { + const lastestTrackersRead = Math.max(...mangaTrackers.map((trackRecord) => trackRecord.lastChapterRead)); + const latestLocalRead = manga.latestReadChapter?.chapterNumber ?? 0; + const localBehindTracker = !mangaChapters?.some((chapter) => chapter.chapterNumber === lastestTrackersRead); + if (localBehindTracker) { + requestManager.getMangaChaptersFetch(manga.id); + } + if (!localBehindTracker) { + const chapterToBeUpdated = mangaChapters?.find((chapter) => chapter.chapterNumber === lastestTrackersRead); + if (chapterToBeUpdated && latestLocalRead < lastestTrackersRead) { + setChapterAsLastRead(chapterToBeUpdated?.id, mangaChapters as TChapter[]); + } + } + }; const handleClick = (openPopup: () => void) => { + refreshTracker(); + updateChapterFromTracker(); + if (trackerList.error) { makeToast(t('tracking.error.label.could_not_load_track_info'), 'error'); return; diff --git a/src/components/tracker/TrackManga.tsx b/src/components/tracker/TrackManga.tsx index 3b748588c0..33f1c1943d 100644 --- a/src/components/tracker/TrackManga.tsx +++ b/src/components/tracker/TrackManga.tsx @@ -8,16 +8,14 @@ import { useNavigate } from 'react-router-dom'; import { Box } from '@mui/material'; -import { useEffect, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; import DialogContent from '@mui/material/DialogContent'; -import { useTranslation } from 'react-i18next'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { EmptyView } from '@/components/util/EmptyView.tsx'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; import { Trackers } from '@/lib/data/Trackers.ts'; import { TrackerCard, TrackerMode } from '@/components/tracker/TrackerCard.tsx'; import { TManga } from '@/typings.ts'; -import { makeToast } from '@/components/util/Toast.tsx'; const getTrackerMode = (id: number, trackersInUse: number[], searchModeForTracker?: number): TrackerMode => { if (id === searchModeForTracker) { @@ -32,7 +30,6 @@ const getTrackerMode = (id: number, trackersInUse: number[], searchModeForTracke }; export const TrackManga = ({ manga }: { manga: Pick }) => { - const { t } = useTranslation(); const navigate = useNavigate(); const [searchModeForTracker, setSearchModeForTracker] = useState(); @@ -47,12 +44,6 @@ export const TrackManga = ({ manga }: { manga: Pick (isSearchActive ? Box : DialogContent), [isSearchActive]); - useEffect(() => { - Promise.all(manga.trackRecords.nodes.map((trackRecord) => requestManager.fetchTrackBind(trackRecord.id))).catch( - () => makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error'), - ); - }, [manga.id]); - const trackerComponents = useMemo( () => loggedInTrackers.map((tracker) => { From 630374976ebaa12c6baf0121d6351cbff8f2f25f Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Fri, 5 Apr 2024 00:59:06 -0400 Subject: [PATCH 04/13] refactor(packe.json): adds --host flag to the vite command to be able to run the dev server remotely --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7555738c17..91a176d550 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "ci": "yarn install --frozen-lockfile", - "dev": "vite", + "dev": "vite --host", "preview": "vite preview", "build": "vite build", "test": "node -e \"console.log('imagine')\"", From 7e2dacb35831ffb8c9fb25f4a9b2a127c87fec09 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Fri, 5 Apr 2024 01:03:00 -0400 Subject: [PATCH 05/13] refactor(findElement.ts): removes commented code --- src/components/util/findElement.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/components/util/findElement.ts b/src/components/util/findElement.ts index a792b9a22c..fa03a4774d 100644 --- a/src/components/util/findElement.ts +++ b/src/components/util/findElement.ts @@ -15,16 +15,6 @@ export const findElement = ( fieldToMatch: unknown, isFieldToSearchArray?: boolean, ): T | 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); - // } const index = findIndexOfElement(elements, fieldToSearch, fieldToMatch, isFieldToSearchArray); if (!index) { From d518d8f3f115d5a9d0cddcad6b7c7539fe9a6c3a Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Fri, 5 Apr 2024 03:10:01 -0400 Subject: [PATCH 06/13] fix(gerPartialList): changes the if statement to return an empty array when index is undefined instead of 0 --- src/components/util/getPartialList.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/util/getPartialList.ts b/src/components/util/getPartialList.ts index 8df4fef7a7..fa6085d7c1 100644 --- a/src/components/util/getPartialList.ts +++ b/src/components/util/getPartialList.ts @@ -12,7 +12,7 @@ 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 allEmenets The list of elements to be firtered. + * @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. @@ -20,20 +20,20 @@ import { findIndexOfElement } from '@/components/util/findIndexOfElement.ts'; */ export const getPartialList = ( elementId: number, - allEmenets: T[], + allElements: T[], halfOfList: 'first' | 'second' = 'first', indexOffset: number = 1, ): T[] => { - const index = findIndexOfElement(allEmenets, 'id', elementId); - if (!index) { + const index = findIndexOfElement(allElements, 'id', elementId); + if (index === undefined) { return [] as T[]; } if (halfOfList === 'second') { - if (index + indexOffset > allEmenets.length - 1) { + if (index + indexOffset > allElements.length - 1) { return [] as T[]; } - return allEmenets.slice(index + indexOffset); + return allElements.slice(index + indexOffset); } - return allEmenets.slice(0, index + indexOffset); + return allElements.slice(0, index + indexOffset); }; From d673e50b949c6c94d46a9968ac771cd91d6716f5 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Fri, 5 Apr 2024 03:33:40 -0400 Subject: [PATCH 07/13] fix(getPartialList): returns the whole array if the index is 0 --- src/components/util/getPartialList.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/util/getPartialList.ts b/src/components/util/getPartialList.ts index fa6085d7c1..6d03f833de 100644 --- a/src/components/util/getPartialList.ts +++ b/src/components/util/getPartialList.ts @@ -32,6 +32,9 @@ export const getPartialList = ( if (index + indexOffset > allElements.length - 1) { return [] as T[]; } + if (index === 0) { + return allElements; + } return allElements.slice(index + indexOffset); } From ffd6f82f526665994457867524a666eba290bb49 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:40:54 -0400 Subject: [PATCH 08/13] feat(apolloClient): adds credential properties to the client to sent cookies with request --- src/lib/requests/client/GraphQLClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/requests/client/GraphQLClient.ts b/src/lib/requests/client/GraphQLClient.ts index 27aee8fd76..02b57784c5 100644 --- a/src/lib/requests/client/GraphQLClient.ts +++ b/src/lib/requests/client/GraphQLClient.ts @@ -138,7 +138,7 @@ export class GraphQLClient extends BaseClient< } private createUploadLink() { - return createUploadLink({ uri: () => this.getBaseUrl() }); + return createUploadLink({ uri: () => this.getBaseUrl(), credentials: 'include' }); } private createWSLink() { From 90308e6818c2a56eebf093c017887ec8b3f02026 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:41:29 -0400 Subject: [PATCH 09/13] feat(fetch class): adds credential property to the config of the fetch client --- src/lib/requests/client/RestClient.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/requests/client/RestClient.ts b/src/lib/requests/client/RestClient.ts index b70da0940d..8ae0238e3e 100644 --- a/src/lib/requests/client/RestClient.ts +++ b/src/lib/requests/client/RestClient.ts @@ -27,10 +27,13 @@ export class RestClient extends BaseClient Promise> implements IRestClient { - private config: RequestInit = {}; + private config: RequestInit = { + credentials: 'include', + }; public readonly fetcher = async ( url: string, + { data, httpMethod = HttpMethod.GET, From 112857c321376c5b8ddc05400c861bdbcfa776ea Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Tue, 9 Apr 2024 19:36:27 -0400 Subject: [PATCH 10/13] fix: adds logic to mark partial chapters as read --- src/components/manga/TrackMangaButton.tsx | 61 +++++++++++++++++------ 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/components/manga/TrackMangaButton.tsx b/src/components/manga/TrackMangaButton.tsx index 8163a8739b..5eb48d0c85 100644 --- a/src/components/manga/TrackMangaButton.tsx +++ b/src/components/manga/TrackMangaButton.tsx @@ -30,35 +30,62 @@ 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); - const mangaChapters = mangaChaptersQuery.data?.chapters.nodes; - const refreshTracker = () => { - mangaTrackers.map((trackRecord) => + /** + * @description This function fetch the last read for the loged in trackers. + */ + const refreshTracker = async () => { + await mangaTrackers.map((trackRecord) => requestManager .fetchTrackBind(trackRecord.id) .response.catch(() => makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error')), ); }; - - const updateChapterFromTracker = () => { - const lastestTrackersRead = Math.max(...mangaTrackers.map((trackRecord) => 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 latestTrackersRead = Math.max(...mangaTrackers.map((trackRecord) => trackRecord.lastChapterRead)); const latestLocalRead = manga.latestReadChapter?.chapterNumber ?? 0; - const localBehindTracker = !mangaChapters?.some((chapter) => chapter.chapterNumber === lastestTrackersRead); + + // 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) { - requestManager.getMangaChaptersFetch(manga.id); + requestManager.getMangaChaptersFetch(manga.id, { awaitRefetchQueries: true }); } if (!localBehindTracker) { - const chapterToBeUpdated = mangaChapters?.find((chapter) => chapter.chapterNumber === lastestTrackersRead); - if (chapterToBeUpdated && latestLocalRead < lastestTrackersRead) { - setChapterAsLastRead(chapterToBeUpdated?.id, mangaChapters as TChapter[]); + 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) + ) { + setChapterAsLastRead(chapterToBeUpdated?.id, mangaChaptersQuery.data?.chapters.nodes as TChapter[]); } } }; - const handleClick = (openPopup: () => void) => { - refreshTracker(); - updateChapterFromTracker(); - if (trackerList.error) { makeToast(t('tracking.error.label.could_not_load_track_info'), 'error'); return; @@ -70,6 +97,10 @@ export const TrackMangaButton = ({ manga }: { manga: TManga }) => { } openPopup(); + (async () => { + await refreshTracker(); + await updateChapterFromTracker(); + })(); }; return ( From 7b0ed6e2e54d11739094c623a4e134c5bf1b7293 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Tue, 9 Apr 2024 20:23:50 -0400 Subject: [PATCH 11/13] fix: adds condition to check that the chapter to be updated is not read. --- src/components/manga/TrackMangaButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/manga/TrackMangaButton.tsx b/src/components/manga/TrackMangaButton.tsx index 5eb48d0c85..f8240b4eed 100644 --- a/src/components/manga/TrackMangaButton.tsx +++ b/src/components/manga/TrackMangaButton.tsx @@ -79,7 +79,7 @@ export const TrackMangaButton = ({ manga }: { manga: TManga }) => { if ( chapterToBeUpdated && (latestLocalRead < latestTrackersRead || - Math.floor(chapterToBeUpdated.chapterNumber) === latestTrackersRead) + (Math.floor(chapterToBeUpdated.chapterNumber) === latestTrackersRead && !chapterToBeUpdated.isRead)) ) { setChapterAsLastRead(chapterToBeUpdated?.id, mangaChaptersQuery.data?.chapters.nodes as TChapter[]); } From 1a7a8d0373b7fcf052566aab9285d5648d7f8a40 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:56:45 -0400 Subject: [PATCH 12/13] removes local files from .gitignore --- .gitignore | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 73cd435e7b..6268e7751c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,4 @@ build/* tools/scripts/github_token.json -src/lib/graphql/schema.json - -# local dev -pnpm-*.yaml -compose.yml +src/lib/graphql/schema.json \ No newline at end of file From 9b8f66e81886260bd955f9a75ec9e71b017d6cb9 Mon Sep 17 00:00:00 2001 From: Angel De La Cruz <122130728+taos15@users.noreply.github.com> Date: Sat, 13 Apr 2024 17:17:42 -0400 Subject: [PATCH 13/13] fix(tracke): awaits for the tracker modal to update the tracker before checking if is behind of local read --- src/components/manga/TrackMangaButton.tsx | 32 ++++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/components/manga/TrackMangaButton.tsx b/src/components/manga/TrackMangaButton.tsx index f8240b4eed..45d89bd7c3 100644 --- a/src/components/manga/TrackMangaButton.tsx +++ b/src/components/manga/TrackMangaButton.tsx @@ -29,24 +29,28 @@ 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); + const mangaChaptersQuery = requestManager.useGetMangaChapters(manga.id, {}); /** * @description This function fetch the last read for the loged in trackers. */ - const refreshTracker = async () => { - await mangaTrackers.map((trackRecord) => - requestManager - .fetchTrackBind(trackRecord.id) - .response.catch(() => makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error')), + 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 latestTrackersRead = Math.max(...mangaTrackers.map((trackRecord) => trackRecord.lastChapterRead)); + 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 @@ -69,7 +73,7 @@ export const TrackMangaButton = ({ manga }: { manga: TManga }) => { // Fetch new chapters if behind tracker if (localBehindTracker) { - requestManager.getMangaChaptersFetch(manga.id, { awaitRefetchQueries: true }); + await requestManager.getMangaChaptersFetch(manga.id, { awaitRefetchQueries: true }).response; } if (!localBehindTracker) { const chapterToBeUpdated = @@ -85,7 +89,7 @@ export const TrackMangaButton = ({ manga }: { manga: TManga }) => { } } }; - const handleClick = (openPopup: () => void) => { + const handleClick = async (openPopup: () => void) => { if (trackerList.error) { makeToast(t('tracking.error.label.could_not_load_track_info'), 'error'); return; @@ -97,10 +101,12 @@ export const TrackMangaButton = ({ manga }: { manga: TManga }) => { } openPopup(); - (async () => { - await refreshTracker(); + + try { await updateChapterFromTracker(); - })(); + } catch (error) { + makeToast(t('tracking.error.label.could_not_load_track_info'), 'error'); + } }; return (