Optionally trigger manga details search in source
In case the manga was opened while browsing the source, the searches should be triggered for in the search instead of the library
This commit is contained in:
@@ -15,7 +15,13 @@ import { useGetCategoryMetadata } from '@/modules/category/services/CategoryMeta
|
||||
import { NullAndUndefined } from '@/Base.types.ts';
|
||||
import { LibraryOptions, LibrarySortMode } from '@/modules/library/Library.types.ts';
|
||||
import { CategoryIdInfo, CategoryMetadataInfo } from '@/modules/category/Category.types.ts';
|
||||
import { MangaChapterCountInfo, MangaIdInfo } from '@/modules/manga/Manga.types.ts';
|
||||
import {
|
||||
MangaChapterCountInfo,
|
||||
MangaDownloadInfo,
|
||||
MangaIdInfo,
|
||||
MangaStatusInfo,
|
||||
MangaUnreadInfo,
|
||||
} from '@/modules/manga/Manga.types.ts';
|
||||
import { SourceDisplayNameInfo } from '@/modules/source/Source.types.ts';
|
||||
|
||||
const triStateFilter = (
|
||||
@@ -96,8 +102,7 @@ const trackerFilter = (trackFilters: LibraryOptions['hasTrackerBinding'], manga:
|
||||
})
|
||||
.every(Boolean);
|
||||
|
||||
type TMangaStatusFilter = Pick<MangaType, 'status'>;
|
||||
const statusFilter = (statusFilters: LibraryOptions['hasStatus'], manga: TMangaStatusFilter): boolean =>
|
||||
const statusFilter = (statusFilters: LibraryOptions['hasStatus'], manga: MangaStatusInfo): boolean =>
|
||||
Object.entries(statusFilters)
|
||||
.map(([status, statusFilterState]) => triStateFilterBoolean(statusFilterState, status === manga.status))
|
||||
.every(Boolean);
|
||||
@@ -112,10 +117,12 @@ type TMangaFilterOptions = Pick<
|
||||
| 'hasTrackerBinding'
|
||||
| 'hasStatus'
|
||||
>;
|
||||
type TMangaFilter = Pick<MangaType, 'downloadCount' | 'unreadCount' | 'bookmarkCount' | 'hasDuplicateChapters'> &
|
||||
type TMangaFilter = Pick<MangaType, 'bookmarkCount' | 'hasDuplicateChapters'> &
|
||||
TMangaTrackerFilter &
|
||||
TMangaStatusFilter &
|
||||
MangaChapterCountInfo;
|
||||
MangaStatusInfo &
|
||||
MangaChapterCountInfo &
|
||||
MangaDownloadInfo &
|
||||
MangaUnreadInfo;
|
||||
const filterManga = (
|
||||
manga: TMangaFilter,
|
||||
{
|
||||
|
||||
@@ -33,6 +33,7 @@ type MangaCardBaseProps = Pick<MangaTypeGql, 'id' | 'title' | 'sourceId'> &
|
||||
|
||||
export type MangaIdInfo = Pick<MangaTypeGql, 'id'>;
|
||||
export type MangaChapterCountInfo = { chapters: Pick<MangaTypeGql['chapters'], 'totalCount'> };
|
||||
export type MangaInLibraryInfo = Pick<MangaTypeGql, 'inLibrary'>;
|
||||
export type MangaDownloadInfo = Pick<MangaTypeGql, 'downloadCount'> & MangaChapterCountInfo;
|
||||
export type MangaUnreadInfo = Pick<MangaTypeGql, 'unreadCount'> & MangaChapterCountInfo;
|
||||
export type MangaThumbnailInfo = Pick<MangaTypeGql, 'thumbnailUrl' | 'thumbnailUrlLastFetched'>;
|
||||
@@ -40,10 +41,14 @@ export type MangaTrackRecordInfo = MangaIdInfo & {
|
||||
trackRecords: { nodes: Pick<TrackRecordType, 'id' | 'trackerId'>[] };
|
||||
};
|
||||
export type MangaGenreInfo = Pick<MangaTypeGql, 'genre'>;
|
||||
export type MangaSourceIdInfo = Pick<MangaTypeGql, 'sourceId'>;
|
||||
export type MangaSourceNameInfo = { source?: Maybe<Pick<SourceType, 'name'>> };
|
||||
export type MangaSourceLngInfo = { source?: Maybe<Pick<SourceType, 'lang'>> };
|
||||
export type MangaArtistInfo = Pick<MangaTypeGql, 'artist'>;
|
||||
export type MangaAuthorInfo = Pick<MangaTypeGql, 'author'>;
|
||||
export type MangaTitleInfo = Pick<MangaTypeGql, 'title'>;
|
||||
export type MangaDescriptionInfo = Pick<MangaTypeGql, 'description'>;
|
||||
export type MangaStatusInfo = Pick<MangaTypeGql, 'status'>;
|
||||
|
||||
export type MigrateMode = 'copy' | 'migrate';
|
||||
|
||||
@@ -92,3 +97,8 @@ export enum MangaType {
|
||||
MANHWA,
|
||||
MANHUA,
|
||||
}
|
||||
|
||||
export interface MangaLocationState {
|
||||
mangaTitle: string;
|
||||
mode: MangaCardMode | undefined;
|
||||
}
|
||||
|
||||
@@ -44,12 +44,26 @@ import { useLocalStorage } from '@/modules/core/hooks/useStorage.tsx';
|
||||
import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx';
|
||||
import { useMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts';
|
||||
import { MANGA_COVER_ASPECT_RATIO, MANGA_STATUS_TO_TRANSLATION } from '@/modules/manga/Manga.constants.ts';
|
||||
import { MangaThumbnailInfo, MangaTrackRecordInfo } from '@/modules/manga/Manga.types.ts';
|
||||
import {
|
||||
MangaArtistInfo,
|
||||
MangaAuthorInfo,
|
||||
MangaDescriptionInfo,
|
||||
MangaGenreInfo,
|
||||
MangaIdInfo,
|
||||
MangaInLibraryInfo,
|
||||
MangaLocationState,
|
||||
MangaSourceIdInfo,
|
||||
MangaStatusInfo,
|
||||
MangaThumbnailInfo,
|
||||
MangaTitleInfo,
|
||||
MangaTrackRecordInfo,
|
||||
} from '@/modules/manga/Manga.types.ts';
|
||||
import { TAppThemeContext, useAppThemeContext } from '@/modules/theme/contexts/AppThemeContext.tsx';
|
||||
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
|
||||
import { CustomButtonIcon } from '@/modules/core/components/buttons/CustomButtonIcon.tsx';
|
||||
import { Sources } from '@/modules/source/services/Sources.ts';
|
||||
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
|
||||
import { SourceIdInfo } from '@/modules/source/Source.types.ts';
|
||||
|
||||
const DetailsWrapper = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@@ -163,19 +177,40 @@ function getSourceName(source?: Pick<SourceType, 'id' | 'displayName'> | null):
|
||||
return source.displayName ?? source.id;
|
||||
}
|
||||
|
||||
const LibrarySearchLink = ({ query, children }: { query: string; children?: ReactNode }) => (
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={AppRoutes.library.path(undefined, query)}
|
||||
sx={{ textDecoration: 'none', color: 'inherit' }}
|
||||
>
|
||||
const SearchLink = ({
|
||||
query,
|
||||
sourceId,
|
||||
mode,
|
||||
children,
|
||||
}: {
|
||||
query: string;
|
||||
sourceId: SourceIdInfo['id'] | undefined;
|
||||
mode: MangaLocationState['mode'];
|
||||
children?: ReactNode;
|
||||
}) => {
|
||||
const link = (() => {
|
||||
const isSourceMode = mode === 'source' && sourceId !== undefined;
|
||||
if (isSourceMode) {
|
||||
return AppRoutes.sources.childRoutes.browse.path(sourceId, query);
|
||||
}
|
||||
|
||||
return AppRoutes.library.path(undefined, query);
|
||||
})();
|
||||
|
||||
return (
|
||||
<Link component={RouterLink} to={link} sx={{ textDecoration: 'none', color: 'inherit' }}>
|
||||
{children ?? query}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const valuesToJoinedLibrarySearchLinks = (values: string[] | undefined) =>
|
||||
const valuesToJoinedSearchLinks = (
|
||||
values: string[] | undefined,
|
||||
sourceId: SourceIdInfo['id'] | undefined,
|
||||
mode: MangaLocationState['mode'],
|
||||
) =>
|
||||
values
|
||||
?.map((value) => <LibrarySearchLink query={value} />)
|
||||
?.map((value) => <SearchLink key={value} query={value} sourceId={sourceId} mode={mode} />)
|
||||
.reduce((acc, valueLink) => (
|
||||
<>
|
||||
{acc}, {valueLink}
|
||||
@@ -310,9 +345,11 @@ const Thumbnail = ({
|
||||
const OPEN_CLOSE_BUTTON_HEIGHT = '35px';
|
||||
const DESCRIPTION_COLLAPSED_SIZE = 75;
|
||||
const DescriptionGenre = ({
|
||||
manga: { description, genre: mangaGenres },
|
||||
manga: { description, genre: mangaGenres, sourceId },
|
||||
mode,
|
||||
}: {
|
||||
manga: Pick<MangaType, 'description' | 'genre'>;
|
||||
manga: MangaDescriptionInfo & MangaGenreInfo & MangaSourceIdInfo;
|
||||
mode: MangaLocationState['mode'];
|
||||
}) => {
|
||||
const [descriptionElement, setDescriptionElement] = useState<HTMLSpanElement | null>(null);
|
||||
const [descriptionHeight, setDescriptionHeight] = useState<number>();
|
||||
@@ -374,9 +411,9 @@ const DescriptionGenre = ({
|
||||
}}
|
||||
>
|
||||
{genres.map((genre) => (
|
||||
<LibrarySearchLink key={genre} query={genre}>
|
||||
<SearchLink key={genre} query={genre} sourceId={sourceId} mode={mode}>
|
||||
<Chip label={genre} variant="outlined" onClick={() => {}} />
|
||||
</LibrarySearchLink>
|
||||
</SearchLink>
|
||||
))}
|
||||
</Stack>
|
||||
</>
|
||||
@@ -385,15 +422,23 @@ const DescriptionGenre = ({
|
||||
|
||||
export const MangaDetails = ({
|
||||
manga,
|
||||
mode,
|
||||
}: {
|
||||
manga: Pick<
|
||||
MangaType,
|
||||
'id' | 'title' | 'author' | 'artist' | 'status' | 'inLibrary' | 'realUrl' | 'description' | 'genre'
|
||||
> &
|
||||
manga: Pick<MangaType, 'realUrl'> &
|
||||
MangaIdInfo &
|
||||
MangaTitleInfo &
|
||||
MangaStatusInfo &
|
||||
MangaInLibraryInfo &
|
||||
MangaAuthorInfo &
|
||||
MangaArtistInfo &
|
||||
MangaDescriptionInfo &
|
||||
MangaGenreInfo &
|
||||
MangaThumbnailInfo &
|
||||
MangaSourceIdInfo &
|
||||
MangaTrackRecordInfo & {
|
||||
source?: Pick<SourceType, 'id' | 'displayName'> | null;
|
||||
};
|
||||
mode: MangaLocationState['mode'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -438,13 +483,13 @@ export const MangaDetails = ({
|
||||
{manga.author && (
|
||||
<Metadata
|
||||
title={t('manga.label.author')}
|
||||
value={valuesToJoinedLibrarySearchLinks(Mangas.getAuthors(manga))}
|
||||
value={valuesToJoinedSearchLinks(Mangas.getAuthors(manga), manga.source?.id, mode)}
|
||||
/>
|
||||
)}
|
||||
{manga.artist && (
|
||||
<Metadata
|
||||
title={t('manga.label.artist')}
|
||||
value={valuesToJoinedLibrarySearchLinks(Mangas.getArtists(manga))}
|
||||
value={valuesToJoinedSearchLinks(Mangas.getArtists(manga), manga.source?.id, mode)}
|
||||
/>
|
||||
)}
|
||||
<Metadata
|
||||
@@ -467,7 +512,7 @@ export const MangaDetails = ({
|
||||
<OpenSourceButton url={manga.realUrl} />
|
||||
</MangaButtonsContainer>
|
||||
</TopContentWrapper>
|
||||
<DescriptionGenre manga={manga} />
|
||||
<DescriptionGenre manga={manga} mode={mode} />
|
||||
</DetailsWrapper>
|
||||
{CategorySelectComponent}
|
||||
</>
|
||||
|
||||
@@ -65,7 +65,7 @@ export const MangaGridCard = memo(
|
||||
{...longPressBind(() => popupState.open(optionButtonRef.current))}
|
||||
onClick={handleClick}
|
||||
to={mangaLinkTo}
|
||||
state={{ mangaTitle: title }}
|
||||
state={Mangas.createLocationState(manga, mode)}
|
||||
sx={{ textDecoration: 'none', touchCallout: 'none' }}
|
||||
>
|
||||
<Box
|
||||
|
||||
@@ -44,7 +44,7 @@ export const MangaListCard = memo(
|
||||
<CardActionArea
|
||||
component={RouterLink}
|
||||
to={mangaLinkTo}
|
||||
state={{ mangaTitle: title }}
|
||||
state={Mangas.createLocationState(manga, mode)}
|
||||
onClick={handleClick}
|
||||
{...longPressBind(() => popupState.open(optionButtonRef.current))}
|
||||
sx={{
|
||||
|
||||
@@ -13,7 +13,7 @@ import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { isNetworkRequestInFlight } from '@apollo/client/core/networkStatus';
|
||||
import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
@@ -27,11 +27,13 @@ import { GetMangaScreenQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_MANGA_SCREEN } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { useAppTitleAndAction } from '@/modules/navigation-bar/hooks/useAppTitleAndAction.ts';
|
||||
import { MangaLocationState } from '@/modules/manga/Manga.types.ts';
|
||||
|
||||
export const Manga: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const { mode } = useLocation<MangaLocationState>().state ?? {};
|
||||
|
||||
const autofetchedRef = useRef(false);
|
||||
|
||||
const {
|
||||
@@ -103,7 +105,7 @@ export const Manga: React.FC = () => {
|
||||
<Box sx={{ display: { md: 'flex' }, overflow: 'hidden' }}>
|
||||
{isLoading && <LoadingPlaceholder />}
|
||||
|
||||
{manga && <MangaDetails manga={manga} />}
|
||||
{manga && <MangaDetails manga={manga} mode={mode} />}
|
||||
{manga && <ChapterList manga={manga} isRefreshing={refreshing} />}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -29,12 +29,15 @@ import {
|
||||
MangaAction,
|
||||
MangaArtistInfo,
|
||||
MangaAuthorInfo,
|
||||
MangaCardMode,
|
||||
MangaDownloadInfo,
|
||||
MangaGenreInfo,
|
||||
MangaIdInfo,
|
||||
MangaLocationState,
|
||||
MangaSourceLngInfo,
|
||||
MangaSourceNameInfo,
|
||||
MangaThumbnailInfo,
|
||||
MangaTitleInfo,
|
||||
MangaType,
|
||||
MangaUnreadInfo,
|
||||
MigrateMode,
|
||||
@@ -604,4 +607,14 @@ export class Mangas {
|
||||
static getAuthors<Manga extends MangaAuthorInfo>(manga: Manga): string[] | undefined {
|
||||
return manga.author?.split(ARTIST_AUTHOR_SEPARATOR_REGEX);
|
||||
}
|
||||
|
||||
static createLocationState<Manga extends MangaTitleInfo>(
|
||||
manga: Manga,
|
||||
mode: MangaCardMode | undefined,
|
||||
): MangaLocationState {
|
||||
return {
|
||||
mangaTitle: manga.title,
|
||||
mode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user