diff --git a/public/locales/en.json b/public/locales/en.json index 04cf476e..933dcea4 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -869,6 +869,15 @@ "invert": "Invert", "rgba": { "alpha": "Alpha", + "blend_mode": { + "darken": "Darken", + "default": "$t(global.label.default)", + "lighten": "Lighten", + "multiply": "Multiply", + "overlay": "Overlay", + "screen": "Screen", + "title": "Color filter blend mode" + }, "blue": "Blue", "green": "Green", "red": "Red", diff --git a/src/modules/reader/components/ReaderRGBAFilter.tsx b/src/modules/reader/components/ReaderRGBAFilter.tsx index 6edde30a..0d1a802a 100644 --- a/src/modules/reader/components/ReaderRGBAFilter.tsx +++ b/src/modules/reader/components/ReaderRGBAFilter.tsx @@ -17,7 +17,7 @@ const BaseReaderRGBAFilter = ({ readerNavBarWidth, customFilter: { rgba: { - value: { red, green, blue, alpha }, + value: { red, green, blue, alpha, blendMode }, enabled, }, }, @@ -36,6 +36,7 @@ const BaseReaderRGBAFilter = ({ bottom: 0, pointerEvents: 'none', background: `rgba(${red} ${green} ${blue} / ${alpha}%)`, + mixBlendMode: `${blendMode}`, }} /> ); diff --git a/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx b/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx index 190ca9b9..c233fcf0 100644 --- a/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx +++ b/src/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx @@ -13,9 +13,14 @@ import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.ts import { TranslationKey } from '@/Base.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, + READER_BLEND_MODE_VALUE_TO_DISPLAY_DATA, + READER_BLEND_MODE_VALUES, +} from '@/modules/reader/constants/ReaderSettings.constants.tsx'; +import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx'; -type RGBAType = keyof IReaderSettings['customFilter']['rgba']['value']; +type RGBAType = Exclude; const RGBA_TYPE_TO_TRANSLATION_KEY: Record = { red: 'reader.settings.custom_filter.rgba.red', @@ -52,49 +57,71 @@ export const ReaderSettingRGBA = ({ /> {rgba.enabled && ( - {Object.entries(rgba.value).map(([key, value]) => ( - - updateSetting( - 'rgba', - { - ...rgba, - value: { - ...rgba.value, - [key]: DEFAULT_READER_SETTINGS.customFilter.rgba.value[key as RGBAType], + {Object.entries(rgba.value).map(([key, value]) => { + if (key === 'blendMode') { + return null; + } + + return ( + + updateSetting( + 'rgba', + { + ...rgba, + value: { + ...rgba.value, + [key]: DEFAULT_READER_SETTINGS.customFilter.rgba.value[key as RGBAType], + }, + }, + true, + ) + } + slotProps={{ + 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], + onChange: (_, newValue) => { + updateSetting( + 'rgba', + { ...rgba, value: { ...rgba.value, [key]: newValue } }, + false, + ); + }, + onChangeCommitted: (_, newValue) => { + updateSetting( + 'rgba', + { ...rgba, value: { ...rgba.value, [key]: newValue } }, + true, + ); }, }, - true, - ) - } - slotProps={{ - 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], - onChange: (_, newValue) => { - updateSetting( - 'rgba', - { ...rgba, value: { ...rgba.value, [key]: newValue } }, - false, - ); - }, - onChangeCommitted: (_, newValue) => { - updateSetting( - 'rgba', - { ...rgba, value: { ...rgba.value, [key]: newValue } }, - true, - ); - }, + }} + /> + ); + })} + + updateSetting( + 'rgba', + { + ...rgba, + value: { ...rgba.value, blendMode: value }, }, - }} - /> - ))} + true, + ) + } + valueToDisplayData={READER_BLEND_MODE_VALUE_TO_DISPLAY_DATA} + /> )} diff --git a/src/modules/reader/constants/ReaderSettings.constants.tsx b/src/modules/reader/constants/ReaderSettings.constants.tsx index 53005c51..295cabca 100644 --- a/src/modules/reader/constants/ReaderSettings.constants.tsx +++ b/src/modules/reader/constants/ReaderSettings.constants.tsx @@ -20,6 +20,7 @@ import { ProgressBarPosition, ProgressBarType, ReaderBackgroundColor, + ReaderBlendMode, ReaderExitMode, ReaderHotkey, ReaderOverlayMode, @@ -108,6 +109,7 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = { green: 0, blue: 0, alpha: 0, + blendMode: ReaderBlendMode.DEFAULT, }, enabled: false, }, @@ -300,3 +302,32 @@ export const AUTO_SCROLL_SPEED = { max: 60, step: 0.5, }; + +export const READER_BLEND_MODE_VALUE_TO_DISPLAY_DATA = { + [ReaderBlendMode.DEFAULT]: { + title: 'reader.settings.custom_filter.rgba.blend_mode.default', + icon: null, + }, + [ReaderBlendMode.MULTIPLY]: { + title: 'reader.settings.custom_filter.rgba.blend_mode.multiply', + icon: null, + }, + [ReaderBlendMode.SCREEN]: { + title: 'reader.settings.custom_filter.rgba.blend_mode.screen', + icon: null, + }, + [ReaderBlendMode.OVERLAY]: { + title: 'reader.settings.custom_filter.rgba.blend_mode.overlay', + icon: null, + }, + [ReaderBlendMode.DARKEN]: { + title: 'reader.settings.custom_filter.rgba.blend_mode.darken', + icon: null, + }, + [ReaderBlendMode.LIGHTEN]: { + title: 'reader.settings.custom_filter.rgba.blend_mode.lighten', + icon: null, + }, +} satisfies ValueToDisplayData; + +export const READER_BLEND_MODE_VALUES = Object.values(ReaderBlendMode); diff --git a/src/modules/reader/services/ReaderSettingsMetadata.ts b/src/modules/reader/services/ReaderSettingsMetadata.ts index 7d3fc1bf..4c18729a 100644 --- a/src/modules/reader/services/ReaderSettingsMetadata.ts +++ b/src/modules/reader/services/ReaderSettingsMetadata.ts @@ -7,6 +7,8 @@ */ import { useEffect, useMemo } from 'react'; +// eslint-disable-next-line import/no-extraneous-dependencies,no-restricted-imports +import { deepmerge } from '@mui/utils'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestUpdateMangaMetadata, @@ -109,9 +111,11 @@ const convertMetadataToSettings = ( tapZoneInvertMode: jsonSaveParse((metadata.tapZoneInvertMode as string) ?? '') ?? defaultSettings.tapZoneInvertMode, - customFilter: - jsonSaveParse((metadata.customFilter as string) ?? '') ?? + customFilter: deepmerge( defaultSettings.customFilter, + jsonSaveParse((metadata.customFilter as string) ?? '') ?? + defaultSettings.customFilter, + ), readerWidth: jsonSaveParse((metadata.readerWidth as string) ?? '') ?? defaultSettings.readerWidth, diff --git a/src/modules/reader/types/Reader.types.ts b/src/modules/reader/types/Reader.types.ts index 694b9316..1577a105 100644 --- a/src/modules/reader/types/Reader.types.ts +++ b/src/modules/reader/types/Reader.types.ts @@ -79,6 +79,16 @@ export interface ReaderFilterRGBA { green: number; blue: number; alpha: number; + blendMode: ReaderBlendMode; +} + +export enum ReaderBlendMode { + DEFAULT = 'normal', + MULTIPLY = 'multiply', + SCREEN = 'screen', + OVERLAY = 'overlay', + DARKEN = 'darken', + LIGHTEN = 'lighten', } export interface ReaderCustomFilter {