Update manga page styling

This commit is contained in:
schroda
2024-08-30 04:34:28 +02:00
parent 74d777a52b
commit 053db4a1e3
7 changed files with 82 additions and 137 deletions

View File

@@ -12,7 +12,6 @@ import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev'; import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
import { loadable } from 'react-lazily/loadable'; import { loadable } from 'react-lazily/loadable';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import { AppContext } from '@/components/context/AppContext'; import { AppContext } from '@/components/context/AppContext';
import '@/i18n'; import '@/i18n';
import { DefaultNavBar } from '@/components/navbar/DefaultNavBar'; import { DefaultNavBar } from '@/components/navbar/DefaultNavBar';
@@ -79,23 +78,22 @@ const BackgroundSubscriptions = () => {
}; };
const MainApp = () => { const MainApp = () => {
const { navBarWidth, bottomBarHeight } = useNavBarContext(); const { navBarWidth, appBarHeight, bottomBarHeight } = useNavBarContext();
return ( return (
<Box <Box
id="appMainContainer" id="appMainContainer"
component="main" component="main"
sx={{ sx={{
minHeight: `calc(100vh - ${bottomBarHeight}px)`, minHeight: `calc(100vh - ${appBarHeight + bottomBarHeight}px)`,
width: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`, width: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`,
minWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`, minWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`,
maxWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`, maxWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`,
position: 'relative', position: 'relative',
mt: `${appBarHeight}px`,
mb: `${bottomBarHeight}px`, mb: `${bottomBarHeight}px`,
}} }}
> >
<Toolbar />
<ErrorBoundary> <ErrorBoundary>
<Routes> <Routes>
{/* General Routes */} {/* General Routes */}

View File

@@ -8,15 +8,17 @@
import Button, { ButtonProps } from '@mui/material/Button'; import Button, { ButtonProps } from '@mui/material/Button';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { TypographyProps } from '@mui/material/Typography'; import { ForwardedRef, forwardRef } from 'react';
export const CustomIconButton = <C extends React.ElementType>({ export const CustomIconButton = forwardRef(
children, <C extends React.ElementType>(
...props { children, ...props }: ButtonProps<C, { component?: C }>,
}: ButtonProps & TypographyProps<C, { component?: C }>) => ( ref: ForwardedRef<HTMLButtonElement | null>,
<Button {...props}> ) => (
<Stack direction="row" alignItems="center" justifyContent="center" gap="10px" flexWrap="wrap"> <Button ref={ref} {...props}>
{children} <Stack direction="row" alignItems="center" justifyContent="center" gap={1} flexWrap="wrap">
</Stack> {children}
</Button> </Stack>
</Button>
),
); );

View File

@@ -16,7 +16,7 @@ import BookmarkRemove from '@mui/icons-material/BookmarkRemove';
import BookmarkAdd from '@mui/icons-material/BookmarkAdd'; import BookmarkAdd from '@mui/icons-material/BookmarkAdd';
import DoneAll from '@mui/icons-material/DoneAll'; import DoneAll from '@mui/icons-material/DoneAll';
import { useMemo } from 'react'; import { useMemo } from 'react';
import PublicIcon from '@mui/icons-material/Public'; import LaunchIcon from '@mui/icons-material/Launch';
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts'; import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
import { import {
actionToTranslationKey, actionToTranslationKey,
@@ -159,7 +159,7 @@ export const ChapterActionMenuItems = ({
)} )}
{isSingleMode && ( {isSingleMode && (
<MenuItem <MenuItem
Icon={PublicIcon} Icon={LaunchIcon}
disabled={!chapter!.realUrl} disabled={!chapter!.realUrl}
onClick={() => { onClick={() => {
window.open(chapter!.realUrl!, '_blank', 'noopener,noreferrer'); window.open(chapter!.realUrl!, '_blank', 'noopener,noreferrer');

View File

@@ -58,7 +58,6 @@ const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
padding: 0, padding: 0,
minHeight: '200px', minHeight: '200px',
[theme.breakpoints.up('md')]: { [theme.breakpoints.up('md')]: {
width: '50vw',
// 64px for the Appbar, 48px for the ChapterCount Header // 64px for the Appbar, 48px for the ChapterCount Header
height: 'calc(100vh - 64px - 48px)', height: 'calc(100vh - 64px - 48px)',
margin: 0, margin: 0,
@@ -165,7 +164,7 @@ export const ChapterList = ({
return ( return (
<> <>
<Stack direction="column" sx={{ position: 'relative' }}> <Stack direction="column" sx={{ position: 'relative', flexBasis: '60%' }}>
<ChapterListHeader direction="row" alignItems="center" justifyContent="space-between"> <ChapterListHeader direction="row" alignItems="center" justifyContent="space-between">
<Typography variant="h5"> <Typography variant="h5">
{`${visibleChapters.length} ${t('chapter.title_one', { {`${visibleChapters.length} ${t('chapter.title_one', {

View File

@@ -8,14 +8,17 @@
import FavoriteIcon from '@mui/icons-material/Favorite'; import FavoriteIcon from '@mui/icons-material/Favorite';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import PublicIcon from '@mui/icons-material/Public';
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
import { ComponentProps, useEffect, useMemo } from 'react'; import { ComponentProps, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { t as translate } from 'i18next'; import { t as translate } from 'i18next';
import Link from '@mui/material/Link'; import Link from '@mui/material/Link';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { useLongPress } from 'use-long-press'; import { useLongPress } from 'use-long-press';
import Chip from '@mui/material/Chip';
import Tooltip from '@mui/material/Tooltip';
import LaunchIcon from '@mui/icons-material/Launch';
import Stack from '@mui/material/Stack';
import { makeToast } from '@/components/util/Toast'; import { makeToast } from '@/components/util/Toast';
import { Mangas, MangaThumbnailInfo, MangaTrackRecordInfo } from '@/lib/data/Mangas.ts'; import { Mangas, MangaThumbnailInfo, MangaTrackRecordInfo } from '@/lib/data/Mangas.ts';
import { SpinnerImage } from '@/components/util/SpinnerImage.tsx'; import { SpinnerImage } from '@/components/util/SpinnerImage.tsx';
@@ -27,132 +30,66 @@ import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts
import { MangaType, SourceType } from '@/lib/graphql/generated/graphql.ts'; import { MangaType, SourceType } from '@/lib/graphql/generated/graphql.ts';
const DetailsWrapper = styled('div')(({ theme }) => ({ const DetailsWrapper = styled('div')(({ theme }) => ({
width: '100%', display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2),
padding: theme.spacing(1),
[theme.breakpoints.up('md')]: { [theme.breakpoints.up('md')]: {
position: 'sticky', flexBasis: '40%',
top: '64px',
left: '0px',
width: '50vw',
height: 'calc(100vh - 64px)', height: 'calc(100vh - 64px)',
alignSelf: 'flex-start',
overflowY: 'auto', overflowY: 'auto',
}, },
})); }));
const TopContentWrapper = styled('div')(() => ({ const TopContentWrapper = styled('div')(() => ({}));
padding: '10px',
// [theme.breakpoints.up('md')]: {
// minWidth: '50%',
// },
}));
const ThumbnailMetadataWrapper = styled('div')(() => ({ const ThumbnailMetadataWrapper = styled('div')(() => ({
display: 'flex', display: 'flex',
})); }));
const Thumbnail = styled('div')(() => ({ const Thumbnail = styled('div')(({ theme }) => ({
'& img': { '& img': {
borderRadius: 4, borderRadius: 4,
maxWidth: '100%', width: '150px',
minWidth: '100%',
height: 'auto', height: 'auto',
}, [theme.breakpoints.up('lg')]: {
maxWidth: '50%', width: '200px',
// [theme.breakpoints.up('md')]: { },
// minWidth: '100px', [theme.breakpoints.up('xl')]: {
// }, width: '300px',
}));
const MetadataContainer = styled('div')(({ theme }) => ({
marginLeft: 15,
maxWidth: '100%',
'& span': {
fontWeight: '400',
},
[theme.breakpoints.up('lg')]: {
fontSize: '1.3em',
},
}));
const Metadata = (props: ComponentProps<typeof BaseMetadata>) => (
<BaseMetadata {...props} titleProps={{ variant: 'h6' }} valueProps={{ variant: 'h6' }} />
);
const MangaButtonsContainer = styled('div')(({ theme }) => ({
display: 'flex',
justifyContent: 'space-around',
'& button, a': {
borderRadius: '25px',
textTransform: 'none',
paddingLeft: '20px',
paddingRight: '20px',
fontSize: 'x-large',
[theme.breakpoints.down('sm')]: {
fontSize: 'larger',
}, },
}, },
})); }));
const BottomContentWrapper = styled('div')(({ theme }) => ({
paddingLeft: '10px', const MetadataContainer = styled('div')(({ theme }) => ({
paddingRight: '10px', marginLeft: theme.spacing(1),
[theme.breakpoints.up('md')]: {
fontSize: '1.2em',
// maxWidth: '50%',
},
[theme.breakpoints.up('lg')]: {
fontSize: '1.3em',
},
})); }));
const Description = styled('div')(() => ({
'& h4': { const Metadata = (props: ComponentProps<typeof BaseMetadata>) => <BaseMetadata {...props} />;
marginTop: '1em',
marginBottom: 0, const MangaButtonsContainer = styled('div')(({ theme }) => ({
},
'& p': {
textAlign: 'justify',
textJustify: 'inter-word',
},
}));
const Genres = styled('div')(() => ({
display: 'flex', display: 'flex',
flexWrap: 'wrap', gap: theme.spacing(1),
'& h5': {
border: '2px solid #2196f3',
borderRadius: '1.13em',
marginRight: '1em',
marginTop: 0,
marginBottom: '10px',
padding: '0.3em',
color: '#2196f3',
},
})); }));
const OpenSourceButton = ({ url }: { url?: string | null }) => { const OpenSourceButton = ({ url }: { url?: string | null }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const button = useMemo( return (
() => ( <Tooltip title={t('global.button.open_site')}>
<CustomIconButton <CustomIconButton
size="large" size="medium"
disabled={!url} disabled={!url}
sx={{ color: 'inherit' }}
component={Link} component={Link}
href={url ?? undefined} href={url ?? undefined}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
variant="outlined"
> >
<PublicIcon /> <LaunchIcon />
{t('global.button.open_site')}
</CustomIconButton> </CustomIconButton>
), </Tooltip>
[url],
); );
if (!url) {
return button;
}
return button;
}; };
function getSourceName(source?: Pick<SourceType, 'id' | 'displayName'> | null): string { function getSourceName(source?: Pick<SourceType, 'id' | 'displayName'> | null): string {
@@ -167,6 +104,30 @@ function getValueOrUnknown(val?: string | null) {
return val ?? translate('global.label.unknown'); return val ?? translate('global.label.unknown');
} }
const DescriptionGenre = ({
manga: { description, genre: genres },
}: {
manga: Pick<MangaType, 'description' | 'genre'>;
}) => (
<>
<Typography style={{ whiteSpace: 'pre-line', textAlign: 'justify', textJustify: 'inter-word' }}>
{description}
</Typography>
<Stack
sx={{
flexDirection: 'row',
flexWrap: isCollapsed ? 'no-wrap' : 'wrap',
gap: 1,
overflowX: isCollapsed ? 'auto' : null,
}}
>
{genres.map((genre) => (
<Chip label={genre} variant="outlined" />
))}
</Stack>
</>
);
export const MangaDetails = ({ export const MangaDetails = ({
manga, manga,
}: { }: {
@@ -208,8 +169,8 @@ export const MangaDetails = ({
</Thumbnail> </Thumbnail>
<MetadataContainer> <MetadataContainer>
<Typography <Typography
variant="h4" variant="h5"
component="h1" component="h2"
sx={{ mb: 1, '@media not (pointer: fine)': { userSelect: 'none' } }} sx={{ mb: 1, '@media not (pointer: fine)': { userSelect: 'none' } }}
{...copyTitleLongPressBind()} {...copyTitleLongPressBind()}
> >
@@ -223,9 +184,9 @@ export const MangaDetails = ({
</ThumbnailMetadataWrapper> </ThumbnailMetadataWrapper>
<MangaButtonsContainer> <MangaButtonsContainer>
<CustomIconButton <CustomIconButton
size="medium"
onClick={updateLibraryState} onClick={updateLibraryState}
size="large" variant={manga.inLibrary ? 'contained' : 'outlined'}
sx={{ color: manga.inLibrary ? '#2196f3' : 'inherit' }}
> >
{manga.inLibrary ? <FavoriteIcon /> : <FavoriteBorderIcon />} {manga.inLibrary ? <FavoriteIcon /> : <FavoriteBorderIcon />}
{manga.inLibrary ? t('manga.button.in_library') : t('manga.button.add_to_library')} {manga.inLibrary ? t('manga.button.in_library') : t('manga.button.add_to_library')}
@@ -234,17 +195,7 @@ export const MangaDetails = ({
<OpenSourceButton url={manga.realUrl} /> <OpenSourceButton url={manga.realUrl} />
</MangaButtonsContainer> </MangaButtonsContainer>
</TopContentWrapper> </TopContentWrapper>
<BottomContentWrapper> <DescriptionGenre manga={manga} />
<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> </DetailsWrapper>
{CategorySelectComponent} {CategorySelectComponent}
</> </>

View File

@@ -52,11 +52,11 @@ export const TrackMangaButton = ({ manga }: { manga: MangaTrackRecordInfo & Pick
<> <>
<CustomIconButton <CustomIconButton
{...bindTrigger(popupState)} {...bindTrigger(popupState)}
size="medium"
disabled={trackerList.loading || !!trackerList.error} disabled={trackerList.loading || !!trackerList.error}
onClick={() => handleClick(popupState.open)} onClick={() => handleClick(popupState.open)}
onTouchStart={() => handleClick(popupState.open)} onTouchStart={() => handleClick(popupState.open)}
size="large" variant={trackersInUse.length ? 'contained' : 'outlined'}
sx={{ color: trackersInUse.length ? '#2196f3' : 'inherit' }}
> >
{trackersInUse.length ? <CheckIcon /> : <SyncIcon />} {trackersInUse.length ? <CheckIcon /> : <SyncIcon />}
{trackersInUse.length {trackersInUse.length

View File

@@ -164,12 +164,7 @@ export function DefaultNavBar() {
{getOptionForDirection(<ArrowBack />, <ArrowForwardIcon />)} {getOptionForDirection(<ArrowBack />, <ArrowForwardIcon />)}
</IconButton> </IconButton>
)} )}
<Typography <Typography variant="h5" component="h1" sx={{ flexGrow: 1 }} noWrap textOverflow="ellipsis">
variant={isMobileWidth ? 'h6' : 'h5'}
sx={{ flexGrow: 1 }}
noWrap
textOverflow="ellipsis"
>
{title} {title}
</Typography> </Typography>
{action} {action}