Trigger library search on artist/author click (#939)
* add search func * Fix lint issues and clean up --------- Co-authored-by: schroda <50052685+schroda@users.noreply.github.com>
This commit is contained in:
@@ -129,7 +129,7 @@ const MainApp = () => {
|
|||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* General Routes */}
|
{/* General Routes */}
|
||||||
<Route path={AppRoutes.root.match} element={<Navigate to={AppRoutes.library.path} replace />} />
|
<Route path={AppRoutes.root.match} element={<Navigate to={AppRoutes.library.path()} replace />} />
|
||||||
<Route path={AppRoutes.matchAll.match} element={<Navigate to={AppRoutes.root.path} replace />} />
|
<Route path={AppRoutes.matchAll.match} element={<Navigate to={AppRoutes.root.path} replace />} />
|
||||||
{isMobileWidth && <Route path={AppRoutes.more.match} element={<More />} />}
|
{isMobileWidth && <Route path={AppRoutes.more.match} element={<More />} />}
|
||||||
<Route path={AppRoutes.about.match} element={<About />} />
|
<Route path={AppRoutes.about.match} element={<About />} />
|
||||||
|
|||||||
@@ -128,7 +128,14 @@ export const AppRoutes = {
|
|||||||
},
|
},
|
||||||
library: {
|
library: {
|
||||||
match: 'library',
|
match: 'library',
|
||||||
path: '/library',
|
path: (tab?: string, search?: string) => {
|
||||||
|
const tabParam = tab ? `tab=${tab}` : '';
|
||||||
|
const searchParam = search ? `query=${encodeURIComponent(search)}` : '';
|
||||||
|
|
||||||
|
const params = [tabParam, searchParam].filter(Boolean).join('&');
|
||||||
|
|
||||||
|
return `/library${params ? `?${params}` : ''}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
updates: {
|
updates: {
|
||||||
match: 'updates',
|
match: 'updates',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Stack, { StackProps } from '@mui/material/Stack';
|
import Stack, { StackProps } from '@mui/material/Stack';
|
||||||
import Typography, { TypographyProps } from '@mui/material/Typography';
|
import Typography, { TypographyProps } from '@mui/material/Typography';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export const Metadata = ({
|
export const Metadata = ({
|
||||||
title,
|
title,
|
||||||
@@ -17,7 +18,7 @@ export const Metadata = ({
|
|||||||
valueProps,
|
valueProps,
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
value: string;
|
value: ReactNode;
|
||||||
stackProps?: StackProps;
|
stackProps?: StackProps;
|
||||||
titleProps?: TypographyProps;
|
titleProps?: TypographyProps;
|
||||||
valueProps?: TypographyProps;
|
valueProps?: TypographyProps;
|
||||||
|
|||||||
@@ -29,6 +29,6 @@ export const useBackButton = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(AppRoutes.library.path);
|
navigate(AppRoutes.library.path());
|
||||||
}, [history, location.pathname]);
|
}, [history, location.pathname]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export type MangaTrackRecordInfo = MangaIdInfo & {
|
|||||||
export type MangaGenreInfo = Pick<MangaTypeGql, 'genre'>;
|
export type MangaGenreInfo = Pick<MangaTypeGql, 'genre'>;
|
||||||
export type MangaSourceNameInfo = { source?: Maybe<Pick<SourceType, 'name'>> };
|
export type MangaSourceNameInfo = { source?: Maybe<Pick<SourceType, 'name'>> };
|
||||||
export type MangaSourceLngInfo = { source?: Maybe<Pick<SourceType, 'lang'>> };
|
export type MangaSourceLngInfo = { source?: Maybe<Pick<SourceType, 'lang'>> };
|
||||||
|
export type MangaArtistInfo = Pick<MangaTypeGql, 'artist'>;
|
||||||
|
export type MangaAuthorInfo = Pick<MangaTypeGql, 'author'>;
|
||||||
|
|
||||||
export type MigrateMode = 'copy' | 'migrate';
|
export type MigrateMode = 'copy' | 'migrate';
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { Vibrant } from 'node-vibrant/browser';
|
|||||||
import { FastAverageColor } from 'fast-average-color';
|
import { FastAverageColor } from 'fast-average-color';
|
||||||
import parseHtml from 'html-react-parser';
|
import parseHtml from 'html-react-parser';
|
||||||
import sanitizeHtml from 'sanitize-html';
|
import sanitizeHtml from 'sanitize-html';
|
||||||
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx';
|
import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx';
|
||||||
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
||||||
import { Mangas } from '@/modules/manga/services/Mangas.ts';
|
import { Mangas } from '@/modules/manga/services/Mangas.ts';
|
||||||
@@ -48,6 +49,7 @@ import { TAppThemeContext, useAppThemeContext } from '@/modules/theme/contexts/A
|
|||||||
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
|
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
|
||||||
import { CustomButtonIcon } from '@/modules/core/components/buttons/CustomButtonIcon.tsx';
|
import { CustomButtonIcon } from '@/modules/core/components/buttons/CustomButtonIcon.tsx';
|
||||||
import { Sources } from '@/modules/source/services/Sources.ts';
|
import { Sources } from '@/modules/source/services/Sources.ts';
|
||||||
|
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
|
||||||
|
|
||||||
const DetailsWrapper = styled('div')(({ theme }) => ({
|
const DetailsWrapper = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -161,10 +163,29 @@ function getSourceName(source?: Pick<SourceType, 'id' | 'displayName'> | null):
|
|||||||
return source.displayName ?? source.id;
|
return source.displayName ?? source.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getValueOrUnknown(val?: string | null) {
|
function getValueOrUnknown<T>(val?: T | null): T | string {
|
||||||
return val ?? translate('global.label.unknown');
|
return val ?? translate('global.label.unknown');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LibrarySearchLink = ({ query }: { query: string }) => (
|
||||||
|
<Link
|
||||||
|
component={RouterLink}
|
||||||
|
to={AppRoutes.library.path(undefined, query)}
|
||||||
|
sx={{ textDecoration: 'none', color: 'inherit' }}
|
||||||
|
>
|
||||||
|
{query}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
const valuesToJoinedLibrarySearchLinks = (values: string[] | undefined) =>
|
||||||
|
values
|
||||||
|
?.map((value) => <LibrarySearchLink query={value} />)
|
||||||
|
.reduce((acc, valueLink) => (
|
||||||
|
<>
|
||||||
|
{acc}, {valueLink}
|
||||||
|
</>
|
||||||
|
));
|
||||||
|
|
||||||
const Thumbnail = ({
|
const Thumbnail = ({
|
||||||
manga,
|
manga,
|
||||||
mangaDynamicColorSchemes,
|
mangaDynamicColorSchemes,
|
||||||
@@ -416,8 +437,14 @@ export const MangaDetails = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomTooltip>
|
</CustomTooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Metadata title={t('manga.label.author')} value={getValueOrUnknown(manga.author)} />
|
<Metadata
|
||||||
<Metadata title={t('manga.label.artist')} value={getValueOrUnknown(manga.artist)} />
|
title={t('manga.label.author')}
|
||||||
|
value={getValueOrUnknown(valuesToJoinedLibrarySearchLinks(Mangas.getAuthors(manga)))}
|
||||||
|
/>
|
||||||
|
<Metadata
|
||||||
|
title={t('manga.label.author')}
|
||||||
|
value={getValueOrUnknown(valuesToJoinedLibrarySearchLinks(Mangas.getArtists(manga)))}
|
||||||
|
/>
|
||||||
<Metadata
|
<Metadata
|
||||||
title={t('manga.label.status')}
|
title={t('manga.label.status')}
|
||||||
value={t(MANGA_STATUS_TO_TRANSLATION[manga.status])}
|
value={t(MANGA_STATUS_TO_TRANSLATION[manga.status])}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import { MANGA_BASE_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
|||||||
import { MetadataMigrationSettings } from '@/modules/migration/Migration.types.ts';
|
import { MetadataMigrationSettings } from '@/modules/migration/Migration.types.ts';
|
||||||
import {
|
import {
|
||||||
MangaAction,
|
MangaAction,
|
||||||
|
MangaArtistInfo,
|
||||||
|
MangaAuthorInfo,
|
||||||
MangaDownloadInfo,
|
MangaDownloadInfo,
|
||||||
MangaGenreInfo,
|
MangaGenreInfo,
|
||||||
MangaIdInfo,
|
MangaIdInfo,
|
||||||
@@ -92,6 +94,8 @@ type PerformActionOptions<Action extends MangaAction> = Action extends 'mark_as_
|
|||||||
|
|
||||||
type MigrateFuncReturn = { copy: () => Promise<unknown>[]; cleanup: () => Promise<unknown>[] };
|
type MigrateFuncReturn = { copy: () => Promise<unknown>[]; cleanup: () => Promise<unknown>[] };
|
||||||
|
|
||||||
|
const ARTIST_AUTHOR_SEPARATOR_REGEX = /\s*[,|、]\s*/;
|
||||||
|
|
||||||
export class Mangas {
|
export class Mangas {
|
||||||
static getIds(mangas: MangaIdInfo[]): number[] {
|
static getIds(mangas: MangaIdInfo[]): number[] {
|
||||||
return mangas.map((manga) => manga.id);
|
return mangas.map((manga) => manga.id);
|
||||||
@@ -592,4 +596,12 @@ export class Mangas {
|
|||||||
Mangas.isType(manga, MangaType.MANHUA)
|
Mangas.isType(manga, MangaType.MANHUA)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getArtists<Manga extends MangaArtistInfo>(manga: Manga): string[] | undefined {
|
||||||
|
return manga.artist?.split(ARTIST_AUTHOR_SEPARATOR_REGEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAuthors<Manga extends MangaAuthorInfo>(manga: Manga): string[] | undefined {
|
||||||
|
return manga.author?.split(ARTIST_AUTHOR_SEPARATOR_REGEX);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type RestrictedNavBarItem<Show extends NavbarItem['show']> = Omit<NavbarItem, 's
|
|||||||
|
|
||||||
const NAVIGATION_BAR_BASE_ITEMS = [
|
const NAVIGATION_BAR_BASE_ITEMS = [
|
||||||
{
|
{
|
||||||
path: AppRoutes.library.path,
|
path: AppRoutes.library.path() as RestrictedNavBarItem<'both'>['path'],
|
||||||
title: 'library.title',
|
title: 'library.title',
|
||||||
SelectedIconComponent: CollectionsBookmarkIcon,
|
SelectedIconComponent: CollectionsBookmarkIcon,
|
||||||
IconComponent: CollectionsOutlinedBookmarkIcon,
|
IconComponent: CollectionsOutlinedBookmarkIcon,
|
||||||
|
|||||||
Reference in New Issue
Block a user