diff --git a/packages/desktop-client/src/components/spreadsheet/useFormat.ts b/packages/desktop-client/src/components/spreadsheet/useFormat.ts index f01c29a751df339e82be87f2ecde79508298f4e3..b5e13f6c5d457be768d9998aa12af0fc704488fb 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 258f156e1fc978566829a11da3f0de6ece4d549b..9a4800ca221a54d80578292eb6f0ebddc8823ceb 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 7bfd067d09728dff73e8fb467d173daf62c0d16d..eb210ec47ca29e2efcba0616cc16b4d9a5aaa262 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 0d74961596a32ab1761ade7011c8e5b1caecf101..5523f5d552c0f6256920e38b3c27ab86c29695b9 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 ddb4356891981dd2516c6c325e5068b91fa9a2b0..0000000000000000000000000000000000000000 --- 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 275ee553229807bd1859a95a4c6c8ec52bf9aec8..7f125962f315017c9206b6e0c50ddcc0059dfa28 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 879a8535fc0f46bd06a102741d77aa428cc81dad..43dabd75372229bb3de1dea07e2fd27b845f363e 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 0000000000000000000000000000000000000000..eb5ceb11f2ed2be99d65587d287735d8e566379e --- /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.