Add "groupByDate" to "Chapters"
This commit is contained in:
8
src/UtilTypes.d.ts
vendored
8
src/UtilTypes.d.ts
vendored
@@ -39,3 +39,11 @@ type TransformRecordToWithDefaultFlag<T extends Record<string, any>> = {
|
|||||||
type MergeObjectsArray<T extends object[]> = T extends [infer F, ...infer R]
|
type MergeObjectsArray<T extends object[]> = T extends [infer F, ...infer R]
|
||||||
? F & MergeObjectsArray<R extends object[] ? R : []>
|
? F & MergeObjectsArray<R extends object[] ? R : []>
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
|
type OmitNotMatching<T, K extends keyof T> = {
|
||||||
|
[P in K]: T[K];
|
||||||
|
};
|
||||||
|
|
||||||
|
type ExtractCommon<T, U> = {
|
||||||
|
[K in keyof T & keyof U]: T[K] extends U[K] ? T[K] : never;
|
||||||
|
};
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { ReaderOpenChapterLocationState, ReaderResumeMode } from '@/modules/read
|
|||||||
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
|
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
import { DOWNLOAD_TYPE_FIELDS } from '@/lib/graphql/fragments/DownloadFragments.ts';
|
import { DOWNLOAD_TYPE_FIELDS } from '@/lib/graphql/fragments/DownloadFragments.ts';
|
||||||
|
import { epochToDate, getDateString } from '@/util/DateHelper.ts';
|
||||||
|
|
||||||
export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
|
export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
|
||||||
|
|
||||||
@@ -455,4 +456,19 @@ export class Chapters {
|
|||||||
updateInitialChapter,
|
updateInitialChapter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the chapters grouped by the passed key representing a timestamp.
|
||||||
|
*
|
||||||
|
* The timestamp gets mapped to a string via {@link getDateString}
|
||||||
|
*/
|
||||||
|
static groupByDate<
|
||||||
|
T extends Pick<ChapterType, 'lastReadAt'> | Pick<ChapterType, 'fetchedAt'> | Pick<ChapterType, 'uploadDate'>,
|
||||||
|
K extends keyof ExtractCommon<OmitNotMatching<ChapterType, 'lastReadAt' | 'fetchedAt' | 'uploadDate'>, T>,
|
||||||
|
>(chapters: T[], key: K): Record<string, T[]> {
|
||||||
|
return Object.groupBy(chapters, (chapter) => getDateString(epochToDate(Number(chapter[key])))) as Record<
|
||||||
|
string,
|
||||||
|
T[]
|
||||||
|
>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,30 +12,15 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx';
|
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx';
|
||||||
import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder/EmptyViewAbsoluteCentered.tsx';
|
import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder/EmptyViewAbsoluteCentered.tsx';
|
||||||
import { ChapterType } from '@/lib/graphql/generated/graphql.ts';
|
|
||||||
import { StyledGroupedVirtuoso } from '@/modules/core/components/virtuoso/StyledGroupedVirtuoso.tsx';
|
import { StyledGroupedVirtuoso } from '@/modules/core/components/virtuoso/StyledGroupedVirtuoso.tsx';
|
||||||
import { StyledGroupHeader } from '@/modules/core/components/virtuoso/StyledGroupHeader.tsx';
|
import { StyledGroupHeader } from '@/modules/core/components/virtuoso/StyledGroupHeader.tsx';
|
||||||
import { StyledGroupItemWrapper } from '@/modules/core/components/virtuoso/StyledGroupItemWrapper.tsx';
|
import { StyledGroupItemWrapper } from '@/modules/core/components/virtuoso/StyledGroupItemWrapper.tsx';
|
||||||
import { epochToDate, getDateString } from '@/util/DateHelper.ts';
|
|
||||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||||
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
|
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
||||||
import { ChapterHistoryCard } from '@/modules/history/components/ChapterHistoryCard.tsx';
|
import { ChapterHistoryCard } from '@/modules/history/components/ChapterHistoryCard.tsx';
|
||||||
|
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||||
const groupByDate = (histories: Pick<ChapterType, 'lastReadAt'>[]): [date: string, items: number][] => {
|
|
||||||
if (!histories.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateToItemMap = new Map<string, number>();
|
|
||||||
histories.forEach((item) => {
|
|
||||||
const date = getDateString(epochToDate(Number(item.lastReadAt)));
|
|
||||||
dateToItemMap.set(date, (dateToItemMap.get(date) ?? 0) + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
return [...dateToItemMap.entries()];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const History: React.FC = () => {
|
export const History: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -54,7 +39,14 @@ export const History: React.FC = () => {
|
|||||||
const hasNextPage = !!chapterHistoryData?.chapters.pageInfo.hasNextPage;
|
const hasNextPage = !!chapterHistoryData?.chapters.pageInfo.hasNextPage;
|
||||||
const endCursor = chapterHistoryData?.chapters.pageInfo.endCursor;
|
const endCursor = chapterHistoryData?.chapters.pageInfo.endCursor;
|
||||||
const readEntries = chapterHistoryData?.chapters.nodes ?? [];
|
const readEntries = chapterHistoryData?.chapters.nodes ?? [];
|
||||||
const groupedHistory = useMemo(() => groupByDate(readEntries), [readEntries]);
|
const groupedHistory = useMemo(
|
||||||
|
() =>
|
||||||
|
Object.entries(Chapters.groupByDate(readEntries, 'lastReadAt')).map(([date, chapters]) => [
|
||||||
|
date,
|
||||||
|
chapters.length,
|
||||||
|
]) satisfies [date: string, itemCount: number][],
|
||||||
|
[readEntries],
|
||||||
|
);
|
||||||
const groupCounts: number[] = useMemo(() => groupedHistory.map((group) => group[1]), [groupedHistory]);
|
const groupCounts: number[] = useMemo(() => groupedHistory.map((group) => group[1]), [groupedHistory]);
|
||||||
|
|
||||||
const computeItemKey = VirtuosoUtil.useCreateGroupedComputeItemKey(
|
const computeItemKey = VirtuosoUtil.useCreateGroupedComputeItemKey(
|
||||||
|
|||||||
@@ -12,31 +12,17 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx';
|
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx';
|
||||||
import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder/EmptyViewAbsoluteCentered.tsx';
|
import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder/EmptyViewAbsoluteCentered.tsx';
|
||||||
import { ChapterType } from '@/lib/graphql/generated/graphql.ts';
|
|
||||||
import { UpdateChecker } from '@/modules/core/components/UpdateChecker.tsx';
|
import { UpdateChecker } from '@/modules/core/components/UpdateChecker.tsx';
|
||||||
import { StyledGroupedVirtuoso } from '@/modules/core/components/virtuoso/StyledGroupedVirtuoso.tsx';
|
import { StyledGroupedVirtuoso } from '@/modules/core/components/virtuoso/StyledGroupedVirtuoso.tsx';
|
||||||
import { StyledGroupHeader } from '@/modules/core/components/virtuoso/StyledGroupHeader.tsx';
|
import { StyledGroupHeader } from '@/modules/core/components/virtuoso/StyledGroupHeader.tsx';
|
||||||
import { StyledGroupItemWrapper } from '@/modules/core/components/virtuoso/StyledGroupItemWrapper.tsx';
|
import { StyledGroupItemWrapper } from '@/modules/core/components/virtuoso/StyledGroupItemWrapper.tsx';
|
||||||
import { dateTimeFormatter, epochToDate, getDateString } from '@/util/DateHelper.ts';
|
import { dateTimeFormatter } from '@/util/DateHelper.ts';
|
||||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||||
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
|
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
import { ChapterUpdateCard } from '@/modules/updates/components/ChapterUpdateCard.tsx';
|
import { ChapterUpdateCard } from '@/modules/updates/components/ChapterUpdateCard.tsx';
|
||||||
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
||||||
|
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||||
const groupByDate = (updates: Pick<ChapterType, 'fetchedAt'>[]): [date: string, items: number][] => {
|
|
||||||
if (!updates.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateToItemMap = new Map<string, number>();
|
|
||||||
updates.forEach((item) => {
|
|
||||||
const date = getDateString(epochToDate(Number(item.fetchedAt)));
|
|
||||||
dateToItemMap.set(date, (dateToItemMap.get(date) ?? 0) + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
return [...dateToItemMap.entries()];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Updates: React.FC = () => {
|
export const Updates: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -55,7 +41,14 @@ export const Updates: React.FC = () => {
|
|||||||
const hasNextPage = !!chapterUpdateData?.chapters.pageInfo.hasNextPage;
|
const hasNextPage = !!chapterUpdateData?.chapters.pageInfo.hasNextPage;
|
||||||
const endCursor = chapterUpdateData?.chapters.pageInfo.endCursor;
|
const endCursor = chapterUpdateData?.chapters.pageInfo.endCursor;
|
||||||
const updateEntries = chapterUpdateData?.chapters.nodes ?? [];
|
const updateEntries = chapterUpdateData?.chapters.nodes ?? [];
|
||||||
const groupedUpdates = useMemo(() => groupByDate(updateEntries), [updateEntries]);
|
const groupedUpdates = useMemo(
|
||||||
|
() =>
|
||||||
|
Object.entries(Chapters.groupByDate(updateEntries, 'fetchedAt')).map(([date, chapters]) => [
|
||||||
|
date,
|
||||||
|
chapters.length,
|
||||||
|
]) satisfies [date: string, itemCount: number][],
|
||||||
|
[updateEntries],
|
||||||
|
);
|
||||||
const groupCounts: number[] = useMemo(() => groupedUpdates.map((group) => group[1]), [groupedUpdates]);
|
const groupCounts: number[] = useMemo(() => groupedUpdates.map((group) => group[1]), [groupedUpdates]);
|
||||||
|
|
||||||
const computeItemKey = VirtuosoUtil.useCreateGroupedComputeItemKey(
|
const computeItemKey = VirtuosoUtil.useCreateGroupedComputeItemKey(
|
||||||
|
|||||||
Reference in New Issue
Block a user