Ensure stored client metadata values respect constraints
This commit is contained in:
14
src/modules/downloads/Downloads.constants.ts
Normal file
14
src/modules/downloads/Downloads.constants.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
export const DOWNLOAD_AHEAD = {
|
||||
min: 2,
|
||||
max: 10,
|
||||
default: 2,
|
||||
step: 1,
|
||||
};
|
||||
@@ -18,10 +18,7 @@ import { makeToast } from '@/modules/core/utils/Toast.ts';
|
||||
import { MetadataDownloadSettings } from '@/modules/downloads/Downloads.types.ts';
|
||||
import { MetadataServerSettings } from '@/modules/settings/Settings.types.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
|
||||
const MIN_LIMIT = 2;
|
||||
const MAX_LIMIT = 10;
|
||||
const DEFAULT_LIMIT = MIN_LIMIT;
|
||||
import { DOWNLOAD_AHEAD } from '@/modules/downloads/Downloads.constants.ts';
|
||||
|
||||
export const DownloadAheadSetting = ({
|
||||
downloadAheadLimit,
|
||||
@@ -33,7 +30,7 @@ export const DownloadAheadSetting = ({
|
||||
const shouldDownloadAhead = !!downloadAheadLimit;
|
||||
const [currentDownloadAheadLimit, persistDownloadAheadLimit] = usePersistedValue(
|
||||
'lastDownloadAheadLimit',
|
||||
DEFAULT_LIMIT,
|
||||
DOWNLOAD_AHEAD.default,
|
||||
downloadAheadLimit,
|
||||
getPersistedServerSetting,
|
||||
);
|
||||
@@ -63,9 +60,10 @@ export const DownloadAheadSetting = ({
|
||||
count: currentDownloadAheadLimit,
|
||||
})}
|
||||
value={currentDownloadAheadLimit}
|
||||
minValue={MIN_LIMIT}
|
||||
maxValue={MAX_LIMIT}
|
||||
defaultValue={DEFAULT_LIMIT}
|
||||
minValue={DOWNLOAD_AHEAD.min}
|
||||
maxValue={DOWNLOAD_AHEAD.max}
|
||||
defaultValue={DOWNLOAD_AHEAD.default}
|
||||
stepSize={DOWNLOAD_AHEAD.step}
|
||||
showSlider
|
||||
dialogDescription={t('download.settings.download_ahead.label.description')}
|
||||
dialogDisclaimer={t('download.settings.download_ahead.label.disclaimer')}
|
||||
|
||||
@@ -7,103 +7,337 @@
|
||||
*/
|
||||
|
||||
import { AppMetadataKeys, IMetadataMigration } from '@/modules/metadata/Metadata.types.ts';
|
||||
import { ProgressBarPosition, ReaderPageScaleMode, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import {
|
||||
IReaderSettings,
|
||||
ProgressBarPosition,
|
||||
ReaderCustomFilter,
|
||||
ReaderPageScaleMode,
|
||||
ReadingMode,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
import {
|
||||
AUTO_SCROLL_SPEED,
|
||||
CUSTOM_FILTER,
|
||||
DEFAULT_READER_SETTINGS,
|
||||
IMAGE_PRE_LOAD_AMOUNT,
|
||||
PAGE_GAP,
|
||||
PROGRESS_BAR_SIZE,
|
||||
SCROLL_AMOUNT,
|
||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { coerceIn } from '@/lib/HelperFunctions.ts';
|
||||
import { DOWNLOAD_AHEAD } from '@/modules/downloads/Downloads.constants.ts';
|
||||
import { MANGA_GRID_WIDTH } from '@/modules/settings/Settings.constants.ts';
|
||||
|
||||
export const APP_METADATA_KEY_PREFIX = 'webUI';
|
||||
|
||||
// At the moment any non-primitive types need to be specified as "string" and handled in the according "MetadataService".
|
||||
// "auto" can be used to try to automatically convert the value to a specific type (string, number, boolean, undefined, null)
|
||||
export const APP_METADATA_KEY_TO_TYPE = {
|
||||
migration: 'number',
|
||||
deleteChaptersManuallyMarkedRead: 'boolean',
|
||||
deleteChaptersWhileReading: 'number',
|
||||
deleteChaptersWithBookmark: 'boolean',
|
||||
downloadAheadLimit: 'number',
|
||||
showAddToLibraryCategorySelectDialog: 'boolean',
|
||||
ignoreFilters: 'boolean',
|
||||
removeMangaFromCategories: 'boolean',
|
||||
showTabSize: 'boolean',
|
||||
devices: 'string', // string[]
|
||||
migrateChapters: 'boolean',
|
||||
migrateCategories: 'boolean',
|
||||
migrateTracking: 'boolean',
|
||||
deleteChapters: 'boolean',
|
||||
migrateSortSettings: 'string', // SortSettings (object)
|
||||
hideLibraryEntries: 'boolean',
|
||||
updateProgressAfterReading: 'boolean',
|
||||
updateProgressManualMarkRead: 'boolean',
|
||||
webUIInformAvailableUpdate: 'boolean',
|
||||
serverInformAvailableUpdate: 'boolean',
|
||||
readerWidth: 'string', // object
|
||||
savedSearches: 'string', // object
|
||||
showContinueReadingButton: 'boolean',
|
||||
showDownloadBadge: 'boolean',
|
||||
showUnreadBadge: 'boolean',
|
||||
gridLayout: 'number', // GridLayout (enum)
|
||||
sortBy: 'auto', // LibrarySortMode undefined null
|
||||
sortDesc: 'auto', // boolean undefined null
|
||||
hasDownloadedChapters: 'auto', // boolean undefined null
|
||||
hasBookmarkedChapters: 'auto', // boolean undefined null
|
||||
hasUnreadChapters: 'auto', // boolean undefined null
|
||||
hasReadChapters: 'auto', // boolean undefined null
|
||||
hasDuplicateChapters: 'auto', // boolean undefined null
|
||||
hasTrackerBinding: 'string', // object
|
||||
hasStatus: 'string', // object
|
||||
customThemes: 'string', // object
|
||||
mangaThumbnailBackdrop: 'boolean',
|
||||
mangaDynamicColorSchemes: 'boolean',
|
||||
tapZoneLayout: 'number', // TapZoneLayouts (enum)
|
||||
tapZoneInvertMode: 'string', // TapZoneInvertMode (object)
|
||||
readingDirection: 'number', // ReadingDirection (enum)
|
||||
progressBarType: 'number', // ProgressBarType (enum)
|
||||
progressBarSize: 'number',
|
||||
progressBarPosition: 'number', // ProgressBarPosition (enum)
|
||||
progressBarPositionAutoVertical: 'number', // TProgressBarPositionAutoVertical (enum)
|
||||
readingMode: 'number', // ReadingMode (enum)
|
||||
pageScaleMode: 'number', // ReaderPageScaleMode (enum)
|
||||
shouldOffsetDoubleSpreads: 'boolean',
|
||||
exitMode: 'number', // ReaderExitMode (enum)
|
||||
customFilter: 'string', // ReaderCustomFilter (object)
|
||||
shouldSkipDupChapters: 'boolean',
|
||||
isStaticNav: 'boolean',
|
||||
overlayMode: 'number', // ReaderOverlayMode (enum)
|
||||
shouldStretchPage: 'boolean',
|
||||
shouldShowPageNumber: 'boolean',
|
||||
backgroundColor: 'number', // ReaderBackgroundColor (enum)
|
||||
pageGap: 'number',
|
||||
hotkeys: 'string', // object
|
||||
imagePreLoadAmount: 'number',
|
||||
shouldUseAutoWebtoonMode: 'boolean',
|
||||
autoScroll: 'string', // object
|
||||
shouldShowReadingModePreview: 'boolean',
|
||||
shouldShowTapZoneLayoutPreview: 'boolean',
|
||||
shouldInformAboutMissingChapter: 'boolean',
|
||||
shouldInformAboutScanlatorChange: 'boolean',
|
||||
hideHistory: 'boolean',
|
||||
scrollAmount: 'number', // ReaderScrollAmount (enum)
|
||||
reverse: 'boolean',
|
||||
bookmarked: 'auto', // boolean undefined null
|
||||
downloaded: 'auto', // boolean undefined null
|
||||
unread: 'auto', // boolean undefined null
|
||||
showChapterNumber: 'boolean',
|
||||
extensionLanguages: 'string', // string[]
|
||||
sourceLanguages: 'string', // string[]
|
||||
showNsfw: 'boolean',
|
||||
shouldUseInfiniteScroll: 'boolean',
|
||||
shouldShowTransitionPage: 'boolean',
|
||||
appTheme: 'string',
|
||||
themeMode: 'string', // ThemeMode (enum)
|
||||
shouldUsePureBlackMode: 'boolean',
|
||||
mangaGridItemWidth: 'number',
|
||||
isPinned: 'boolean',
|
||||
isEnabled: 'boolean',
|
||||
lastUsedSourceId: 'string',
|
||||
shouldShowOnlySourcesWithResults: 'boolean',
|
||||
excludedScanlators: 'string', // string[]
|
||||
} as const satisfies Record<AppMetadataKeys, 'auto' | 'string' | 'number' | 'boolean'>;
|
||||
export const APP_METADATA: Record<
|
||||
AppMetadataKeys,
|
||||
{
|
||||
type: 'auto' | 'string' | 'number' | 'boolean';
|
||||
toValidValue?: (value: any) => any;
|
||||
}
|
||||
> = {
|
||||
migration: {
|
||||
type: 'number',
|
||||
},
|
||||
deleteChaptersManuallyMarkedRead: {
|
||||
type: 'boolean',
|
||||
},
|
||||
deleteChaptersWhileReading: {
|
||||
type: 'number',
|
||||
},
|
||||
deleteChaptersWithBookmark: {
|
||||
type: 'boolean',
|
||||
},
|
||||
downloadAheadLimit: {
|
||||
type: 'number',
|
||||
toValidValue: (value: number) => coerceIn(value, DOWNLOAD_AHEAD.min, DOWNLOAD_AHEAD.max),
|
||||
},
|
||||
showAddToLibraryCategorySelectDialog: {
|
||||
type: 'boolean',
|
||||
},
|
||||
ignoreFilters: {
|
||||
type: 'boolean',
|
||||
},
|
||||
removeMangaFromCategories: {
|
||||
type: 'boolean',
|
||||
},
|
||||
showTabSize: {
|
||||
type: 'boolean',
|
||||
},
|
||||
devices: {
|
||||
type: 'string', // string[]
|
||||
},
|
||||
migrateChapters: {
|
||||
type: 'boolean',
|
||||
},
|
||||
migrateCategories: {
|
||||
type: 'boolean',
|
||||
},
|
||||
migrateTracking: {
|
||||
type: 'boolean',
|
||||
},
|
||||
deleteChapters: {
|
||||
type: 'boolean',
|
||||
},
|
||||
migrateSortSettings: {
|
||||
type: 'string', // SortSettings (object)
|
||||
},
|
||||
hideLibraryEntries: {
|
||||
type: 'boolean',
|
||||
},
|
||||
updateProgressAfterReading: {
|
||||
type: 'boolean',
|
||||
},
|
||||
updateProgressManualMarkRead: {
|
||||
type: 'boolean',
|
||||
},
|
||||
webUIInformAvailableUpdate: {
|
||||
type: 'boolean',
|
||||
},
|
||||
serverInformAvailableUpdate: {
|
||||
type: 'boolean',
|
||||
},
|
||||
readerWidth: {
|
||||
type: 'string', // object
|
||||
},
|
||||
savedSearches: {
|
||||
type: 'string', // object
|
||||
},
|
||||
showContinueReadingButton: {
|
||||
type: 'boolean',
|
||||
},
|
||||
showDownloadBadge: {
|
||||
type: 'boolean',
|
||||
},
|
||||
showUnreadBadge: {
|
||||
type: 'boolean',
|
||||
},
|
||||
gridLayout: {
|
||||
type: 'number', // GridLayout (enum)
|
||||
},
|
||||
sortBy: {
|
||||
type: 'auto', // LibrarySortMode undefined null
|
||||
},
|
||||
sortDesc: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
hasDownloadedChapters: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
hasBookmarkedChapters: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
hasUnreadChapters: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
hasReadChapters: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
hasDuplicateChapters: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
hasTrackerBinding: {
|
||||
type: 'string', // object
|
||||
},
|
||||
hasStatus: {
|
||||
type: 'string', // object
|
||||
},
|
||||
customThemes: {
|
||||
type: 'string', // object
|
||||
},
|
||||
mangaThumbnailBackdrop: {
|
||||
type: 'boolean',
|
||||
},
|
||||
mangaDynamicColorSchemes: {
|
||||
type: 'boolean',
|
||||
},
|
||||
tapZoneLayout: {
|
||||
type: 'number', // TapZoneLayouts (enum)
|
||||
},
|
||||
tapZoneInvertMode: {
|
||||
type: 'string', // TapZoneInvertMode (object)
|
||||
},
|
||||
readingDirection: {
|
||||
type: 'number', // ReadingDirection (enum)
|
||||
},
|
||||
progressBarType: {
|
||||
type: 'number', // ProgressBarType (enum)
|
||||
},
|
||||
progressBarSize: {
|
||||
type: 'number',
|
||||
toValidValue: (value: number) => coerceIn(value, PROGRESS_BAR_SIZE.min, PROGRESS_BAR_SIZE.max),
|
||||
},
|
||||
progressBarPosition: {
|
||||
type: 'number', // ProgressBarPosition (enum)
|
||||
},
|
||||
progressBarPositionAutoVertical: {
|
||||
type: 'number', // TProgressBarPositionAutoVertical (enum)
|
||||
},
|
||||
readingMode: {
|
||||
type: 'number', // ReadingMode (enum)
|
||||
},
|
||||
pageScaleMode: {
|
||||
type: 'number', // ReaderPageScaleMode (enum)
|
||||
},
|
||||
shouldOffsetDoubleSpreads: {
|
||||
type: 'boolean',
|
||||
},
|
||||
exitMode: {
|
||||
type: 'number', // ReaderExitMode (enum)
|
||||
},
|
||||
customFilter: {
|
||||
type: 'string', // ReaderCustomFilter (object)
|
||||
toValidValue: (value: ReaderCustomFilter): ReaderCustomFilter => ({
|
||||
...value,
|
||||
brightness: {
|
||||
...value.brightness,
|
||||
value: coerceIn(value.brightness.value, CUSTOM_FILTER.brightness.min, CUSTOM_FILTER.brightness.max),
|
||||
},
|
||||
contrast: {
|
||||
...value.contrast,
|
||||
value: coerceIn(value.contrast.value, CUSTOM_FILTER.contrast.min, CUSTOM_FILTER.contrast.max),
|
||||
},
|
||||
saturate: {
|
||||
...value.saturate,
|
||||
value: coerceIn(value.saturate.value, CUSTOM_FILTER.saturate.min, CUSTOM_FILTER.saturate.max),
|
||||
},
|
||||
hue: {
|
||||
...value.hue,
|
||||
value: coerceIn(value.hue.value, CUSTOM_FILTER.hue.min, CUSTOM_FILTER.hue.max),
|
||||
},
|
||||
rgba: {
|
||||
...value.rgba,
|
||||
value: {
|
||||
...value.rgba.value,
|
||||
red: coerceIn(value.rgba.value.red, CUSTOM_FILTER.rgba.red.min, CUSTOM_FILTER.rgba.red.max),
|
||||
green: coerceIn(value.rgba.value.green, CUSTOM_FILTER.rgba.green.min, CUSTOM_FILTER.rgba.green.max),
|
||||
blue: coerceIn(value.rgba.value.blue, CUSTOM_FILTER.rgba.blue.min, CUSTOM_FILTER.rgba.blue.max),
|
||||
alpha: coerceIn(value.rgba.value.alpha, CUSTOM_FILTER.rgba.alpha.min, CUSTOM_FILTER.rgba.alpha.max),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
shouldSkipDupChapters: {
|
||||
type: 'boolean',
|
||||
},
|
||||
isStaticNav: {
|
||||
type: 'boolean',
|
||||
},
|
||||
overlayMode: {
|
||||
type: 'number', // ReaderOverlayMode (enum)
|
||||
},
|
||||
shouldStretchPage: {
|
||||
type: 'boolean',
|
||||
},
|
||||
shouldShowPageNumber: {
|
||||
type: 'boolean',
|
||||
},
|
||||
backgroundColor: {
|
||||
type: 'number', // ReaderBackgroundColor (enum)
|
||||
},
|
||||
pageGap: {
|
||||
type: 'number',
|
||||
toValidValue: (value: number) => coerceIn(value, PAGE_GAP.min, PAGE_GAP.max),
|
||||
},
|
||||
hotkeys: {
|
||||
type: 'string', // object
|
||||
},
|
||||
imagePreLoadAmount: {
|
||||
type: 'number',
|
||||
toValidValue: (value: number) => coerceIn(value, IMAGE_PRE_LOAD_AMOUNT.min, IMAGE_PRE_LOAD_AMOUNT.max),
|
||||
},
|
||||
shouldUseAutoWebtoonMode: {
|
||||
type: 'boolean',
|
||||
},
|
||||
autoScroll: {
|
||||
type: 'string', // object
|
||||
toValidValue: (value: IReaderSettings['autoScroll']): IReaderSettings['autoScroll'] => ({
|
||||
...value,
|
||||
value: coerceIn(value.value, AUTO_SCROLL_SPEED.min, AUTO_SCROLL_SPEED.max),
|
||||
}),
|
||||
},
|
||||
shouldShowReadingModePreview: {
|
||||
type: 'boolean',
|
||||
},
|
||||
shouldShowTapZoneLayoutPreview: {
|
||||
type: 'boolean',
|
||||
},
|
||||
shouldInformAboutMissingChapter: {
|
||||
type: 'boolean',
|
||||
},
|
||||
shouldInformAboutScanlatorChange: {
|
||||
type: 'boolean',
|
||||
},
|
||||
hideHistory: {
|
||||
type: 'boolean',
|
||||
},
|
||||
scrollAmount: {
|
||||
type: 'number', // ReaderScrollAmount (enum)
|
||||
toValidValue: (value: number) => coerceIn(value, SCROLL_AMOUNT.min, SCROLL_AMOUNT.max),
|
||||
},
|
||||
reverse: {
|
||||
type: 'boolean',
|
||||
},
|
||||
bookmarked: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
downloaded: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
unread: {
|
||||
type: 'auto', // boolean undefined null
|
||||
},
|
||||
showChapterNumber: {
|
||||
type: 'boolean',
|
||||
},
|
||||
extensionLanguages: {
|
||||
type: 'string', // string[]
|
||||
},
|
||||
sourceLanguages: {
|
||||
type: 'string', // string[]
|
||||
},
|
||||
showNsfw: {
|
||||
type: 'boolean',
|
||||
},
|
||||
shouldUseInfiniteScroll: {
|
||||
type: 'boolean',
|
||||
},
|
||||
shouldShowTransitionPage: {
|
||||
type: 'boolean',
|
||||
},
|
||||
appTheme: {
|
||||
type: 'string',
|
||||
},
|
||||
themeMode: {
|
||||
type: 'string', // ThemeMode (enum)
|
||||
},
|
||||
shouldUsePureBlackMode: {
|
||||
type: 'boolean',
|
||||
},
|
||||
mangaGridItemWidth: {
|
||||
type: 'number',
|
||||
toValidValue: (value: number) => coerceIn(value, MANGA_GRID_WIDTH.min, MANGA_GRID_WIDTH.max),
|
||||
},
|
||||
isPinned: {
|
||||
type: 'boolean',
|
||||
},
|
||||
isEnabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
lastUsedSourceId: {
|
||||
type: 'string',
|
||||
},
|
||||
shouldShowOnlySourcesWithResults: {
|
||||
type: 'boolean',
|
||||
},
|
||||
excludedScanlators: {
|
||||
type: 'string', // string[]
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA_KEY_TO_TYPE);
|
||||
export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA);
|
||||
|
||||
export const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
|
||||
// metadata applied migration id
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
import { MetaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { AllowedMetadataValueTypes, AppMetadataKeys, Metadata } from '@/modules/metadata/Metadata.types.ts';
|
||||
import { APP_METADATA_KEY_TO_TYPE } from '@/modules/metadata/Metadata.constants.ts';
|
||||
import { APP_METADATA } from '@/modules/metadata/Metadata.constants.ts';
|
||||
|
||||
export const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
|
||||
key: string,
|
||||
value: string,
|
||||
): T => {
|
||||
const typeOfKey = APP_METADATA_KEY_TO_TYPE[key as AppMetadataKeys];
|
||||
const typeOfKey = APP_METADATA[key as AppMetadataKeys].type;
|
||||
const isAutoType = typeOfKey === 'auto';
|
||||
|
||||
if ((isAutoType && !Number.isNaN(+value)) || typeOfKey === 'number') {
|
||||
|
||||
@@ -22,6 +22,7 @@ import { doesMetadataKeyExistIn, getMetadataKey } from '@/modules/metadata/Metad
|
||||
import { applyMetadataMigrations } from '@/modules/metadata/services/MetadataMigrations.ts';
|
||||
import { SourceIdInfo } from '@/modules/source/Source.types.ts';
|
||||
import { ChapterIdInfo } from '@/modules/chapter/Chapter.types.ts';
|
||||
import { APP_METADATA } from '@/modules/metadata/Metadata.constants.ts';
|
||||
|
||||
const getMetadataValueFrom = <Key extends AppMetadataKeys, Value extends AllowedMetadataValueTypes>(
|
||||
metadata: Metadata | undefined,
|
||||
@@ -37,7 +38,9 @@ const getMetadataValueFrom = <Key extends AppMetadataKeys, Value extends Allowed
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return convertValueFromMetadata(key, metadata[getMetadataKey(key, prefixes)]);
|
||||
const convertedValue = convertValueFromMetadata(key, metadata[getMetadataKey(key, prefixes)]);
|
||||
|
||||
return APP_METADATA[key].toValidValue?.(convertedValue) ?? convertedValue;
|
||||
};
|
||||
|
||||
export function getMetadataFrom<METADATA extends Partial<Metadata<AppMetadataKeys, AllowedMetadataValueTypes>>>(
|
||||
|
||||
@@ -16,7 +16,10 @@ import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.ts
|
||||
import { ReaderSettingExitMode } from '@/modules/reader/components/settings/behaviour/ReaderSettingExitMode.tsx';
|
||||
import { isOffsetDoubleSpreadPagesEditable } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import {
|
||||
DEFAULT_READER_SETTINGS,
|
||||
IMAGE_PRE_LOAD_AMOUNT,
|
||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { ReaderSettingAutoScroll } from '@/modules/reader/components/settings/behaviour/ReaderSettingAutoScroll.tsx';
|
||||
import { ReaderSettingScrollAmount } from '@/modules/reader/components/settings/behaviour/ReaderSettingScrollAmount.tsx';
|
||||
|
||||
@@ -129,9 +132,9 @@ export const ReaderBehaviourSettings = ({
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.imagePreLoadAmount,
|
||||
value: settings.imagePreLoadAmount,
|
||||
step: 1,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: IMAGE_PRE_LOAD_AMOUNT.step,
|
||||
min: IMAGE_PRE_LOAD_AMOUNT.min,
|
||||
max: IMAGE_PRE_LOAD_AMOUNT.max,
|
||||
onChange: (_, value) => {
|
||||
updateSetting('imagePreLoadAmount', value as number, false);
|
||||
},
|
||||
|
||||
@@ -7,15 +7,11 @@
|
||||
*/
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { IReaderSettings, ReaderScrollAmount } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import {
|
||||
ReaderScrollAmount,
|
||||
DEFAULT_READER_SETTINGS,
|
||||
SCROLL_AMOUNT,
|
||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { DEFAULT_READER_SETTINGS, SCROLL_AMOUNT } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderScrollAmount> = {
|
||||
[ReaderScrollAmount.TINY]: {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { CUSTOM_FILTER, DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingBrightness = ({
|
||||
brightness,
|
||||
@@ -48,9 +48,9 @@ export const ReaderSettingBrightness = ({
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.brightness.value,
|
||||
value: brightness.value,
|
||||
step: 1,
|
||||
min: 5,
|
||||
max: 200,
|
||||
step: CUSTOM_FILTER.brightness.step,
|
||||
min: CUSTOM_FILTER.brightness.min,
|
||||
max: CUSTOM_FILTER.brightness.max,
|
||||
onChange: (_, value) => {
|
||||
updateSetting('brightness', { ...brightness, value: value as number }, false);
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { CUSTOM_FILTER, DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingContrast = ({
|
||||
contrast,
|
||||
@@ -47,9 +47,9 @@ export const ReaderSettingContrast = ({
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.contrast.value,
|
||||
value: contrast.value,
|
||||
step: 1,
|
||||
min: 5,
|
||||
max: 200,
|
||||
step: CUSTOM_FILTER.contrast.step,
|
||||
min: CUSTOM_FILTER.contrast.min,
|
||||
max: CUSTOM_FILTER.contrast.max,
|
||||
onChange: (_, newValue) => {
|
||||
updateSetting('contrast', { ...contrast, value: newValue as number }, false);
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { CUSTOM_FILTER, DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingHue = ({
|
||||
hue,
|
||||
@@ -43,9 +43,9 @@ export const ReaderSettingHue = ({
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.hue.value,
|
||||
value: hue.value,
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 200,
|
||||
step: CUSTOM_FILTER.hue.step,
|
||||
min: CUSTOM_FILTER.hue.min,
|
||||
max: CUSTOM_FILTER.hue.max,
|
||||
onChange: (_, newValue) => {
|
||||
updateSetting('hue', { ...hue, value: newValue as number }, false);
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ import { TranslationKey } from '@/Base.types.ts';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
|
||||
import {
|
||||
CUSTOM_FILTER,
|
||||
DEFAULT_READER_SETTINGS,
|
||||
READER_BLEND_MODE_VALUE_TO_DISPLAY_DATA,
|
||||
READER_BLEND_MODE_VALUES,
|
||||
@@ -29,13 +30,6 @@ const RGBA_TYPE_TO_TRANSLATION_KEY: Record<RGBAType, TranslationKey> = {
|
||||
alpha: 'reader.settings.custom_filter.rgba.alpha',
|
||||
};
|
||||
|
||||
const RGBA_TYPE_TO_MAX_VALUE: Record<RGBAType, number> = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255,
|
||||
alpha: 100,
|
||||
};
|
||||
|
||||
export const ReaderSettingRGBA = ({
|
||||
rgba,
|
||||
updateSetting,
|
||||
@@ -84,9 +78,9 @@ export const ReaderSettingRGBA = ({
|
||||
slider: {
|
||||
value,
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.rgba.value[key as RGBAType],
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: RGBA_TYPE_TO_MAX_VALUE[key as RGBAType],
|
||||
step: CUSTOM_FILTER.rgba[key as RGBAType].step,
|
||||
min: CUSTOM_FILTER.rgba[key as RGBAType].min,
|
||||
max: CUSTOM_FILTER.rgba[key as RGBAType].max,
|
||||
onChange: (_, newValue) => {
|
||||
updateSetting(
|
||||
'rgba',
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { CUSTOM_FILTER, DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingSaturate = ({
|
||||
saturate,
|
||||
@@ -47,9 +47,9 @@ export const ReaderSettingSaturate = ({
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.saturate.value,
|
||||
value: saturate.value,
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 200,
|
||||
step: CUSTOM_FILTER.saturate.step,
|
||||
min: CUSTOM_FILTER.saturate.min,
|
||||
max: CUSTOM_FILTER.saturate.max,
|
||||
onChange: (_, value) => {
|
||||
updateSetting('saturate', { ...saturate, value: value as number }, false);
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings, ProgressBarType, ReaderOverlayMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { AUTO_SCROLL_SPEED, DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingProgressBarSize = ({
|
||||
overlayMode,
|
||||
@@ -37,9 +37,9 @@ export const ReaderSettingProgressBarSize = ({
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.progressBarSize,
|
||||
value: progressBarSize,
|
||||
step: 1,
|
||||
min: 2,
|
||||
max: 20,
|
||||
step: AUTO_SCROLL_SPEED.step,
|
||||
min: AUTO_SCROLL_SPEED.min,
|
||||
max: AUTO_SCROLL_SPEED.max,
|
||||
onChange: (_, value) => {
|
||||
setProgressBarSize(value as number, false);
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings, IReaderSettingsWithDefaultFlag, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { DEFAULT_READER_SETTINGS, PAGE_GAP } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { isContinuousReadingMode } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||
import { MultiValueButtonDefaultableProps } from '@/modules/core/Core.types.ts';
|
||||
|
||||
@@ -37,11 +37,11 @@ export const ReaderSettingPageGap = ({
|
||||
onDefault={isDefaultable ? onDefault : undefined}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.readerWidth.value,
|
||||
defaultValue: DEFAULT_READER_SETTINGS.pageGap,
|
||||
value: pageGap.value,
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 20,
|
||||
step: PAGE_GAP.step,
|
||||
min: PAGE_GAP.min,
|
||||
max: PAGE_GAP.max,
|
||||
onChange: (_, value) => {
|
||||
updateSetting(value as number, false);
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
ReaderHotkey,
|
||||
ReaderOverlayMode,
|
||||
ReaderPageScaleMode,
|
||||
ReaderScrollAmount,
|
||||
ReadingDirection,
|
||||
ReadingMode,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
@@ -36,15 +37,93 @@ import { TranslationKey } from '@/Base.types.ts';
|
||||
import { TapZoneLayouts } from '@/modules/reader/types/TapZoneLayout.types.ts';
|
||||
import { WebtoonPageIcon } from '@/assets/icons/svg/WebtoonPageIcon.tsx';
|
||||
|
||||
/**
|
||||
* percentage values
|
||||
*/
|
||||
export enum ReaderScrollAmount {
|
||||
TINY = 10,
|
||||
SMALL = 25,
|
||||
MEDIUM = 75,
|
||||
LARGE = 95,
|
||||
}
|
||||
export const AUTO_SCROLL_SPEED = {
|
||||
min: 0.5,
|
||||
max: 60,
|
||||
step: 0.5,
|
||||
default: 5,
|
||||
};
|
||||
|
||||
export const SCROLL_AMOUNT = {
|
||||
min: 5,
|
||||
max: 100,
|
||||
default: ReaderScrollAmount.LARGE,
|
||||
step: 5,
|
||||
};
|
||||
|
||||
export const PROGRESS_BAR_SIZE = {
|
||||
min: 2,
|
||||
max: 20,
|
||||
step: 1,
|
||||
default: 4,
|
||||
};
|
||||
|
||||
export const IMAGE_PRE_LOAD_AMOUNT = {
|
||||
min: 1,
|
||||
max: 20,
|
||||
default: 5,
|
||||
step: 1,
|
||||
};
|
||||
|
||||
export const PAGE_GAP = {
|
||||
min: 0,
|
||||
max: 20,
|
||||
default: 5,
|
||||
step: 1,
|
||||
};
|
||||
|
||||
export const CUSTOM_FILTER = {
|
||||
brightness: {
|
||||
min: 5,
|
||||
max: 200,
|
||||
step: 1,
|
||||
default: 100,
|
||||
},
|
||||
contrast: {
|
||||
min: 5,
|
||||
max: 200,
|
||||
step: 1,
|
||||
default: 100,
|
||||
},
|
||||
saturate: {
|
||||
min: 0,
|
||||
max: 200,
|
||||
step: 1,
|
||||
default: 100,
|
||||
},
|
||||
hue: {
|
||||
min: 0,
|
||||
max: 200,
|
||||
step: 1,
|
||||
default: 0,
|
||||
},
|
||||
rgba: {
|
||||
red: {
|
||||
min: 0,
|
||||
max: 255,
|
||||
step: 1,
|
||||
default: 0,
|
||||
},
|
||||
green: {
|
||||
min: 0,
|
||||
max: 255,
|
||||
step: 1,
|
||||
default: 0,
|
||||
},
|
||||
blue: {
|
||||
min: 0,
|
||||
max: 255,
|
||||
step: 1,
|
||||
default: 0,
|
||||
},
|
||||
alpha: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const READING_DIRECTION_TO_THEME_DIRECTION: Record<ReadingDirection, Direction> = {
|
||||
[ReadingDirection.LTR]: 'ltr',
|
||||
@@ -84,7 +163,7 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = {
|
||||
tapZoneLayout: TapZoneLayouts.RIGHT_LEFT,
|
||||
tapZoneInvertMode: { vertical: false, horizontal: false },
|
||||
progressBarType: ProgressBarType.STANDARD,
|
||||
progressBarSize: 4,
|
||||
progressBarSize: PROGRESS_BAR_SIZE.default,
|
||||
progressBarPosition: ProgressBarPosition.AUTO,
|
||||
progressBarPositionAutoVertical: ProgressBarPosition.RIGHT,
|
||||
pageScaleMode: ReaderPageScaleMode.ORIGINAL,
|
||||
@@ -99,27 +178,27 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = {
|
||||
backgroundColor: ReaderBackgroundColor.THEME,
|
||||
customFilter: {
|
||||
brightness: {
|
||||
value: 100,
|
||||
value: CUSTOM_FILTER.brightness.default,
|
||||
enabled: false,
|
||||
},
|
||||
contrast: {
|
||||
value: 100,
|
||||
value: CUSTOM_FILTER.contrast.default,
|
||||
enabled: false,
|
||||
},
|
||||
saturate: {
|
||||
value: 100,
|
||||
value: CUSTOM_FILTER.saturate.default,
|
||||
enabled: false,
|
||||
},
|
||||
hue: {
|
||||
value: 0,
|
||||
value: CUSTOM_FILTER.hue.default,
|
||||
enabled: false,
|
||||
},
|
||||
rgba: {
|
||||
value: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
alpha: 0,
|
||||
red: CUSTOM_FILTER.rgba.red.default,
|
||||
green: CUSTOM_FILTER.rgba.green.default,
|
||||
blue: CUSTOM_FILTER.rgba.blue.default,
|
||||
alpha: CUSTOM_FILTER.rgba.alpha.default,
|
||||
blendMode: ReaderBlendMode.DEFAULT,
|
||||
},
|
||||
enabled: false,
|
||||
@@ -128,7 +207,7 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = {
|
||||
grayscale: false,
|
||||
invert: false,
|
||||
},
|
||||
pageGap: 5,
|
||||
pageGap: PAGE_GAP.default,
|
||||
hotkeys: {
|
||||
[ReaderHotkey.PREVIOUS_PAGE]: ['arrowleft', 'a'],
|
||||
[ReaderHotkey.NEXT_PAGE]: ['arrowright', 'd'],
|
||||
@@ -147,10 +226,10 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = {
|
||||
[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE]: ['v'],
|
||||
[ReaderHotkey.EXIT_READER]: ['c'],
|
||||
},
|
||||
imagePreLoadAmount: 5,
|
||||
imagePreLoadAmount: IMAGE_PRE_LOAD_AMOUNT.default,
|
||||
shouldUseAutoWebtoonMode: true,
|
||||
autoScroll: {
|
||||
value: 5,
|
||||
value: AUTO_SCROLL_SPEED.default,
|
||||
smooth: true,
|
||||
},
|
||||
shouldShowReadingModePreview: true,
|
||||
@@ -303,18 +382,6 @@ export const CONTINUOUS_READING_MODE_TO_SCROLL_DIRECTION: Record<
|
||||
[ReadingMode.WEBTOON]: ScrollDirection.Y,
|
||||
};
|
||||
|
||||
export const AUTO_SCROLL_SPEED = {
|
||||
min: 0.5,
|
||||
max: 60,
|
||||
step: 0.5,
|
||||
};
|
||||
|
||||
export const SCROLL_AMOUNT = {
|
||||
min: 5,
|
||||
max: 100,
|
||||
step: 5,
|
||||
};
|
||||
|
||||
export const READER_BLEND_MODE_VALUE_TO_DISPLAY_DATA = {
|
||||
[ReaderBlendMode.DEFAULT]: {
|
||||
title: 'reader.settings.custom_filter.rgba.blend_mode.default',
|
||||
|
||||
@@ -9,16 +9,18 @@
|
||||
import { memo, ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import { Direction, useTheme } from '@mui/material/styles';
|
||||
import { useAutomaticScrolling } from '@/modules/core/hooks/useAutomaticScrolling.ts';
|
||||
import { IReaderSettings, ReadingMode, TReaderAutoScrollContext } from '@/modules/reader/types/Reader.types.ts';
|
||||
import {
|
||||
IReaderSettings,
|
||||
ReaderScrollAmount,
|
||||
ReadingMode,
|
||||
TReaderAutoScrollContext,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ReaderAutoScrollContext } from '@/modules/reader/contexts/ReaderAutoScrollContext.tsx';
|
||||
import { ReaderControls } from '@/modules/reader/services/ReaderControls';
|
||||
import { isContinuousReadingMode } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
|
||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||
import {
|
||||
CONTINUOUS_READING_MODE_TO_SCROLL_DIRECTION,
|
||||
ReaderScrollAmount,
|
||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { CONTINUOUS_READING_MODE_TO_SCROLL_DIRECTION } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
const BaseReaderAutoScrollContextProvider = ({
|
||||
children,
|
||||
|
||||
@@ -24,15 +24,13 @@ import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/R
|
||||
import {
|
||||
PageInViewportType,
|
||||
ProgressBarPosition,
|
||||
ReaderScrollAmount,
|
||||
ReaderTransitionPageMode,
|
||||
ReadingDirection,
|
||||
ReadingMode,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ScrollDirection, ScrollOffset } from '@/modules/core/Core.types.ts';
|
||||
import {
|
||||
ReaderScrollAmount,
|
||||
READING_DIRECTION_TO_THEME_DIRECTION,
|
||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import {
|
||||
isATransitionPageVisible,
|
||||
isEndOfPageInViewport,
|
||||
|
||||
@@ -14,7 +14,6 @@ import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types
|
||||
import { TMangaReader } from '@/modules/manga/Manga.types.ts';
|
||||
import { useAutomaticScrolling } from '@/modules/core/hooks/useAutomaticScrolling.ts';
|
||||
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
|
||||
import { ReaderScrollAmount } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export enum ProgressBarType {
|
||||
HIDDEN,
|
||||
@@ -130,6 +129,16 @@ export interface ReaderCustomFilter {
|
||||
invert: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* percentage values
|
||||
*/
|
||||
export enum ReaderScrollAmount {
|
||||
TINY = 10,
|
||||
SMALL = 25,
|
||||
MEDIUM = 75,
|
||||
LARGE = 95,
|
||||
}
|
||||
|
||||
export interface IReaderSettingsGlobal {
|
||||
overlayMode: ReaderOverlayMode;
|
||||
exitMode: ReaderExitMode;
|
||||
|
||||
@@ -13,6 +13,13 @@ import { GridLayout } from '@/modules/core/Core.types.ts';
|
||||
import { getDefaultLanguages } from '@/modules/core/utils/Languages.ts';
|
||||
import { ThemeMode } from '@/modules/theme/contexts/AppThemeContext.tsx';
|
||||
|
||||
export const MANGA_GRID_WIDTH = {
|
||||
min: 100,
|
||||
max: 1000,
|
||||
step: 10,
|
||||
default: 300,
|
||||
};
|
||||
|
||||
export const SERVER_SETTINGS_METADATA_DEFAULT: MetadataServerSettings = {
|
||||
// downloads
|
||||
deleteChaptersManuallyMarkedRead: false,
|
||||
@@ -66,5 +73,5 @@ export const SERVER_SETTINGS_METADATA_DEFAULT: MetadataServerSettings = {
|
||||
customThemes: {},
|
||||
mangaThumbnailBackdrop: true,
|
||||
mangaDynamicColorSchemes: true,
|
||||
mangaGridItemWidth: 300,
|
||||
mangaGridItemWidth: MANGA_GRID_WIDTH.default,
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ import { MetadataThemeSettings } from '@/modules/theme/AppTheme.types.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { AppStorage } from '@/lib/storage/AppStorage.ts';
|
||||
import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts';
|
||||
import { SERVER_SETTINGS_METADATA_DEFAULT } from '@/modules/settings/Settings.constants.ts';
|
||||
import { MANGA_GRID_WIDTH, SERVER_SETTINGS_METADATA_DEFAULT } from '@/modules/settings/Settings.constants.ts';
|
||||
|
||||
export const Appearance = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
@@ -155,9 +155,9 @@ export const Appearance = () => {
|
||||
settingValue={`px: ${mangaGridItemWidth}`}
|
||||
value={mangaGridItemWidth}
|
||||
defaultValue={SERVER_SETTINGS_METADATA_DEFAULT.mangaGridItemWidth}
|
||||
minValue={100}
|
||||
maxValue={1000}
|
||||
stepSize={10}
|
||||
minValue={MANGA_GRID_WIDTH.min}
|
||||
maxValue={MANGA_GRID_WIDTH.max}
|
||||
stepSize={MANGA_GRID_WIDTH.step}
|
||||
valueUnit="px"
|
||||
showSlider
|
||||
handleUpdate={(width) => updateMetadataSetting('mangaGridItemWidth', width)}
|
||||
|
||||
Reference in New Issue
Block a user