2021-03-26 04:17:02 +04:30
|
|
|
/*
|
|
|
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
2021-01-26 23:32:12 +03:30
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
2023-05-18 13:17:41 +02:00
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
2021-01-26 23:32:12 +03:30
|
|
|
|
2021-09-09 17:51:22 +04:30
|
|
|
import FavoriteIcon from '@mui/icons-material/Favorite';
|
|
|
|
|
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
|
|
|
|
import PublicIcon from '@mui/icons-material/Public';
|
2023-06-04 20:58:44 +02:00
|
|
|
import { styled } from '@mui/material/styles';
|
2024-05-23 00:40:20 +02:00
|
|
|
import React, { ComponentProps, useEffect, useMemo } from 'react';
|
2023-03-23 13:59:32 +01:00
|
|
|
import { useTranslation } from 'react-i18next';
|
2023-05-01 23:31:14 +02:00
|
|
|
import { t as translate } from 'i18next';
|
2024-04-14 15:50:12 +02:00
|
|
|
import Link from '@mui/material/Link';
|
2024-05-23 00:40:20 +02:00
|
|
|
import Typography from '@mui/material/Typography';
|
2023-10-15 16:03:08 +02:00
|
|
|
import { ISource, TManga } from '@/typings';
|
2023-10-28 00:32:02 +02:00
|
|
|
import { makeToast } from '@/components/util/Toast';
|
2024-02-16 19:38:13 +01:00
|
|
|
import { Mangas } from '@/lib/data/Mangas.ts';
|
2024-02-16 21:21:47 +01:00
|
|
|
import { SpinnerImage } from '@/components/util/SpinnerImage.tsx';
|
2024-03-29 00:15:03 +01:00
|
|
|
import { CustomIconButton } from '@/components/atoms/CustomIconButton';
|
2024-03-10 17:36:22 +01:00
|
|
|
import { TrackMangaButton } from '@/components/manga/TrackMangaButton.tsx';
|
2024-04-07 15:33:08 +02:00
|
|
|
import { useManageMangaLibraryState } from '@/components/manga/useManageMangaLibraryState.tsx';
|
2024-05-23 00:40:20 +02:00
|
|
|
import { Metadata as BaseMetadata } from '@/components/atoms/Metadata.tsx';
|
2021-02-21 04:27:41 +03:30
|
|
|
|
2023-06-04 20:58:44 +02:00
|
|
|
const DetailsWrapper = styled('div')(({ theme }) => ({
|
|
|
|
|
width: '100%',
|
|
|
|
|
[theme.breakpoints.up('md')]: {
|
|
|
|
|
position: 'sticky',
|
|
|
|
|
top: '64px',
|
|
|
|
|
left: '0px',
|
|
|
|
|
width: '50vw',
|
|
|
|
|
height: 'calc(100vh - 64px)',
|
|
|
|
|
alignSelf: 'flex-start',
|
|
|
|
|
overflowY: 'auto',
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const TopContentWrapper = styled('div')(() => ({
|
|
|
|
|
padding: '10px',
|
|
|
|
|
// [theme.breakpoints.up('md')]: {
|
|
|
|
|
// minWidth: '50%',
|
|
|
|
|
// },
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const ThumbnailMetadataWrapper = styled('div')(() => ({
|
|
|
|
|
display: 'flex',
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const Thumbnail = styled('div')(() => ({
|
|
|
|
|
'& img': {
|
|
|
|
|
borderRadius: 4,
|
|
|
|
|
maxWidth: '100%',
|
|
|
|
|
minWidth: '100%',
|
|
|
|
|
height: 'auto',
|
|
|
|
|
},
|
|
|
|
|
maxWidth: '50%',
|
|
|
|
|
// [theme.breakpoints.up('md')]: {
|
|
|
|
|
// minWidth: '100px',
|
|
|
|
|
// },
|
|
|
|
|
}));
|
|
|
|
|
|
2024-05-23 00:40:20 +02:00
|
|
|
const MetadataContainer = styled('div')(({ theme }) => ({
|
2023-06-04 20:58:44 +02:00
|
|
|
marginLeft: 15,
|
|
|
|
|
maxWidth: '100%',
|
|
|
|
|
'& span': {
|
|
|
|
|
fontWeight: '400',
|
|
|
|
|
},
|
|
|
|
|
[theme.breakpoints.up('lg')]: {
|
|
|
|
|
fontSize: '1.3em',
|
|
|
|
|
},
|
|
|
|
|
}));
|
2024-05-23 00:40:20 +02:00
|
|
|
|
|
|
|
|
const Metadata = (props: ComponentProps<typeof BaseMetadata>) => (
|
|
|
|
|
<BaseMetadata {...props} titleProps={{ variant: 'h6' }} valueProps={{ variant: 'h6' }} />
|
|
|
|
|
);
|
|
|
|
|
|
2024-03-29 00:15:03 +01:00
|
|
|
const MangaButtonsContainer = styled('div')(({ theme }) => ({
|
2023-06-04 20:58:44 +02:00
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'space-around',
|
2024-03-29 00:15:03 +01:00
|
|
|
'& button, a': {
|
2023-07-04 21:55:39 +02:00
|
|
|
borderRadius: '25px',
|
|
|
|
|
textTransform: 'none',
|
|
|
|
|
paddingLeft: '20px',
|
|
|
|
|
paddingRight: '20px',
|
|
|
|
|
fontSize: 'x-large',
|
|
|
|
|
[theme.breakpoints.down('sm')]: {
|
|
|
|
|
fontSize: 'larger',
|
|
|
|
|
},
|
2023-06-04 20:58:44 +02:00
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
const BottomContentWrapper = styled('div')(({ theme }) => ({
|
|
|
|
|
paddingLeft: '10px',
|
|
|
|
|
paddingRight: '10px',
|
|
|
|
|
[theme.breakpoints.up('md')]: {
|
|
|
|
|
fontSize: '1.2em',
|
|
|
|
|
// maxWidth: '50%',
|
|
|
|
|
},
|
|
|
|
|
[theme.breakpoints.up('lg')]: {
|
|
|
|
|
fontSize: '1.3em',
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
const Description = styled('div')(() => ({
|
|
|
|
|
'& h4': {
|
|
|
|
|
marginTop: '1em',
|
|
|
|
|
marginBottom: 0,
|
|
|
|
|
},
|
|
|
|
|
'& p': {
|
|
|
|
|
textAlign: 'justify',
|
|
|
|
|
textJustify: 'inter-word',
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
const Genres = styled('div')(() => ({
|
|
|
|
|
display: 'flex',
|
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
|
'& h5': {
|
|
|
|
|
border: '2px solid #2196f3',
|
|
|
|
|
borderRadius: '1.13em',
|
|
|
|
|
marginRight: '1em',
|
|
|
|
|
marginTop: 0,
|
|
|
|
|
marginBottom: '10px',
|
|
|
|
|
padding: '0.3em',
|
|
|
|
|
color: '#2196f3',
|
|
|
|
|
},
|
|
|
|
|
}));
|
2021-01-19 20:20:28 +03:30
|
|
|
|
2023-09-08 00:44:01 +02:00
|
|
|
const OpenSourceButton = ({ url }: { url?: string | null }) => {
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
|
2024-03-29 00:15:03 +01:00
|
|
|
const button = useMemo(
|
|
|
|
|
() => (
|
|
|
|
|
<CustomIconButton
|
|
|
|
|
size="large"
|
|
|
|
|
disabled={!url}
|
|
|
|
|
sx={{ color: 'inherit' }}
|
|
|
|
|
component={Link}
|
|
|
|
|
href={url ?? undefined}
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noreferrer"
|
|
|
|
|
>
|
|
|
|
|
<PublicIcon />
|
2023-09-08 00:44:01 +02:00
|
|
|
{t('global.button.open_site')}
|
2024-03-29 00:15:03 +01:00
|
|
|
</CustomIconButton>
|
|
|
|
|
),
|
|
|
|
|
[url],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!url) {
|
|
|
|
|
return button;
|
2023-09-08 00:44:01 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-29 00:15:03 +01:00
|
|
|
return button;
|
2023-09-08 00:44:01 +02:00
|
|
|
};
|
|
|
|
|
|
2023-02-06 10:06:33 +01:00
|
|
|
interface IProps {
|
2023-10-15 16:03:08 +02:00
|
|
|
manga: TManga;
|
2021-03-14 23:57:33 +03:30
|
|
|
}
|
|
|
|
|
|
2023-09-08 00:44:01 +02:00
|
|
|
function getSourceName(source?: ISource | null) {
|
2023-05-01 23:31:14 +02:00
|
|
|
if (!source) {
|
|
|
|
|
return translate('global.label.unknown');
|
2021-03-17 19:17:03 +03:30
|
|
|
}
|
2023-05-01 23:31:14 +02:00
|
|
|
|
|
|
|
|
return source.displayName ?? source.id;
|
2021-01-19 20:20:28 +03:30
|
|
|
}
|
|
|
|
|
|
2023-09-08 00:44:01 +02:00
|
|
|
function getValueOrUnknown(val?: string | null) {
|
2024-05-23 01:21:20 +02:00
|
|
|
return val ?? translate('global.label.unknown');
|
2021-03-17 19:17:03 +03:30
|
|
|
}
|
|
|
|
|
|
2023-10-28 00:32:02 +02:00
|
|
|
export const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
2023-03-23 13:59:32 +01:00
|
|
|
const { t } = useTranslation();
|
2023-12-27 18:08:16 +01:00
|
|
|
|
2023-05-02 00:35:56 +02:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (!manga.source) {
|
|
|
|
|
makeToast(translate('source.error.label.source_not_found'), 'error');
|
|
|
|
|
}
|
|
|
|
|
}, [manga.source]);
|
2023-05-01 23:31:14 +02:00
|
|
|
|
2024-04-07 15:33:08 +02:00
|
|
|
const { CategorySelectComponent, updateLibraryState } = useManageMangaLibraryState(manga);
|
2021-01-19 20:20:28 +03:30
|
|
|
|
|
|
|
|
return (
|
2023-12-27 18:08:16 +01:00
|
|
|
<>
|
|
|
|
|
<DetailsWrapper>
|
|
|
|
|
<TopContentWrapper>
|
|
|
|
|
<ThumbnailMetadataWrapper>
|
|
|
|
|
<Thumbnail>
|
2024-02-16 21:21:47 +01:00
|
|
|
<SpinnerImage src={Mangas.getThumbnailUrl(manga)} alt="Manga Thumbnail" />
|
2023-12-27 18:08:16 +01:00
|
|
|
</Thumbnail>
|
2024-05-23 00:40:20 +02:00
|
|
|
<MetadataContainer>
|
|
|
|
|
<Typography variant="h4" component="h1" sx={{ mb: 1 }}>
|
|
|
|
|
{manga.title}
|
|
|
|
|
</Typography>
|
|
|
|
|
<Metadata title={t('manga.label.author')} value={getValueOrUnknown(manga.author)} />
|
|
|
|
|
<Metadata title={t('manga.label.artist')} value={getValueOrUnknown(manga.artist)} />
|
|
|
|
|
<Metadata title={t('manga.label.status')} value={getValueOrUnknown(manga.status)} />
|
|
|
|
|
<Metadata title={t('source.title')} value={getSourceName(manga.source)} />
|
|
|
|
|
</MetadataContainer>
|
2023-12-27 18:08:16 +01:00
|
|
|
</ThumbnailMetadataWrapper>
|
2024-03-29 00:15:03 +01:00
|
|
|
<MangaButtonsContainer>
|
|
|
|
|
<CustomIconButton
|
2024-04-07 15:33:08 +02:00
|
|
|
onClick={updateLibraryState}
|
2024-03-29 00:15:03 +01:00
|
|
|
size="large"
|
|
|
|
|
sx={{ color: manga.inLibrary ? '#2196f3' : 'inherit' }}
|
|
|
|
|
>
|
|
|
|
|
{manga.inLibrary ? <FavoriteIcon /> : <FavoriteBorderIcon />}
|
|
|
|
|
{manga.inLibrary ? t('manga.button.in_library') : t('manga.button.add_to_library')}
|
|
|
|
|
</CustomIconButton>
|
2024-03-10 17:36:22 +01:00
|
|
|
<TrackMangaButton manga={manga} />
|
2023-12-27 18:08:16 +01:00
|
|
|
<OpenSourceButton url={manga.realUrl} />
|
|
|
|
|
</MangaButtonsContainer>
|
|
|
|
|
</TopContentWrapper>
|
|
|
|
|
<BottomContentWrapper>
|
|
|
|
|
<Description>
|
|
|
|
|
<h4>{t('settings.about.title')}</h4>
|
|
|
|
|
<p style={{ whiteSpace: 'pre-line' }}>{manga.description}</p>
|
|
|
|
|
</Description>
|
|
|
|
|
<Genres>
|
|
|
|
|
{manga.genre.map((g) => (
|
|
|
|
|
<h5 key={g}>{g}</h5>
|
|
|
|
|
))}
|
|
|
|
|
</Genres>
|
|
|
|
|
</BottomContentWrapper>
|
|
|
|
|
</DetailsWrapper>
|
2024-04-07 15:33:08 +02:00
|
|
|
{CategorySelectComponent}
|
2023-12-27 18:08:16 +01:00
|
|
|
</>
|
2021-01-19 20:20:28 +03:30
|
|
|
);
|
2022-11-09 18:09:15 +01:00
|
|
|
};
|