diff --git a/apps/mobile/app/screens/settings/section-item.tsx b/apps/mobile/app/screens/settings/section-item.tsx index 75e3c4129..8a5733279 100644 --- a/apps/mobile/app/screens/settings/section-item.tsx +++ b/apps/mobile/app/screens/settings/section-item.tsx @@ -43,6 +43,7 @@ import { AppFontSize } from "../../utils/size"; import { DefaultAppStyles } from "../../utils/styles"; import { components } from "./components"; import { RouteParams, SettingSection } from "./types"; +import { strings } from "@notesnook/intl"; const _SectionItem = ({ item }: { item: SettingSection }) => { const { colors } = useThemeColors(); @@ -67,6 +68,7 @@ const _SectionItem = ({ item }: { item: SettingSection }) => { ); const inputRef = useRef(null); const [loading, setLoading] = useState(false); + const [inputError, setInputError] = useState(); const onChangeSettings = async () => { if (isDisabled) return; @@ -106,18 +108,30 @@ const _SectionItem = ({ item }: { item: SettingSection }) => { }); }; - const onChangeInputSelectorValue = (text: any) => { - if (text) { - const min = item.minInputValue || 0; - const max = item.maxInputValue || 0; - const value = parseInt(text); - text = - Number.isNaN(value) || value < min ? min : value > max ? max : text; - - SettingsService.set({ - [item.property as string]: `${text}` - }); + const onChangeInputSelectorValue = (text: string) => { + if (!item.property) return; + const trimmed = text.trim(); + if (!trimmed) { + setInputError(undefined); + return; } + + if (trimmed.endsWith(".")) { + setInputError(undefined); + return; + } + const value = Number(trimmed); + const min = item.minInputValue ?? 0; + const max = item.maxInputValue ?? 0; + + if (Number.isNaN(value) || value < min || value > max) { + setInputError(strings.valueMustBeBetween(min, max)); + return; + } + setInputError(undefined); + SettingsService.set({ + [item.property]: value + }); }; useEffect(() => { @@ -302,91 +316,116 @@ const _SectionItem = ({ item }: { item: SettingSection }) => { )} {item.type === "input-selector" && ( - - { - if (!checkIsFeatureAvailable()) return; - if (isDisabled) return; - const rawValue = SettingsService.get()[ - item.property as keyof SettingStore["settings"] - ] as string; - if (rawValue) { - const currentValue = parseInt(rawValue); + <> + + { + if (!checkIsFeatureAvailable()) return; + if (isDisabled) return; + const rawValue = SettingsService.get()[ + item.property as keyof SettingStore["settings"] + ] as string; + if (rawValue == null) return; + const step = item.step ?? 1; + const decimalPlaces = (step.toString().split(".")[1] || "") + .length; + + const currentValue = Number(rawValue); + if (Number.isNaN(currentValue)) return; const minValue = item.minInputValue || 0; if (currentValue <= minValue) return; - const nextValue = currentValue - 1; + const nextValue = Number( + (currentValue - step).toFixed(decimalPlaces) + ); SettingsService.set({ [item.property as string]: nextValue }); + setInputError(undefined); updateInput(nextValue); - } - }} - size={AppFontSize.xl} - /> - { - onChangeInputSelectorValue(e.nativeEvent.text); - item.inputProperties?.onSubmitEditing?.(e); - }} - editable={!isDisabled} - onChangeText={(text) => { - onChangeInputSelectorValue(text); - item.inputProperties?.onSubmitEditing?.(text as any); - }} - keyboardType="decimal-pad" - containerStyle={{ - width: 60 - }} - inputStyle={{ - width: 60, - textAlign: "center" - }} - wrapperStyle={{ - maxWidth: 60, - flexGrow: 0, - marginBottom: 0, - marginHorizontal: DefaultAppStyles.GAP_SMALL - }} - fwdRef={inputRef} - onLayout={() => { - if (item.property) { - updateInput(SettingsService.get()[item.property]); - } - }} - defaultValue={item.inputProperties?.defaultValue} - /> - { - if (!checkIsFeatureAvailable()) return; - if (isDisabled) return; - const rawValue = SettingsService.get()[ - item.property as keyof SettingStore["settings"] - ] as string; - if (rawValue) { - const currentValue = parseInt(rawValue); + }} + size={AppFontSize.xl} + /> + { + onChangeInputSelectorValue(e.nativeEvent.text); + item.inputProperties?.onSubmitEditing?.(e); + }} + editable={!isDisabled} + onChangeText={(text) => { + onChangeInputSelectorValue(text); + item.inputProperties?.onSubmitEditing?.(text as any); + }} + keyboardType="decimal-pad" + containerStyle={{ + width: 60 + }} + inputStyle={{ + width: 60, + textAlign: "center" + }} + wrapperStyle={{ + maxWidth: 60, + flexGrow: 0, + marginBottom: 0, + marginHorizontal: DefaultAppStyles.GAP_SMALL + }} + fwdRef={inputRef} + onLayout={() => { + if (item.property) { + updateInput(SettingsService.get()[item.property]); + } + }} + defaultValue={item.inputProperties?.defaultValue} + /> + { + if (!checkIsFeatureAvailable()) return; + if (isDisabled) return; + const rawValue = SettingsService.get()[ + item.property as keyof SettingStore["settings"] + ] as string; + if (rawValue == null) return; + const step = item.step ?? 1; + const decimalPlaces = (step.toString().split(".")[1] || "") + .length; + + const currentValue = Number(rawValue); + if (Number.isNaN(currentValue)) return; const max = item.maxInputValue || 0; if (currentValue >= max) return; - const nextValue = currentValue + 1; + const nextValue = Number( + (currentValue + step).toFixed(decimalPlaces) + ); SettingsService.set({ [item.property as string]: nextValue }); + setInputError(undefined); updateInput(nextValue); - } - }} - size={AppFontSize.xl} - /> - + }} + size={AppFontSize.xl} + /> + + + {inputError && ( + + {inputError} + + )} + )} diff --git a/apps/mobile/app/screens/settings/settings-data.tsx b/apps/mobile/app/screens/settings/settings-data.tsx index 5ee151d07..c35764f8c 100644 --- a/apps/mobile/app/screens/settings/settings-data.tsx +++ b/apps/mobile/app/screens/settings/settings-data.tsx @@ -206,7 +206,7 @@ export const settingsGroups: SettingSection[] = [ : status === SubscriptionStatus.ACTIVE ? strings.subRenewOn(expiryDate) : status === SubscriptionStatus.CANCELED || - status === SubscriptionStatus.PAUSED + status === SubscriptionStatus.PAUSED ? strings.subEndsOn(expiryDate) : status === SubscriptionStatus.EXPIRED ? subscriptionDaysLeft.time < -3 @@ -884,7 +884,8 @@ export const settingsGroups: SettingSection[] = [ property: "defaultLineHeight", icon: "format-line-spacing", minInputValue: EDITOR_LINE_HEIGHT.MIN, - maxInputValue: EDITOR_LINE_HEIGHT.MAX + maxInputValue: EDITOR_LINE_HEIGHT.MAX, + step: 0.1 }, { id: "title-format", diff --git a/apps/mobile/app/screens/settings/types.ts b/apps/mobile/app/screens/settings/types.ts index 4af28534e..e8766889f 100644 --- a/apps/mobile/app/screens/settings/types.ts +++ b/apps/mobile/app/screens/settings/types.ts @@ -53,6 +53,7 @@ export type SettingSection = { hideHeader?: boolean; disabled?: (current: unknown) => boolean; featureId?: FeatureId; + step?: number; }; export type SettingsGroup = {