Add "safe area inset" setting to reader
This commit is contained in:
@@ -13,7 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
- (**Server Update**) Add an option to disable showing information when the server got updated
|
- (**Server Update**) Add an option to disable showing information when the server got updated
|
||||||
- (**Manga**) Add an option to include client data during migration
|
- (**Manga**) Add an option to include client data during migration
|
||||||
- (**Migrate**) Add bulk migration
|
- (**Migrate**) Add bulk migration
|
||||||
- (**Reader**) Respect the safe inset area (e.g., top-notch) of devices
|
- (**Reader**) Add an option to apply safe area paddings (e.g., top-notch, rounded corners)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -20,42 +20,34 @@ export enum DirectionOffset {
|
|||||||
NEXT = 1,
|
NEXT = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DisplayDataTranslation {
|
interface DisplayData {
|
||||||
isTitleString?: never;
|
title: MessageDescriptor | string;
|
||||||
title: MessageDescriptor;
|
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DisplayDataString {
|
|
||||||
isTitleString: true;
|
|
||||||
title: string;
|
|
||||||
icon: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
type DisplayData = DisplayDataTranslation | DisplayDataString;
|
|
||||||
|
|
||||||
export type ValueToDisplayData<Value extends string | number> = Record<Value, DisplayData>;
|
export type ValueToDisplayData<Value extends string | number> = Record<Value, DisplayData>;
|
||||||
|
|
||||||
export interface MultiValueButtonBaseProps<Value extends string | number> {
|
export interface MultiValueButtonBaseProps<Value extends string | number, MultiValue extends Value | Value[] = Value> {
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
value: Value;
|
value: MultiValue;
|
||||||
defaultValue?: Value;
|
defaultValue?: Value;
|
||||||
values: Value[];
|
values: Value[];
|
||||||
setValue: (value: Value) => void;
|
setValue: (value: MultiValue) => void;
|
||||||
valueToDisplayData: ValueToDisplayData<Value>;
|
valueToDisplayData: ValueToDisplayData<Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MultiValueButtonDefaultableProps<Value extends string | number> extends OptionalProperty<
|
export interface MultiValueButtonDefaultableProps<
|
||||||
MultiValueButtonBaseProps<Value>,
|
Value extends string | number,
|
||||||
'value'
|
MultiValue extends Value | Value[] = Value,
|
||||||
> {
|
> extends OptionalProperty<MultiValueButtonBaseProps<Value, MultiValue>, 'value'> {
|
||||||
isDefaultable?: boolean;
|
isDefaultable?: boolean;
|
||||||
onDefault?: () => void;
|
onDefault?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MultiValueButtonProps<Value extends string | number> =
|
export type MultiValueButtonProps<Value extends string | number, MultiValue extends Value | Value[] = Value> =
|
||||||
| (MultiValueButtonBaseProps<Value> & PropertiesNever<MultiValueButtonDefaultableProps<Value>>)
|
| (MultiValueButtonBaseProps<Value, MultiValue> &
|
||||||
| MultiValueButtonDefaultableProps<Value>;
|
PropertiesNever<MultiValueButtonDefaultableProps<Value, MultiValue>>)
|
||||||
|
| MultiValueButtonDefaultableProps<Value, MultiValue>;
|
||||||
|
|
||||||
export enum ScrollOffset {
|
export enum ScrollOffset {
|
||||||
BACKWARD,
|
BACKWARD,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
|
|||||||
import type { MultiValueButtonProps } from '@/base/Base.types.ts';
|
import type { MultiValueButtonProps } from '@/base/Base.types.ts';
|
||||||
import { Superscript } from '@/base/components/texts/Superscript.tsx';
|
import { Superscript } from '@/base/components/texts/Superscript.tsx';
|
||||||
|
|
||||||
export const ButtonSelect = <Value extends string | number>({
|
export const ButtonSelect = <Value extends string | number, MultiValue extends Value | Value[] = Value>({
|
||||||
value,
|
value,
|
||||||
values,
|
values,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
@@ -21,7 +21,7 @@ export const ButtonSelect = <Value extends string | number>({
|
|||||||
valueToDisplayData,
|
valueToDisplayData,
|
||||||
isDefaultable,
|
isDefaultable,
|
||||||
onDefault,
|
onDefault,
|
||||||
}: MultiValueButtonProps<Value>) => {
|
}: MultiValueButtonProps<Value, MultiValue>) => {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -33,16 +33,31 @@ export const ButtonSelect = <Value extends string | number>({
|
|||||||
)}
|
)}
|
||||||
{values.map((displayValue) => {
|
{values.map((displayValue) => {
|
||||||
const isDefault = value === undefined && displayValue === defaultValue;
|
const isDefault = value === undefined && displayValue === defaultValue;
|
||||||
|
const isMultiSelect = Array.isArray(value);
|
||||||
|
const isSelected = isMultiSelect ? value.includes(displayValue) : displayValue === value;
|
||||||
|
|
||||||
const text = valueToDisplayData[displayValue].isTitleString
|
const newValue = (() => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return [displayValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMultiSelect) {
|
||||||
|
return isSelected ? value.filter((v) => v !== displayValue) : [...value, displayValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
return displayValue;
|
||||||
|
})() as MultiValue;
|
||||||
|
|
||||||
|
const text =
|
||||||
|
typeof valueToDisplayData[displayValue].title === 'string'
|
||||||
? valueToDisplayData[displayValue].title
|
? valueToDisplayData[displayValue].title
|
||||||
: t(valueToDisplayData[displayValue].title);
|
: t(valueToDisplayData[displayValue].title);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomTooltip key={displayValue} title={isDefault ? t`Active setting` : ''}>
|
<CustomTooltip key={displayValue} title={isDefault ? t`Active setting` : ''}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setValue(displayValue)}
|
onClick={() => setValue(newValue)}
|
||||||
variant={displayValue === value ? 'contained' : 'outlined'}
|
variant={isSelected ? 'contained' : 'outlined'}
|
||||||
startIcon={valueToDisplayData[displayValue].icon}
|
startIcon={valueToDisplayData[displayValue].icon}
|
||||||
>
|
>
|
||||||
{isDefault ? <Superscript superscript="*" text={text} /> : text}
|
{isDefault ? <Superscript superscript="*" text={text} /> : text}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const ValueRotationButton = <Value extends string | number>({
|
|||||||
<Superscript
|
<Superscript
|
||||||
superscript={`(${t`Default`})`}
|
superscript={`(${t`Default`})`}
|
||||||
text={
|
text={
|
||||||
valueToDisplayData[defaultValue].isTitleString
|
typeof valueToDisplayData[defaultValue].title === 'string'
|
||||||
? valueToDisplayData[defaultValue].title
|
? valueToDisplayData[defaultValue].title
|
||||||
: t(valueToDisplayData[defaultValue].title)
|
: t(valueToDisplayData[defaultValue].title)
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ export const ValueRotationButton = <Value extends string | number>({
|
|||||||
startIcon={valueToDisplayData[value].icon}
|
startIcon={valueToDisplayData[value].icon}
|
||||||
size="large"
|
size="large"
|
||||||
>
|
>
|
||||||
{valueToDisplayData[value].isTitleString
|
{typeof valueToDisplayData[value].title === 'string'
|
||||||
? valueToDisplayData[value].title
|
? valueToDisplayData[value].title
|
||||||
: t(valueToDisplayData[value].title)}
|
: t(valueToDisplayData[value].title)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import type { ComponentProps } from 'react';
|
|||||||
|
|
||||||
import { ButtonSelect } from '@/base/components/buttons/ButtonSelect.tsx';
|
import { ButtonSelect } from '@/base/components/buttons/ButtonSelect.tsx';
|
||||||
|
|
||||||
export const ButtonSelectInput = <Value extends string | number>({
|
export const ButtonSelectInput = <Value extends string | number, MultiValue extends Value | Value[] = Value>({
|
||||||
label,
|
label,
|
||||||
description,
|
description,
|
||||||
...buttonSelectProps
|
...buttonSelectProps
|
||||||
}: ComponentProps<typeof ButtonSelect<Value>> & { label: string; description?: string }) => (
|
}: ComponentProps<typeof ButtonSelect<Value, MultiValue>> & { label: string; description?: string }) => (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Typography>{label}</Typography>
|
<Typography>{label}</Typography>
|
||||||
{description && (
|
{description && (
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import deepmerge from '@mui/utils/deepmerge';
|
import deepmerge from '@mui/utils/deepmerge';
|
||||||
import type { AppMetadataKeys, IMetadataMigration } from '@/features/metadata/Metadata.types.ts';
|
import type { AppMetadataKeys, IMetadataMigration } from '@/features/metadata/Metadata.types.ts';
|
||||||
import type { IReaderSettings, ReaderCustomFilter } from '@/features/reader/Reader.types.ts';
|
import type { IReaderSettings, ReaderCustomFilter, SafeAreaInset } from '@/features/reader/Reader.types.ts';
|
||||||
import { ProgressBarPosition, ReaderPageScaleMode, ReadingMode } from '@/features/reader/Reader.types.ts';
|
import { ProgressBarPosition, ReaderPageScaleMode, ReadingMode } from '@/features/reader/Reader.types.ts';
|
||||||
import {
|
import {
|
||||||
AUTO_SCROLL_SPEED,
|
AUTO_SCROLL_SPEED,
|
||||||
@@ -394,6 +394,9 @@ export const APP_METADATA: Record<
|
|||||||
return locale;
|
return locale;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
safeAreaInset: {
|
||||||
|
convert: convertToObject<SafeAreaInset>,
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA);
|
export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA);
|
||||||
|
|||||||
@@ -136,6 +136,13 @@ export enum ReaderScrollAmount {
|
|||||||
LARGE = 95,
|
LARGE = 95,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SafeAreaInset {
|
||||||
|
top: boolean;
|
||||||
|
right: boolean;
|
||||||
|
bottom: boolean;
|
||||||
|
left: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IReaderSettingsGlobal {
|
export interface IReaderSettingsGlobal {
|
||||||
overlayMode: ReaderOverlayMode;
|
overlayMode: ReaderOverlayMode;
|
||||||
exitMode: ReaderExitMode;
|
exitMode: ReaderExitMode;
|
||||||
@@ -169,6 +176,7 @@ export interface IReaderSettingsGlobal {
|
|||||||
scrollAmount: ReaderScrollAmount;
|
scrollAmount: ReaderScrollAmount;
|
||||||
shouldUseInfiniteScroll: boolean;
|
shouldUseInfiniteScroll: boolean;
|
||||||
shouldShowTransitionPage: boolean;
|
shouldShowTransitionPage: boolean;
|
||||||
|
safeAreaInset: SafeAreaInset;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IReaderSettingsManga {
|
export interface IReaderSettingsManga {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const BaseReaderPageNumber = ({
|
|||||||
shouldShowPageNumber: state.shouldShowPageNumber,
|
shouldShowPageNumber: state.shouldShowPageNumber,
|
||||||
progressBarType: state.progressBarType,
|
progressBarType: state.progressBarType,
|
||||||
}));
|
}));
|
||||||
|
const safeAreaInset = useReaderSettingsStore((state) => state.safeAreaInset);
|
||||||
const isMaximized = useReaderProgressBarStore('isMaximized');
|
const isMaximized = useReaderProgressBarStore('isMaximized');
|
||||||
|
|
||||||
const pageName = useMemo(() => {
|
const pageName = useMemo(() => {
|
||||||
@@ -68,7 +69,8 @@ const BaseReaderPageNumber = ({
|
|||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
left: readerNavBarWidth,
|
left: readerNavBarWidth,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: (theme) => `calc(${theme.spacing(1)} + max(${scrollbar.xSize}px, env(safe-area-inset-bottom)))`,
|
bottom: (theme) =>
|
||||||
|
`calc(${theme.spacing(1)} + max(${scrollbar.xSize}px, ${safeAreaInset.bottom ? 'env(safe-area-inset-bottom)' : '0px'}))`,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
transition: (theme) => `left 0.${theme.transitions.duration.shortest}s`,
|
transition: (theme) => `left 0.${theme.transitions.duration.shortest}s`,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ const BaseReader = ({
|
|||||||
'tapZoneInvertMode',
|
'tapZoneInvertMode',
|
||||||
'shouldShowReadingModePreview',
|
'shouldShowReadingModePreview',
|
||||||
'shouldShowTapZoneLayoutPreview',
|
'shouldShowTapZoneLayoutPreview',
|
||||||
|
'safeAreaInset',
|
||||||
);
|
);
|
||||||
|
const safeAreaInset = useReaderSettingsStore((state) => state.safeAreaInset);
|
||||||
|
|
||||||
const scrollElementRef = useRef<HTMLDivElement | null>(null);
|
const scrollElementRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
@@ -228,10 +230,10 @@ const BaseReader = ({
|
|||||||
maxWidth: `calc(100vw - ${readerNavBarWidth}px)`,
|
maxWidth: `calc(100vw - ${readerNavBarWidth}px)`,
|
||||||
width: `calc(100vw - ${readerNavBarWidth}px)`,
|
width: `calc(100vw - ${readerNavBarWidth}px)`,
|
||||||
height: `100vh`,
|
height: `100vh`,
|
||||||
pt: 'env(safe-area-inset-top)',
|
pt: safeAreaInset.top ? 'env(safe-area-inset-top)' : undefined,
|
||||||
pb: 'env(safe-area-inset-bottom)',
|
pb: safeAreaInset.bottom ? 'env(safe-area-inset-bottom)' : undefined,
|
||||||
pr: 'env(safe-area-inset-right)',
|
pr: safeAreaInset.right ? 'env(safe-area-inset-right)' : undefined,
|
||||||
pl: 'env(safe-area-inset-left)',
|
pl: safeAreaInset.left ? 'env(safe-area-inset-left)' : undefined,
|
||||||
marginLeft: `${readerNavBarWidth}px`,
|
marginLeft: `${readerNavBarWidth}px`,
|
||||||
transition: (theme) =>
|
transition: (theme) =>
|
||||||
`width 0.${theme.transitions.duration.shortest}s, margin-left 0.${theme.transitions.duration.shortest}s`,
|
`width 0.${theme.transitions.duration.shortest}s, margin-left 0.${theme.transitions.duration.shortest}s`,
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ const GLOBAL_READER_SETTING_OBJECT: Record<keyof IReaderSettingsGlobal, undefine
|
|||||||
scrollAmount: undefined,
|
scrollAmount: undefined,
|
||||||
shouldUseInfiniteScroll: undefined,
|
shouldUseInfiniteScroll: undefined,
|
||||||
shouldShowTransitionPage: undefined,
|
shouldShowTransitionPage: undefined,
|
||||||
|
safeAreaInset: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GLOBAL_READER_SETTING_KEYS = Object.keys(GLOBAL_READER_SETTING_OBJECT);
|
export const GLOBAL_READER_SETTING_KEYS = Object.keys(GLOBAL_READER_SETTING_OBJECT);
|
||||||
@@ -242,6 +243,12 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = {
|
|||||||
scrollAmount: ReaderScrollAmount.LARGE,
|
scrollAmount: ReaderScrollAmount.LARGE,
|
||||||
shouldUseInfiniteScroll: true,
|
shouldUseInfiniteScroll: true,
|
||||||
shouldShowTransitionPage: true,
|
shouldShowTransitionPage: true,
|
||||||
|
safeAreaInset: {
|
||||||
|
top: true,
|
||||||
|
right: true,
|
||||||
|
bottom: true,
|
||||||
|
left: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const READER_PROGRESS_BAR_POSITION_TO_PLACEMENT: Record<ProgressBarPosition, TooltipProps['placement']> = {
|
export const READER_PROGRESS_BAR_POSITION_TO_PLACEMENT: Record<ProgressBarPosition, TooltipProps['placement']> = {
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ const convertSettingsToMetadata = (
|
|||||||
readerWidth: JSON.stringify(settings.readerWidth),
|
readerWidth: JSON.stringify(settings.readerWidth),
|
||||||
hotkeys: JSON.stringify(settings.hotkeys),
|
hotkeys: JSON.stringify(settings.hotkeys),
|
||||||
autoScroll: JSON.stringify(settings.autoScroll),
|
autoScroll: JSON.stringify(settings.autoScroll),
|
||||||
|
safeAreaInset: JSON.stringify(settings.autoScroll),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG = convertToSettingsWithDefaultFlag(
|
export const DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG = convertToSettingsWithDefaultFlag(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { ProgressBarType, ReaderOverlayMode } from '@/features/reader/Reader.typ
|
|||||||
import { ReaderSettingOverlayMode } from '@/features/reader/overlay/settings/ReaderSettingOverlayMode.tsx';
|
import { ReaderSettingOverlayMode } from '@/features/reader/overlay/settings/ReaderSettingOverlayMode.tsx';
|
||||||
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
|
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
|
||||||
import { ReaderSettingBackgroundColor } from '@/features/reader/settings/general/components/ReaderSettingBackgroundColor.tsx';
|
import { ReaderSettingBackgroundColor } from '@/features/reader/settings/general/components/ReaderSettingBackgroundColor.tsx';
|
||||||
|
import { ReaderSettingSafeAreaInset } from '@/features/reader/settings/general/components/ReaderSettingSafeAreaInset.tsx';
|
||||||
|
|
||||||
export const ReaderGeneralSettings = ({
|
export const ReaderGeneralSettings = ({
|
||||||
overlayMode,
|
overlayMode,
|
||||||
@@ -59,6 +60,10 @@ export const ReaderGeneralSettings = ({
|
|||||||
onChange={(_, checked) => updateSetting('shouldShowPageNumber', checked)}
|
onChange={(_, checked) => updateSetting('shouldShowPageNumber', checked)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<ReaderSettingSafeAreaInset
|
||||||
|
safeAreaInset={settings.safeAreaInset}
|
||||||
|
updateSetting={(value) => updateSetting('safeAreaInset', value)}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useLingui } from '@lingui/react/macro';
|
||||||
|
import { msg } from '@lingui/core/macro';
|
||||||
|
import type { ValueToDisplayData } from '@/base/Base.types.ts';
|
||||||
|
import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.tsx';
|
||||||
|
import type { IReaderSettingsWithDefaultFlag, SafeAreaInset } from '@/features/reader/Reader.types.ts';
|
||||||
|
import { DEFAULT_READER_SETTINGS } from '@/features/reader/settings/ReaderSettings.constants.tsx';
|
||||||
|
|
||||||
|
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<keyof SafeAreaInset> = {
|
||||||
|
top: {
|
||||||
|
title: msg`Top`,
|
||||||
|
icon: null,
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
title: msg`Right`,
|
||||||
|
icon: null,
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
title: msg`Bottom`,
|
||||||
|
icon: null,
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
title: msg`Left`,
|
||||||
|
icon: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const READER_SAFE_AREA_INSET_VALUES = Object.keys(DEFAULT_READER_SETTINGS.safeAreaInset) as (keyof SafeAreaInset)[];
|
||||||
|
|
||||||
|
export const ReaderSettingSafeAreaInset = ({
|
||||||
|
safeAreaInset,
|
||||||
|
updateSetting,
|
||||||
|
}: Pick<IReaderSettingsWithDefaultFlag, 'safeAreaInset'> & {
|
||||||
|
updateSetting: (safeAreaInset: SafeAreaInset) => void;
|
||||||
|
}) => {
|
||||||
|
const { t } = useLingui();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ButtonSelectInput
|
||||||
|
label={t`Apply safe area padding`}
|
||||||
|
description={t`Prevents content from overlapping device cutouts and screen edges`}
|
||||||
|
value={Object.entries(safeAreaInset)
|
||||||
|
.filter(([, enabled]) => enabled)
|
||||||
|
.map(([key]) => key as keyof SafeAreaInset)}
|
||||||
|
values={READER_SAFE_AREA_INSET_VALUES}
|
||||||
|
setValue={(values) =>
|
||||||
|
updateSetting(
|
||||||
|
Object.fromEntries(
|
||||||
|
READER_SAFE_AREA_INSET_VALUES.map((key) => [key, values.includes(key)]),
|
||||||
|
) as unknown as SafeAreaInset,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -93,7 +93,7 @@ export const ReaderSettingTapZoneInvertMode = ({
|
|||||||
const tapZonesInvertOption = convertTapZoneInvertModeToOption(tapZoneInvertMode.value);
|
const tapZonesInvertOption = convertTapZoneInvertModeToOption(tapZoneInvertMode.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonSelectInput
|
<ButtonSelectInput<TapZonesInvertOption>
|
||||||
{...buttonSelectInputProps}
|
{...buttonSelectInputProps}
|
||||||
label={t`Invert tap zones`}
|
label={t`Invert tap zones`}
|
||||||
value={tapZoneInvertMode.isDefault ? undefined : tapZonesInvertOption}
|
value={tapZoneInvertMode.isDefault ? undefined : tapZonesInvertOption}
|
||||||
|
|||||||
@@ -414,6 +414,10 @@ msgstr "Also remove from {0}"
|
|||||||
msgid "Appearance"
|
msgid "Appearance"
|
||||||
msgstr "Appearance"
|
msgstr "Appearance"
|
||||||
|
|
||||||
|
#: src/features/reader/settings/general/components/ReaderSettingSafeAreaInset.tsx
|
||||||
|
msgid "Apply safe area padding"
|
||||||
|
msgstr "Apply safe area padding"
|
||||||
|
|
||||||
#: src/features/migration/MigrationManager.ts
|
#: src/features/migration/MigrationManager.ts
|
||||||
msgid "Are you sure you want to abort the migration?"
|
msgid "Are you sure you want to abort the migration?"
|
||||||
msgstr "Are you sure you want to abort the migration?"
|
msgstr "Are you sure you want to abort the migration?"
|
||||||
@@ -587,6 +591,7 @@ msgid "Both"
|
|||||||
msgstr "Both"
|
msgstr "Both"
|
||||||
|
|
||||||
#: src/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarPosition.tsx
|
#: src/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarPosition.tsx
|
||||||
|
#: src/features/reader/settings/general/components/ReaderSettingSafeAreaInset.tsx
|
||||||
msgid "Bottom"
|
msgid "Bottom"
|
||||||
msgstr "Bottom"
|
msgstr "Bottom"
|
||||||
|
|
||||||
@@ -2052,6 +2057,7 @@ msgid "Layout"
|
|||||||
msgstr "Layout"
|
msgstr "Layout"
|
||||||
|
|
||||||
#: src/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarPosition.tsx
|
#: src/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarPosition.tsx
|
||||||
|
#: src/features/reader/settings/general/components/ReaderSettingSafeAreaInset.tsx
|
||||||
msgid "Left"
|
msgid "Left"
|
||||||
msgstr "Left"
|
msgstr "Left"
|
||||||
|
|
||||||
@@ -2623,6 +2629,10 @@ msgstr "Preload images"
|
|||||||
msgid "Press keys"
|
msgid "Press keys"
|
||||||
msgstr "Press keys"
|
msgstr "Press keys"
|
||||||
|
|
||||||
|
#: src/features/reader/settings/general/components/ReaderSettingSafeAreaInset.tsx
|
||||||
|
msgid "Prevents content from overlapping device cutouts and screen edges"
|
||||||
|
msgstr "Prevents content from overlapping device cutouts and screen edges"
|
||||||
|
|
||||||
#: src/features/settings/Settings.constants.ts
|
#: src/features/settings/Settings.constants.ts
|
||||||
msgid "Preview"
|
msgid "Preview"
|
||||||
msgstr "Preview"
|
msgstr "Preview"
|
||||||
@@ -2830,6 +2840,7 @@ msgid "Retry errored pages"
|
|||||||
msgstr "Retry errored pages"
|
msgstr "Retry errored pages"
|
||||||
|
|
||||||
#: src/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarPosition.tsx
|
#: src/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarPosition.tsx
|
||||||
|
#: src/features/reader/settings/general/components/ReaderSettingSafeAreaInset.tsx
|
||||||
msgid "Right"
|
msgid "Right"
|
||||||
msgstr "Right"
|
msgstr "Right"
|
||||||
|
|
||||||
@@ -3508,6 +3519,10 @@ msgstr "Toggle offset double spreads"
|
|||||||
msgid "Toggle stretch image"
|
msgid "Toggle stretch image"
|
||||||
msgstr "Toggle stretch image"
|
msgstr "Toggle stretch image"
|
||||||
|
|
||||||
|
#: src/features/reader/settings/general/components/ReaderSettingSafeAreaInset.tsx
|
||||||
|
msgid "Top"
|
||||||
|
msgstr "Top"
|
||||||
|
|
||||||
#: src/features/library/components/LibraryOptionsPanel.tsx
|
#: src/features/library/components/LibraryOptionsPanel.tsx
|
||||||
msgid "Total chapters"
|
msgid "Total chapters"
|
||||||
msgstr "Total chapters"
|
msgstr "Total chapters"
|
||||||
|
|||||||
Reference in New Issue
Block a user