diff --git a/src/modules/downloads/Downloads.constants.ts b/src/modules/downloads/Downloads.constants.ts new file mode 100644 index 00000000..d1f10059 --- /dev/null +++ b/src/modules/downloads/Downloads.constants.ts @@ -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, +}; diff --git a/src/modules/downloads/components/DownloadAheadSetting.tsx b/src/modules/downloads/components/DownloadAheadSetting.tsx index 5232e20d..38721d0f 100644 --- a/src/modules/downloads/components/DownloadAheadSetting.tsx +++ b/src/modules/downloads/components/DownloadAheadSetting.tsx @@ -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')} diff --git a/src/modules/metadata/Metadata.constants.ts b/src/modules/metadata/Metadata.constants.ts index 99ff5247..2005603c 100644 --- a/src/modules/metadata/Metadata.constants.ts +++ b/src/modules/metadata/Metadata.constants.ts @@ -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; +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 diff --git a/src/modules/metadata/services/MetadataConverter.ts b/src/modules/metadata/services/MetadataConverter.ts index 620c82ce..5a939c05 100644 --- a/src/modules/metadata/services/MetadataConverter.ts +++ b/src/modules/metadata/services/MetadataConverter.ts @@ -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 = ( 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') { diff --git a/src/modules/metadata/services/MetadataReader.ts b/src/modules/metadata/services/MetadataReader.ts index 2ba81220..d9451d0d 100644 --- a/src/modules/metadata/services/MetadataReader.ts +++ b/src/modules/metadata/services/MetadataReader.ts @@ -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 = ( metadata: Metadata | undefined, @@ -37,7 +38,9 @@ const getMetadataValueFrom = >>( diff --git a/src/modules/reader/components/settings/behaviour/ReaderBehaviourSettings.tsx b/src/modules/reader/components/settings/behaviour/ReaderBehaviourSettings.tsx index 9071771e..6a29f051 100644 --- a/src/modules/reader/components/settings/behaviour/ReaderBehaviourSettings.tsx +++ b/src/modules/reader/components/settings/behaviour/ReaderBehaviourSettings.tsx @@ -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); }, diff --git a/src/modules/reader/components/settings/behaviour/ReaderSettingScrollAmount.tsx b/src/modules/reader/components/settings/behaviour/ReaderSettingScrollAmount.tsx index 9a304463..19d11f22 100644 --- a/src/modules/reader/components/settings/behaviour/ReaderSettingScrollAmount.tsx +++ b/src/modules/reader/components/settings/behaviour/ReaderSettingScrollAmount.tsx @@ -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.TINY]: { diff --git a/src/modules/reader/components/settings/filters/ReaderSettingBrightness.tsx b/src/modules/reader/components/settings/filters/ReaderSettingBrightness.tsx index 0b3e5dee..8599cee7 100644 --- a/src/modules/reader/components/settings/filters/ReaderSettingBrightness.tsx +++ b/src/modules/reader/components/settings/filters/ReaderSettingBrightness.tsx @@ -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); }, diff --git a/src/modules/reader/components/settings/filters/ReaderSettingContrast.tsx b/src/modules/reader/components/settings/filters/ReaderSettingContrast.tsx index cec114fb..e60341d2 100644 --- a/src/modules/reader/components/settings/filters/ReaderSettingContrast.tsx +++ b/src/modules/reader/components/settings/filters/ReaderSettingContrast.tsx @@ -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); }, diff --git a/src/modules/reader/components/settings/filters/ReaderSettingHue.tsx b/src/modules/reader/components/settings/filters/ReaderSettingHue.tsx index f201e412..814d47f0 100644 --- a/src/modules/reader/components/settings/filters/ReaderSettingHue.tsx +++ b/src/modules/reader/components/settings/filters/ReaderSettingHue.tsx @@ -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); }, diff --git a/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx b/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx index c233fcf0..87b234eb 100644 --- a/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx +++ b/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx @@ -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 = { alpha: 'reader.settings.custom_filter.rgba.alpha', }; -const RGBA_TYPE_TO_MAX_VALUE: Record = { - 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', diff --git a/src/modules/reader/components/settings/filters/ReaderSettingSaturate.tsx b/src/modules/reader/components/settings/filters/ReaderSettingSaturate.tsx index 20406470..6f7e4904 100644 --- a/src/modules/reader/components/settings/filters/ReaderSettingSaturate.tsx +++ b/src/modules/reader/components/settings/filters/ReaderSettingSaturate.tsx @@ -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); }, diff --git a/src/modules/reader/components/settings/general/ReaderSettingProgressBarSize.tsx b/src/modules/reader/components/settings/general/ReaderSettingProgressBarSize.tsx index fbace728..ff20a115 100644 --- a/src/modules/reader/components/settings/general/ReaderSettingProgressBarSize.tsx +++ b/src/modules/reader/components/settings/general/ReaderSettingProgressBarSize.tsx @@ -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); }, diff --git a/src/modules/reader/components/settings/layout/ReaderSettingPageGap.tsx b/src/modules/reader/components/settings/layout/ReaderSettingPageGap.tsx index 0f364bbb..139db168 100644 --- a/src/modules/reader/components/settings/layout/ReaderSettingPageGap.tsx +++ b/src/modules/reader/components/settings/layout/ReaderSettingPageGap.tsx @@ -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); }, diff --git a/src/modules/reader/constants/ReaderSettings.constants.tsx b/src/modules/reader/constants/ReaderSettings.constants.tsx index f89e71ed..155eca2f 100644 --- a/src/modules/reader/constants/ReaderSettings.constants.tsx +++ b/src/modules/reader/constants/ReaderSettings.constants.tsx @@ -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.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', diff --git a/src/modules/reader/contexts/ReaderAutoScrollContextProvider.tsx b/src/modules/reader/contexts/ReaderAutoScrollContextProvider.tsx index adf4fda9..cdd0a3cc 100644 --- a/src/modules/reader/contexts/ReaderAutoScrollContextProvider.tsx +++ b/src/modules/reader/contexts/ReaderAutoScrollContextProvider.tsx @@ -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, diff --git a/src/modules/reader/services/ReaderControls.ts b/src/modules/reader/services/ReaderControls.ts index cdb0f7d6..38221aea 100644 --- a/src/modules/reader/services/ReaderControls.ts +++ b/src/modules/reader/services/ReaderControls.ts @@ -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, diff --git a/src/modules/reader/types/Reader.types.ts b/src/modules/reader/types/Reader.types.ts index 7a7dba52..4b6342c1 100644 --- a/src/modules/reader/types/Reader.types.ts +++ b/src/modules/reader/types/Reader.types.ts @@ -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; diff --git a/src/modules/settings/Settings.constants.ts b/src/modules/settings/Settings.constants.ts index de3dc9ab..8f4dcacb 100644 --- a/src/modules/settings/Settings.constants.ts +++ b/src/modules/settings/Settings.constants.ts @@ -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, }; diff --git a/src/modules/settings/screens/Appearance.tsx b/src/modules/settings/screens/Appearance.tsx index b9a9089f..51bf2571 100644 --- a/src/modules/settings/screens/Appearance.tsx +++ b/src/modules/settings/screens/Appearance.tsx @@ -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)}