pl-fe: Complete migration of settings store

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-10-07 18:37:50 +02:00
parent e4615b70f7
commit 97d1ce2f47
12 changed files with 51 additions and 110 deletions

View file

@ -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());
};

View file

@ -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,
});
};

View file

@ -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,
};

View file

@ -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));

View file

@ -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<HTMLButtonElement>) => {
e.preventDefault();
dispatch(changeSettingImmediate(['isDeveloper'], false));
dispatch(changeSetting(['isDeveloper'], false));
toast.success(intl.formatMessage(messages.leave));
history.push('/');
};

View file

@ -22,8 +22,8 @@ const STATUS_TYPE_ICONS: Record<string, string> = {
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<IStatusTypeIcon> = ({ status }) => {

View file

@ -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;
},
};

View file

@ -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)

View file

@ -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,

View file

@ -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,
}));
});
});

View file

@ -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<Settings>;
// 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 };

View file

@ -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<State>((set) => ({
@ -49,6 +60,12 @@ const useSettingsStore = create<State>((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 = {};