From 97d1ce2f471e3041f714f383cc6478a136f408a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 7 Oct 2024 18:37:50 +0200 Subject: [PATCH] pl-fe: Complete migration of settings store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- packages/pl-fe/src/actions/notifications.ts | 12 +++---- packages/pl-fe/src/actions/search.ts | 5 ++- packages/pl-fe/src/actions/settings.ts | 34 +------------------ .../developers/developers-challenge.tsx | 4 +-- .../features/developers/developers-menu.tsx | 4 +-- .../status/components/status-type-icon.tsx | 4 +-- packages/pl-fe/src/globals.ts | 4 +-- packages/pl-fe/src/reducers/compose.ts | 30 ++++++++-------- packages/pl-fe/src/reducers/index.ts | 2 -- packages/pl-fe/src/reducers/settings.test.ts | 11 ------ packages/pl-fe/src/reducers/settings.ts | 34 ------------------- packages/pl-fe/src/stores/settings.ts | 17 ++++++++++ 12 files changed, 51 insertions(+), 110 deletions(-) delete mode 100644 packages/pl-fe/src/reducers/settings.test.ts delete mode 100644 packages/pl-fe/src/reducers/settings.ts diff --git a/packages/pl-fe/src/actions/notifications.ts b/packages/pl-fe/src/actions/notifications.ts index 59c30394c..41d757fff 100644 --- a/packages/pl-fe/src/actions/notifications.ts +++ b/packages/pl-fe/src/actions/notifications.ts @@ -294,14 +294,14 @@ const scrollTopNotifications = (top: boolean) => const setFilter = (filterType: FilterType, abort?: boolean) => (dispatch: AppDispatch) => { - const activeFilter = useSettingsStore.getState().settings.notifications.quickFilter.active as FilterType; + const settingsStore = useSettingsStore.getState(); + const activeFilter = settingsStore.settings.notifications.quickFilter.active as FilterType; - dispatch({ - type: NOTIFICATIONS_FILTER_SET, - path: ['notifications', 'quickFilter', 'active'], - value: filterType, - }); + settingsStore.changeSetting(['notifications', 'quickFilter', 'active'], filterType); + + dispatch({ type: NOTIFICATIONS_FILTER_SET }); dispatch(expandNotifications(undefined, undefined, abort)); + if (activeFilter !== filterType) dispatch(saveSettings()); }; diff --git a/packages/pl-fe/src/actions/search.ts b/packages/pl-fe/src/actions/search.ts index 57a1b6fdb..a72e253f1 100644 --- a/packages/pl-fe/src/actions/search.ts +++ b/packages/pl-fe/src/actions/search.ts @@ -1,3 +1,5 @@ +import { useSettingsStore } from 'pl-fe/stores/settings'; + import { getClient } from '../api'; import { fetchRelationships } from './accounts'; @@ -88,9 +90,10 @@ const setFilter = (value: string, filterType: SearchFilter) => (dispatch: AppDispatch) => { dispatch(submitSearch(value, filterType)); + useSettingsStore.getState().changeSetting(['search', 'filter'], filterType); + return dispatch({ type: SEARCH_FILTER_SET, - path: ['search', 'filter'], value: filterType, }); }; diff --git a/packages/pl-fe/src/actions/settings.ts b/packages/pl-fe/src/actions/settings.ts index e55f78660..0b5726f73 100644 --- a/packages/pl-fe/src/actions/settings.ts +++ b/packages/pl-fe/src/actions/settings.ts @@ -11,8 +11,6 @@ import { isLoggedIn } from 'pl-fe/utils/auth'; import type { AppDispatch, RootState } from 'pl-fe/store'; -const SETTING_CHANGE = 'SETTING_CHANGE' as const; - const FE_NAME = 'pl_fe'; const getAccount = makeGetAccount(); @@ -129,33 +127,9 @@ const saveSuccessMessage = defineMessage({ id: 'settings.save.success', defaultM // }), // }); -interface SettingChangeAction { - type: typeof SETTING_CHANGE; - path: string[]; - value: any; -} - -const changeSettingImmediate = (path: string[], value: any, opts?: SettingOpts) => - (dispatch: AppDispatch) => { - const action: SettingChangeAction = { - type: SETTING_CHANGE, - path, - value, - }; - - dispatch(action); - dispatch(saveSettings(opts)); - }; - const changeSetting = (path: string[], value: any, opts?: SettingOpts) => (dispatch: AppDispatch) => { - const action: SettingChangeAction = { - type: SETTING_CHANGE, - path, - value, - }; - - dispatch(action); + useSettingsStore.getState().changeSetting(path, value); return dispatch(saveSettings(opts)); }; @@ -214,16 +188,10 @@ const getLocale = (fallback = 'en') => { return Object.keys(messages).includes(localeWithVariant) ? localeWithVariant : Object.keys(messages).includes(locale) ? locale : fallback; }; -type SettingsAction = - | SettingChangeAction - export { - SETTING_CHANGE, FE_NAME, - changeSettingImmediate, changeSetting, saveSettings, updateSettingsStore, getLocale, - type SettingsAction, }; diff --git a/packages/pl-fe/src/features/developers/developers-challenge.tsx b/packages/pl-fe/src/features/developers/developers-challenge.tsx index 8c1f53eb6..69cb18ceb 100644 --- a/packages/pl-fe/src/features/developers/developers-challenge.tsx +++ b/packages/pl-fe/src/features/developers/developers-challenge.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { changeSettingImmediate } from 'pl-fe/actions/settings'; +import { changeSetting } from 'pl-fe/actions/settings'; import { Column, Button, Form, FormActions, FormGroup, Input, Text } from 'pl-fe/components/ui'; import { useAppDispatch } from 'pl-fe/hooks'; import toast from 'pl-fe/toast'; @@ -26,7 +26,7 @@ const DevelopersChallenge = () => { const handleSubmit = () => { if (answer === 'fe-pl') { - dispatch(changeSettingImmediate(['isDeveloper'], true)); + dispatch(changeSetting(['isDeveloper'], true)); toast.success(intl.formatMessage(messages.success)); } else { toast.error(intl.formatMessage(messages.fail)); diff --git a/packages/pl-fe/src/features/developers/developers-menu.tsx b/packages/pl-fe/src/features/developers/developers-menu.tsx index 679042604..234e8fa46 100644 --- a/packages/pl-fe/src/features/developers/developers-menu.tsx +++ b/packages/pl-fe/src/features/developers/developers-menu.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { Link, useHistory } from 'react-router-dom'; -import { changeSettingImmediate } from 'pl-fe/actions/settings'; +import { changeSetting } from 'pl-fe/actions/settings'; import { Column, Text } from 'pl-fe/components/ui'; import SvgIcon from 'pl-fe/components/ui/icon/svg-icon'; import { useAppDispatch } from 'pl-fe/hooks'; @@ -38,7 +38,7 @@ const Developers: React.FC = () => { const leaveDevelopers = (e: React.MouseEvent) => { e.preventDefault(); - dispatch(changeSettingImmediate(['isDeveloper'], false)); + dispatch(changeSetting(['isDeveloper'], false)); toast.success(intl.formatMessage(messages.leave)); history.push('/'); }; diff --git a/packages/pl-fe/src/features/status/components/status-type-icon.tsx b/packages/pl-fe/src/features/status/components/status-type-icon.tsx index b39eab085..7b67a3718 100644 --- a/packages/pl-fe/src/features/status/components/status-type-icon.tsx +++ b/packages/pl-fe/src/features/status/components/status-type-icon.tsx @@ -22,8 +22,8 @@ const STATUS_TYPE_ICONS: Record = { direct: require('@tabler/icons/outline/mail.svg'), private: require('@tabler/icons/outline/lock.svg'), mutuals_only: require('@tabler/icons/outline/users-group.svg'), - local: require('@tabler/icons/outline/affiliate.svg'), - list: require('@tabler/icons/outline/list.svg'), + local: require('@tabler/icons/outline/affiliate.svg'), + list: require('@tabler/icons/outline/list.svg'), }; const StatusTypeIcon: React.FC = ({ status }) => { diff --git a/packages/pl-fe/src/globals.ts b/packages/pl-fe/src/globals.ts index 6b3a9af99..6a14cd9be 100644 --- a/packages/pl-fe/src/globals.ts +++ b/packages/pl-fe/src/globals.ts @@ -2,7 +2,7 @@ * globals: do things through the console. * This feature is for developers. */ -import { changeSettingImmediate } from 'pl-fe/actions/settings'; +import { changeSetting } from 'pl-fe/actions/settings'; import type { Store } from 'pl-fe/store'; @@ -14,7 +14,7 @@ const createGlobals = (store: Store) => { if (![true, false].includes(bool)) { throw `Invalid option ${bool}. Must be true or false.`; } - store.dispatch(changeSettingImmediate(['isDeveloper'], bool) as any); + store.dispatch(changeSetting(['isDeveloper'], bool) as any); return bool; }, }; diff --git a/packages/pl-fe/src/reducers/compose.ts b/packages/pl-fe/src/reducers/compose.ts index 8a262974e..665f23575 100644 --- a/packages/pl-fe/src/reducers/compose.ts +++ b/packages/pl-fe/src/reducers/compose.ts @@ -62,7 +62,7 @@ import { } from '../actions/compose'; import { EVENT_COMPOSE_CANCEL, EVENT_FORM_SET, type EventsAction } from '../actions/events'; import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS, MeAction } from '../actions/me'; -import { SETTING_CHANGE, FE_NAME, SettingsAction } from '../actions/settings'; +import { FE_NAME } from '../actions/settings'; import { TIMELINE_DELETE, TimelineAction } from '../actions/timelines'; import { unescapeHTML } from '../utils/html'; @@ -263,17 +263,17 @@ const importAccount = (compose: Compose, account: CredentialAccount) => { }); }; -const updateSetting = (compose: Compose, path: string[], value: string) => { - const pathString = path.join(','); - switch (pathString) { - case 'defaultPrivacy': - return compose.set('privacy', value); - case 'defaultContentType': - return compose.set('content_type', value); - default: - return compose; - } -}; +// const updateSetting = (compose: Compose, path: string[], value: string) => { +// const pathString = path.join(','); +// switch (pathString) { +// case 'defaultPrivacy': +// return compose.set('privacy', value); +// case 'defaultContentType': +// return compose.set('content_type', value); +// default: +// return compose; +// } +// }; const updateCompose = (state: State, key: string, updater: (compose: Compose) => Compose) => state.update(key, state.get('default')!, updater); @@ -282,7 +282,7 @@ const initialState: State = ImmutableMap({ default: ReducerCompose({ idempotencyKey: crypto.randomUUID(), resetFileKey: getResetFileKey() }), }); -const compose = (state = initialState, action: ComposeAction | EventsAction | MeAction | SettingsAction | TimelineAction) => { +const compose = (state = initialState, action: ComposeAction | EventsAction | MeAction | TimelineAction) => { switch (action.type) { case COMPOSE_TYPE_CHANGE: return updateCompose(state, action.composeId, compose => compose.withMutations(map => { @@ -544,8 +544,8 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | Me case ME_FETCH_SUCCESS: case ME_PATCH_SUCCESS: return updateCompose(state, 'default', compose => importAccount(compose, action.me)); - case SETTING_CHANGE: - return updateCompose(state, 'default', compose => updateSetting(compose, action.path, action.value)); + // case SETTING_CHANGE: + // return updateCompose(state, 'default', compose => updateSetting(compose, action.path, action.value)); case COMPOSE_EDITOR_STATE_SET: return updateCompose(state, action.composeId, compose => compose .setIn(!compose.modified_language || compose.modified_language === compose.language ? ['editorState'] : ['editorStateMap', compose.modified_language], action.editorState as string) diff --git a/packages/pl-fe/src/reducers/index.ts b/packages/pl-fe/src/reducers/index.ts index 2b4d0f49b..110fdacee 100644 --- a/packages/pl-fe/src/reducers/index.ts +++ b/packages/pl-fe/src/reducers/index.ts @@ -37,7 +37,6 @@ import push_notifications from './push-notifications'; import scheduled_statuses from './scheduled-statuses'; import search from './search'; import security from './security'; -// import settings from './settings'; import status_lists from './status-lists'; import statuses from './statuses'; import suggestions from './suggestions'; @@ -81,7 +80,6 @@ const reducers = { scheduled_statuses, search, security, - // settings, status_lists, statuses, suggestions, diff --git a/packages/pl-fe/src/reducers/settings.test.ts b/packages/pl-fe/src/reducers/settings.test.ts deleted file mode 100644 index 1623a9a96..000000000 --- a/packages/pl-fe/src/reducers/settings.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from './settings'; - -describe('settings reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap({ - saved: true, - })); - }); -}); diff --git a/packages/pl-fe/src/reducers/settings.ts b/packages/pl-fe/src/reducers/settings.ts deleted file mode 100644 index 888f9866f..000000000 --- a/packages/pl-fe/src/reducers/settings.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { produce } from 'immer'; -import { AnyAction } from 'redux'; - -import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings'; - -import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications'; -import { SEARCH_FILTER_SET } from '../actions/search'; -import { SETTING_CHANGE } from '../actions/settings'; - -type State = Partial; - -// Default settings are in action/settings.js -// -// Settings should be accessed with `getSettings(getState()).getIn(...)` -// instead of directly from the state. -const settings = ( - state: State = settingsSchema.partial().parse({}), - action: AnyAction, -): State => { - switch (action.type) { - case NOTIFICATIONS_FILTER_SET: - case SEARCH_FILTER_SET: - case SETTING_CHANGE: - return produce(state, draft => { - // @ts-ignore - draft[action.path] = action.value; - draft.saved = false; - }); - default: - return state; - } -}; - -export { settings as default }; diff --git a/packages/pl-fe/src/stores/settings.ts b/packages/pl-fe/src/stores/settings.ts index 018d2aa3c..2b76c4da5 100644 --- a/packages/pl-fe/src/stores/settings.ts +++ b/packages/pl-fe/src/stores/settings.ts @@ -17,10 +17,21 @@ type State = { loadDefaultSettings: (settings: APIEntity) => void; loadUserSettings: (settings: APIEntity) => void; userSettingsSaving: () => void; + changeSetting: (path: string[], value: any) => void; rememberEmojiUse: (emoji: Emoji) => void; rememberLanguageUse: (language: string) => void; } +const changeSetting = (object: APIEntity, path: string[], value: any) => { + if (path.length === 1) { + object[path[0]] = value; + return; + } + + if (typeof object[path[0]] !== 'object') object[path[0]] = {}; + return changeSetting(object[path[0]], path.slice(1), value); +}; + const mergeSettings = (state: State) => state.settings = { ...state.defaultSettings, ...state.userSettings }; const useSettingsStore = create((set) => ({ @@ -49,6 +60,12 @@ const useSettingsStore = create((set) => ({ mergeSettings(state); })), + changeSetting: (path: string[], value: any) => set(produce((state: State) => { + changeSetting(state.userSettings, path, value); + + mergeSettings(state); + })), + rememberEmojiUse: (emoji: Emoji) => set(produce((state: State) => { const settings = state.userSettings; if (!settings.frequentlyUsedEmojis) settings.frequentlyUsedEmojis = {};