Set manga thumbnail as background image

This commit is contained in:
schroda
2024-09-10 14:45:08 +02:00
parent b3a8821023
commit a9e0853e34
6 changed files with 83 additions and 4 deletions

View File

@@ -819,6 +819,10 @@
}
},
"appearance": {
"manga_thumbnail_backdrop": {
"description": "Sets the manga thumbnail as the background image on the manga page",
"title": "Manga thumbnail as background"
},
"theme": {
"create": {
"action": "Creating theme…",

View File

@@ -37,6 +37,7 @@ import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts
import { MangaType, SourceType } from '@/lib/graphql/generated/graphql.ts';
import { useLocalStorage } from '@/util/useStorage.tsx';
import { useResizeObserver } from '@/util/useResizeObserver.tsx';
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
const DetailsWrapper = styled('div')(({ theme }) => ({
display: 'flex',
@@ -50,7 +51,29 @@ const DetailsWrapper = styled('div')(({ theme }) => ({
},
}));
const TopContentWrapper = styled('div')(() => ({}));
const TopContentWrapper = styled('div', {
shouldForwardProp: (prop) => !['url', 'mangaThumbnailBackdrop'].includes(prop as string),
})<{
url: string;
mangaThumbnailBackdrop: boolean;
}>(({ theme, url, mangaThumbnailBackdrop }) => ({
position: 'relative',
backgroundImage: mangaThumbnailBackdrop ? `url(${url})` : undefined,
backgroundRepeat: mangaThumbnailBackdrop ? 'no-repeat' : undefined,
backgroundSize: mangaThumbnailBackdrop ? 'cover' : undefined,
borderRadius: mangaThumbnailBackdrop ? theme.shape.borderRadius : undefined,
'&::before': mangaThumbnailBackdrop && {
position: 'absolute',
display: 'inline-block',
content: '""',
top: 0,
left: 0,
width: '100%',
height: '100%',
background: `linear-gradient(to top, ${theme.palette.background.default}, transparent 100%, transparent 1px),linear-gradient(to right, ${theme.palette.background.default}, transparent 50%, transparent 1px),linear-gradient(to bottom, ${theme.palette.background.default}, transparent 50%, transparent 1px),linear-gradient(to left, ${theme.palette.background.default}, transparent 50%, transparent 1px)`,
backdropFilter: 'blur(4.5px) brightness(0.75)',
},
}));
const ThumbnailMetadataWrapper = styled('div')(({ theme }) => ({
display: 'flex',
@@ -58,6 +81,7 @@ const ThumbnailMetadataWrapper = styled('div')(({ theme }) => ({
}));
const MetadataContainer = styled('div')(({ theme }) => ({
zIndex: 1,
marginLeft: theme.spacing(1),
}));
@@ -261,6 +285,10 @@ export const MangaDetails = ({
}) => {
const { t } = useTranslation();
const {
settings: { mangaThumbnailBackdrop },
} = useMetadataServerSettings();
useEffect(() => {
if (!manga.source) {
makeToast(translate('source.error.label.source_not_found'), 'error');
@@ -281,7 +309,7 @@ export const MangaDetails = ({
return (
<>
<DetailsWrapper>
<TopContentWrapper>
<TopContentWrapper url={Mangas.getThumbnailUrl(manga)} mangaThumbnailBackdrop={mangaThumbnailBackdrop}>
<ThumbnailMetadataWrapper>
<Thumbnail manga={manga} />
<MetadataContainer>

View File

@@ -60,6 +60,7 @@ const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
// themes
'customThemes',
'mangaThumbnailBackdrop',
];
/**

View File

@@ -54,6 +54,7 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
// themes
customThemes: {},
mangaThumbnailBackdrop: true,
});
export const convertSettingsToMetadata = (

View File

@@ -25,12 +25,19 @@ import { I18nResourceCode, i18nResources } from '@/i18n';
import { langCodeToName } from '@/util/language.tsx';
import { getTheme } from '@/lib/ui/AppThemes.ts';
import { ThemeList } from '@/screens/settings/appearance/theme/ThemeList.tsx';
import {
createUpdateMetadataServerSettings,
useMetadataServerSettings,
} from '@/lib/metadata/metadataServerSettings.ts';
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { MetadataThemeSettings } from '@/typings.ts';
import { makeToast } from '@/components/util/Toast.tsx';
export const Appearance = () => {
const { t, i18n } = useTranslation();
const { themeMode, setThemeMode, pureBlackMode, setPureBlackMode, appTheme } = useContext(ThemeModeContext);
const isDarkMode =
getTheme(appTheme).muiTheme.palette?.mode === 'dark' || MediaQuery.getThemeMode() === ThemeMode.DARK;
const { setTitle, setAction } = useContext(NavBarContext);
useEffect(() => {
@@ -43,9 +50,34 @@ export const Appearance = () => {
};
}, [t]);
const {
settings,
request: { loading, error, refetch },
} = useMetadataServerSettings();
const updateMetadataSetting = createUpdateMetadataServerSettings<keyof MetadataThemeSettings>(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
);
const isDarkMode =
getTheme(appTheme).muiTheme.palette?.mode === 'dark' || MediaQuery.getThemeMode() === ThemeMode.DARK;
const DEFAULT_ITEM_WIDTH = 300;
const [itemWidth, setItemWidth] = useLocalStorage<number>('ItemWidth', DEFAULT_ITEM_WIDTH);
if (loading) {
return <LoadingPlaceholder />;
}
if (error) {
return (
<EmptyViewAbsoluteCentered
message={t('global.error.label.failed_to_load_data')}
messageExtra={error.message}
retry={() => refetch().catch(defaultPromiseErrorHandler('Appearance::refetch'))}
/>
);
}
return (
<List
subheader={
@@ -121,6 +153,18 @@ export const Appearance = () => {
showSlider
handleUpdate={setItemWidth}
/>
<ListItem>
<ListItemText
primary={t('settings.appearance.manga_thumbnail_backdrop.title')}
secondary={t('settings.appearance.manga_thumbnail_backdrop.description')}
/>
<Switch
edge="end"
checked={settings.mangaThumbnailBackdrop}
onChange={(e) => updateMetadataSetting('mangaThumbnailBackdrop', e.target.checked)}
/>
</ListItem>
</List>
</List>
);

View File

@@ -159,6 +159,7 @@ export type MetadataUpdateSettings = {
export type MetadataThemeSettings = {
customThemes: Record<string, AppTheme>;
mangaThumbnailBackdrop: boolean;
};
export type MetadataServerSettings = MetadataDownloadSettings &