From 8498d7f78816edea6f48eec25b64232cdf4b41ee Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins <matiss@mja.lv> Date: Mon, 9 Sep 2024 08:04:41 +0100 Subject: [PATCH] :recycle: (synced-prefs) refactor number formatter away from redux (#3397) --- .../src/components/spreadsheet/useFormat.ts | 36 +++++++++++++++---- .../desktop-client/src/hooks/useDateFormat.ts | 7 ++-- .../src/hooks/useFeatureFlag.ts | 15 ++++---- .../loot-core/src/client/reducers/prefs.ts | 25 ------------- packages/loot-core/src/client/selectors.ts | 18 ---------- .../src/client/state-types/prefs.d.ts | 13 +++---- packages/loot-core/src/shared/util.ts | 2 +- upcoming-release-notes/3397.md | 6 ++++ 8 files changed, 49 insertions(+), 73 deletions(-) delete mode 100644 packages/loot-core/src/client/selectors.ts create mode 100644 upcoming-release-notes/3397.md diff --git a/packages/desktop-client/src/components/spreadsheet/useFormat.ts b/packages/desktop-client/src/components/spreadsheet/useFormat.ts index f01c29a75..b5e13f6c5 100644 --- a/packages/desktop-client/src/components/spreadsheet/useFormat.ts +++ b/packages/desktop-client/src/components/spreadsheet/useFormat.ts @@ -1,8 +1,13 @@ -import { useCallback } from 'react'; -import { useSelector } from 'react-redux'; +import { useCallback, useEffect, useMemo } from 'react'; -import { selectNumberFormat } from 'loot-core/src/client/selectors'; -import { integerToCurrency } from 'loot-core/src/shared/util'; +import { + getNumberFormat, + integerToCurrency, + isNumberFormat, + setNumberFormat, +} from 'loot-core/src/shared/util'; + +import { useSyncedPref } from '../../hooks/useSyncedPref'; export type FormatType = | 'string' @@ -55,11 +60,28 @@ function format( } export function useFormat() { - const numberFormat = useSelector(selectNumberFormat); + const [numberFormat] = useSyncedPref('numberFormat'); + const [hideFraction] = useSyncedPref('hideFraction'); + + const config = useMemo( + () => ({ + format: isNumberFormat(numberFormat) ? numberFormat : 'comma-dot', + hideFraction: String(hideFraction) === 'true', + }), + [numberFormat, hideFraction], + ); + + // Hack: keep the global number format in sync - update the settings when + // the underlying configuration changes. + // This should be patched by moving all number-formatting utilities away from + // the global `getNumberFormat()` and to using the reactive `useFormat` hook. + useEffect(() => { + setNumberFormat(config); + }, [config]); return useCallback( (value: unknown, type: FormatType = 'string') => - format(value, type, numberFormat.formatter), - [numberFormat], + format(value, type, getNumberFormat(config).formatter), + [config], ); } diff --git a/packages/desktop-client/src/hooks/useDateFormat.ts b/packages/desktop-client/src/hooks/useDateFormat.ts index 258f156e1..9a4800ca2 100644 --- a/packages/desktop-client/src/hooks/useDateFormat.ts +++ b/packages/desktop-client/src/hooks/useDateFormat.ts @@ -1,7 +1,6 @@ -import { useSelector } from 'react-redux'; - -import { type State } from 'loot-core/src/client/state-types'; +import { useSyncedPref } from './useSyncedPref'; export function useDateFormat() { - return useSelector((state: State) => state.prefs.local?.dateFormat); + const [dateFormat] = useSyncedPref('dateFormat'); + return dateFormat; } diff --git a/packages/desktop-client/src/hooks/useFeatureFlag.ts b/packages/desktop-client/src/hooks/useFeatureFlag.ts index 7bfd067d0..eb210ec47 100644 --- a/packages/desktop-client/src/hooks/useFeatureFlag.ts +++ b/packages/desktop-client/src/hooks/useFeatureFlag.ts @@ -1,8 +1,7 @@ -import { useSelector } from 'react-redux'; - -import { type State } from 'loot-core/src/client/state-types'; import type { FeatureFlag } from 'loot-core/src/types/prefs'; +import { useSyncedPref } from './useSyncedPref'; + const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = { reportBudget: false, goalTemplatesEnabled: false, @@ -12,11 +11,9 @@ const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = { }; export function useFeatureFlag(name: FeatureFlag): boolean { - return useSelector((state: State) => { - const value = state.prefs.local[`flags.${name}`]; + const [value] = useSyncedPref(`flags.${name}`); - return value === undefined - ? DEFAULT_FEATURE_FLAG_STATE[name] || false - : String(value) === 'true'; - }); + return value === undefined + ? DEFAULT_FEATURE_FLAG_STATE[name] || false + : String(value) === 'true'; } diff --git a/packages/loot-core/src/client/reducers/prefs.ts b/packages/loot-core/src/client/reducers/prefs.ts index 0d7496159..5523f5d55 100644 --- a/packages/loot-core/src/client/reducers/prefs.ts +++ b/packages/loot-core/src/client/reducers/prefs.ts @@ -1,5 +1,4 @@ // @ts-strict-ignore -import { isNumberFormat, setNumberFormat } from '../../shared/util'; import * as constants from '../constants'; import type { Action } from '../state-types'; import type { PrefsState } from '../state-types/prefs'; @@ -12,32 +11,8 @@ const initialState: PrefsState = { export function update(state = initialState, action: Action): PrefsState { switch (action.type) { case constants.SET_PREFS: - if (action.prefs) { - setNumberFormat({ - format: isNumberFormat(action.prefs.numberFormat) - ? action.prefs.numberFormat - : 'comma-dot', - hideFraction: String(action.prefs.hideFraction) === 'true', - }); - } return { local: action.prefs, global: action.globalPrefs }; case constants.MERGE_LOCAL_PREFS: - if (action.prefs.numberFormat || action.prefs.hideFraction != null) { - setNumberFormat({ - format: isNumberFormat(action.prefs.numberFormat) - ? action.prefs.numberFormat - : isNumberFormat(state.local.numberFormat) - ? state.local.numberFormat - : 'comma-dot', - hideFraction: - String( - action.prefs.hideFraction != null - ? action.prefs.hideFraction - : state.local.hideFraction, - ) === 'true', - }); - } - return { ...state, local: { ...state.local, ...action.prefs }, diff --git a/packages/loot-core/src/client/selectors.ts b/packages/loot-core/src/client/selectors.ts deleted file mode 100644 index ddb435689..000000000 --- a/packages/loot-core/src/client/selectors.ts +++ /dev/null @@ -1,18 +0,0 @@ -// @ts-strict-ignore -import { createSelector } from 'reselect'; - -import { getNumberFormat, isNumberFormat } from '../shared/util'; - -import type { State } from './state-types'; - -const getState = (state: State) => state; - -const getPrefsState = createSelector(getState, state => state.prefs); -const getLocalPrefsState = createSelector(getPrefsState, prefs => prefs.local); - -export const selectNumberFormat = createSelector(getLocalPrefsState, prefs => - getNumberFormat({ - format: isNumberFormat(prefs.numberFormat) ? prefs.numberFormat : undefined, - hideFraction: String(prefs.hideFraction) === 'true', - }), -); diff --git a/packages/loot-core/src/client/state-types/prefs.d.ts b/packages/loot-core/src/client/state-types/prefs.d.ts index 275ee5532..7f125962f 100644 --- a/packages/loot-core/src/client/state-types/prefs.d.ts +++ b/packages/loot-core/src/client/state-types/prefs.d.ts @@ -1,25 +1,20 @@ -import type { - GlobalPrefs, - LocalPrefs, - MetadataPrefs, - SyncedPrefs, -} from '../../types/prefs'; +import type { GlobalPrefs, LocalPrefs, MetadataPrefs } from '../../types/prefs'; import type * as constants from '../constants'; export type PrefsState = { - local: LocalPrefs & MetadataPrefs & SyncedPrefs; + local: LocalPrefs & MetadataPrefs; global: GlobalPrefs; }; export type SetPrefsAction = { type: typeof constants.SET_PREFS; - prefs: LocalPrefs & MetadataPrefs & SyncedPrefs; + prefs: LocalPrefs & MetadataPrefs; globalPrefs: GlobalPrefs; }; export type MergeLocalPrefsAction = { type: typeof constants.MERGE_LOCAL_PREFS; - prefs: LocalPrefs & MetadataPrefs & SyncedPrefs; + prefs: LocalPrefs & MetadataPrefs; }; export type MergeGlobalPrefsAction = { diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts index 879a8535f..43dabd753 100644 --- a/packages/loot-core/src/shared/util.ts +++ b/packages/loot-core/src/shared/util.ts @@ -233,7 +233,7 @@ const NUMBER_FORMATS = [ type NumberFormats = (typeof NUMBER_FORMATS)[number]; -export function isNumberFormat(input: string): input is NumberFormats { +export function isNumberFormat(input: string = ''): input is NumberFormats { return (NUMBER_FORMATS as readonly string[]).includes(input); } diff --git a/upcoming-release-notes/3397.md b/upcoming-release-notes/3397.md new file mode 100644 index 000000000..eb5ceb11f --- /dev/null +++ b/upcoming-release-notes/3397.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +SyncedPrefs: refactor usages of number formatter away from redux. -- GitLab