pl-fe: WIP migrate settings store to zustand
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@@ -10,7 +10,6 @@ import { blockDomain, unblockDomain } from 'pl-fe/actions/domain-blocks';
|
||||
import { initMuteModal } from 'pl-fe/actions/mutes';
|
||||
import { initReport, ReportableEntities } from 'pl-fe/actions/reports';
|
||||
import { setSearchAccount } from 'pl-fe/actions/search';
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { useFollow } from 'pl-fe/api/hooks';
|
||||
import Badge from 'pl-fe/components/badge';
|
||||
import DropdownMenu, { Menu } from 'pl-fe/components/dropdown-menu';
|
||||
@@ -24,6 +23,7 @@ import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'pl-f
|
||||
import { useChats } from 'pl-fe/queries/chats';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
import toast from 'pl-fe/toast';
|
||||
import { isDefaultHeader } from 'pl-fe/utils/accounts';
|
||||
import copy from 'pl-fe/utils/copy';
|
||||
@@ -90,6 +90,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||
const { account: ownAccount } = useOwnAccount();
|
||||
const { follow } = useFollow();
|
||||
const { openModal } = useModalsStore();
|
||||
const { settings } = useSettingsStore();
|
||||
|
||||
const { software } = useAppSelector((state) => state.auth.client.features.version);
|
||||
|
||||
@@ -221,19 +222,17 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||
};
|
||||
|
||||
const onRemoveFromFollowers = () => {
|
||||
dispatch((_, getState) => {
|
||||
const unfollowModal = getSettings(getState()).get('unfollowModal');
|
||||
if (unfollowModal) {
|
||||
openModal('CONFIRM', {
|
||||
heading: <FormattedMessage id='confirmations.remove_from_followers.heading' defaultMessage='Remove {name} from followers' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
||||
message: <FormattedMessage id='confirmations.remove_from_followers.message' defaultMessage='Are you sure you want to remove {name} from your followers?' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
||||
confirm: intl.formatMessage(messages.removeFromFollowersConfirm),
|
||||
onConfirm: () => dispatch(removeFromFollowers(account.id)),
|
||||
});
|
||||
} else {
|
||||
dispatch(removeFromFollowers(account.id));
|
||||
}
|
||||
});
|
||||
const unfollowModal = settings.unfollowModal;
|
||||
if (unfollowModal) {
|
||||
openModal('CONFIRM', {
|
||||
heading: <FormattedMessage id='confirmations.remove_from_followers.heading' defaultMessage='Remove {name} from followers' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
||||
message: <FormattedMessage id='confirmations.remove_from_followers.message' defaultMessage='Are you sure you want to remove {name} from your followers?' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
||||
confirm: intl.formatMessage(messages.removeFromFollowersConfirm),
|
||||
onConfirm: () => dispatch(removeFromFollowers(account.id)),
|
||||
});
|
||||
} else {
|
||||
dispatch(removeFromFollowers(account.id));
|
||||
}
|
||||
};
|
||||
|
||||
const onSearch = () => {
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import clsx from 'clsx';
|
||||
import fuzzysort from 'fuzzysort';
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { addComposeLanguage, changeComposeLanguage, changeComposeModifiedLanguage, deleteComposeLanguage } from 'pl-fe/actions/compose';
|
||||
import DropdownMenu from 'pl-fe/components/dropdown-menu';
|
||||
import { Button, Icon, Input } from 'pl-fe/components/ui';
|
||||
import { type Language, languages as languagesObject } from 'pl-fe/features/preferences';
|
||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures } from 'pl-fe/hooks';
|
||||
import { useAppDispatch, useCompose, useFeatures, useSettings } from 'pl-fe/hooks';
|
||||
|
||||
const getFrequentlyUsedLanguages = createSelector([
|
||||
state => state.settings.get('frequentlyUsedLanguages', ImmutableMap()),
|
||||
], (languageCounters: ImmutableMap<Language, number>) => (
|
||||
languageCounters.keySeq()
|
||||
.sort((a, b) => languageCounters.get(a, 0) - languageCounters.get(b, 0))
|
||||
.reverse()
|
||||
.toArray()
|
||||
));
|
||||
const getFrequentlyUsedLanguages = (languageCounters: Record<string, number>) => (
|
||||
Object.keys(languageCounters)
|
||||
.toSorted((a, b) => languageCounters[a] - languageCounters[b])
|
||||
.toReversed()
|
||||
);
|
||||
|
||||
const languages = Object.entries(languagesObject) as Array<[Language, string]>;
|
||||
|
||||
@@ -39,7 +34,8 @@ const getLanguageDropdown = (composeId: string): React.FC<ILanguageDropdown> =>
|
||||
const intl = useIntl();
|
||||
const features = useFeatures();
|
||||
const dispatch = useAppDispatch();
|
||||
const frequentlyUsedLanguages = useAppSelector(getFrequentlyUsedLanguages);
|
||||
const settings = useSettings();
|
||||
const frequentlyUsedLanguages = useMemo(() => getFrequentlyUsedLanguages(settings.frequentlyUsedLanguages), [settings.frequentlyUsedLanguages]);
|
||||
|
||||
const node = useRef<HTMLDivElement>(null);
|
||||
const focusedItem = useRef<HTMLButtonElement>(null);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { useAppSelector } from 'pl-fe/hooks';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
|
||||
import DevelopersChallenge from './developers-challenge';
|
||||
import DevelopersMenu from './developers-menu';
|
||||
|
||||
const Developers: React.FC = () => {
|
||||
const isDeveloper = useAppSelector((state) => getSettings(state).get('isDeveloper'));
|
||||
const { isDeveloper } = useSettingsStore().settings;
|
||||
|
||||
return isDeveloper ? <DevelopersMenu /> : <DevelopersChallenge />;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||
|
||||
import { SETTINGS_UPDATE, changeSetting, updateSettingsStore } from 'pl-fe/actions/settings';
|
||||
import { changeSetting, updateSettingsStore } from 'pl-fe/actions/settings';
|
||||
import List, { ListItem } from 'pl-fe/components/list';
|
||||
import {
|
||||
CardHeader,
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
Textarea,
|
||||
} from 'pl-fe/components/ui';
|
||||
import SettingToggle from 'pl-fe/features/notifications/components/setting-toggle';
|
||||
import { useAppSelector, useAppDispatch, useSettings } from 'pl-fe/hooks';
|
||||
import { useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
import toast from 'pl-fe/toast';
|
||||
|
||||
const isJSONValid = (text: any): boolean => {
|
||||
@@ -35,10 +36,9 @@ const messages = defineMessages({
|
||||
const SettingsStore: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const settings = useSettings();
|
||||
const settingsStore = useAppSelector(state => state.settings);
|
||||
const { settings, userSettings, loadUserSettings } = useSettingsStore();
|
||||
|
||||
const [rawJSON, setRawJSON] = useState<string>(JSON.stringify(settingsStore, null, 2));
|
||||
const [rawJSON, setRawJSON] = useState<string>(JSON.stringify(userSettings, null, 2));
|
||||
const [jsonValid, setJsonValid] = useState(true);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
@@ -57,7 +57,7 @@ const SettingsStore: React.FC = () => {
|
||||
|
||||
setLoading(true);
|
||||
dispatch(updateSettingsStore(settings)).then(() => {
|
||||
dispatch({ type: SETTINGS_UPDATE, settings });
|
||||
loadUserSettings(settings);
|
||||
setLoading(false);
|
||||
}).catch(error => {
|
||||
toast.showAlertForError(error);
|
||||
@@ -66,9 +66,9 @@ const SettingsStore: React.FC = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setRawJSON(JSON.stringify(settingsStore, null, 2));
|
||||
setRawJSON(JSON.stringify(userSettings, null, 2));
|
||||
setJsonValid(true);
|
||||
}, [settingsStore]);
|
||||
}, [userSettings]);
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)} backHref='/developers'>
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { useAccount } from 'pl-fe/api/hooks';
|
||||
import Account from 'pl-fe/components/account';
|
||||
import Badge from 'pl-fe/components/badge';
|
||||
@@ -12,6 +11,7 @@ import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
|
||||
import { Avatar, Stack, Text } from 'pl-fe/components/ui';
|
||||
import ActionButton from 'pl-fe/features/ui/components/action-button';
|
||||
import { useAppSelector } from 'pl-fe/hooks';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
import { shortNumberFormat } from 'pl-fe/utils/numbers';
|
||||
|
||||
interface IAccountCard {
|
||||
@@ -21,7 +21,7 @@ interface IAccountCard {
|
||||
const AccountCard: React.FC<IAccountCard> = ({ id }) => {
|
||||
const me = useAppSelector((state) => state.me);
|
||||
const { account } = useAccount(id);
|
||||
const autoPlayGif = useAppSelector((state) => getSettings(state).get('autoPlayGif'));
|
||||
const { autoPlayGif } = useSettingsStore().settings;
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { setComposeToStatus } from 'pl-fe/actions/compose';
|
||||
import { cancelDraftStatus } from 'pl-fe/actions/draft-statuses';
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { Button, HStack } from 'pl-fe/components/ui';
|
||||
import { useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
|
||||
import type { Status as StatusEntity } from 'pl-fe/normalizers';
|
||||
import type { DraftStatus } from 'pl-fe/reducers/draft-statuses';
|
||||
@@ -26,12 +26,13 @@ const DraftStatusActionBar: React.FC<IDraftStatusActionBar> = ({ source, status
|
||||
const intl = useIntl();
|
||||
|
||||
const { openModal } = useModalsStore();
|
||||
const { settings } = useSettingsStore();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleCancelClick = () => {
|
||||
dispatch((_, getState) => {
|
||||
|
||||
const deleteModal = getSettings(getState()).get('deleteModal');
|
||||
const deleteModal = settings.deleteModal;
|
||||
if (!deleteModal) {
|
||||
dispatch(cancelDraftStatus(source.draft_id));
|
||||
} else {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
import React, { useEffect, useState, useLayoutEffect, Suspense } from 'react';
|
||||
import React, { useEffect, useState, useLayoutEffect, Suspense, useMemo } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { chooseEmoji } from 'pl-fe/actions/emojis';
|
||||
import { changeSetting } from 'pl-fe/actions/settings';
|
||||
import { useAppDispatch, useAppSelector, useTheme } from 'pl-fe/hooks';
|
||||
import { useAppDispatch, useAppSelector, useSettings, useTheme } from 'pl-fe/hooks';
|
||||
import { RootState } from 'pl-fe/store';
|
||||
|
||||
import { buildCustomEmojis } from '../../emoji';
|
||||
@@ -71,15 +70,11 @@ const DEFAULTS = [
|
||||
'ok_hand',
|
||||
];
|
||||
|
||||
const getFrequentlyUsedEmojis = createSelector([
|
||||
(state: RootState) => state.settings.get('frequentlyUsedEmojis', ImmutableMap()),
|
||||
], (emojiCounters: ImmutableMap<string, number>) => {
|
||||
let emojis = emojiCounters
|
||||
.keySeq()
|
||||
.sort((a, b) => emojiCounters.get(a)! - emojiCounters.get(b)!)
|
||||
.reverse()
|
||||
.slice(0, perLine * lines)
|
||||
.toArray();
|
||||
const getFrequentlyUsedEmojis = (emojiCounters: Record<string, number>) => {
|
||||
let emojis = Object.keys(emojiCounters)
|
||||
.toSorted((a, b) => emojiCounters[a] - emojiCounters[b])
|
||||
.toReversed()
|
||||
.slice(0, perLine * lines);
|
||||
|
||||
if (emojis.length < DEFAULTS.length) {
|
||||
const uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji));
|
||||
@@ -87,7 +82,7 @@ const getFrequentlyUsedEmojis = createSelector([
|
||||
}
|
||||
|
||||
return emojis;
|
||||
});
|
||||
};
|
||||
|
||||
const getCustomEmojis = createSelector([
|
||||
(state: RootState) => state.custom_emojis,
|
||||
@@ -132,7 +127,9 @@ const EmojiPickerDropdown: React.FC<IEmojiPickerDropdown> = ({
|
||||
const theme = useTheme();
|
||||
|
||||
const customEmojis = useAppSelector((state) => getCustomEmojis(state));
|
||||
const frequentlyUsedEmojis = useAppSelector((state) => getFrequentlyUsedEmojis(state));
|
||||
|
||||
const settings = useSettings();
|
||||
const frequentlyUsedEmojis = useMemo(() => getFrequentlyUsedEmojis(settings.frequentlyUsedEmojis), [settings.frequentlyUsedEmojis]);
|
||||
|
||||
const handlePick = (emoji: any) => {
|
||||
setVisible(false);
|
||||
@@ -238,6 +235,5 @@ const EmojiPickerDropdown: React.FC<IEmojiPickerDropdown> = ({
|
||||
export {
|
||||
messages,
|
||||
type IEmojiPickerDropdown,
|
||||
getFrequentlyUsedEmojis,
|
||||
EmojiPickerDropdown as default,
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Link, useHistory } from 'react-router-dom';
|
||||
|
||||
import { mentionCompose } from 'pl-fe/actions/compose';
|
||||
import { reblog, favourite, unreblog, unfavourite } from 'pl-fe/actions/interactions';
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
|
||||
import Icon from 'pl-fe/components/icon';
|
||||
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
|
||||
@@ -15,6 +14,7 @@ import { HotKeys } from 'pl-fe/features/ui/components/hotkeys';
|
||||
import { useAppDispatch, useAppSelector, useInstance, useLoggedIn } from 'pl-fe/hooks';
|
||||
import { makeGetNotification } from 'pl-fe/selectors';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
import { NotificationType } from 'pl-fe/utils/notification';
|
||||
|
||||
import type { Notification as BaseNotification } from 'pl-api';
|
||||
@@ -196,6 +196,7 @@ const Notification: React.FC<INotification> = (props) => {
|
||||
|
||||
const { me } = useLoggedIn();
|
||||
const { openModal } = useModalsStore();
|
||||
const { settings } = useSettingsStore();
|
||||
const notification = useAppSelector((state) => getNotification(state, props.notification));
|
||||
|
||||
const history = useHistory();
|
||||
@@ -252,23 +253,21 @@ const Notification: React.FC<INotification> = (props) => {
|
||||
|
||||
const handleHotkeyBoost = useCallback((e?: KeyboardEvent) => {
|
||||
if (status && typeof status === 'object') {
|
||||
dispatch((_, getState) => {
|
||||
const boostModal = getSettings(getState()).get('boostModal');
|
||||
if (status.reblogged) {
|
||||
dispatch(unreblog(status));
|
||||
const boostModal = settings.boostModal;
|
||||
if (status.reblogged) {
|
||||
dispatch(unreblog(status));
|
||||
} else {
|
||||
if (e?.shiftKey || !boostModal) {
|
||||
dispatch(reblog(status));
|
||||
} else {
|
||||
if (e?.shiftKey || !boostModal) {
|
||||
dispatch(reblog(status));
|
||||
} else {
|
||||
openModal('BOOST', {
|
||||
statusId: status.id,
|
||||
onReblog: (status) => {
|
||||
dispatch(reblog(status));
|
||||
},
|
||||
});
|
||||
}
|
||||
openModal('BOOST', {
|
||||
statusId: status.id,
|
||||
onReblog: (status) => {
|
||||
dispatch(reblog(status));
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import clsx from 'clsx';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { defaultSettings } from 'pl-fe/actions/settings';
|
||||
import BackgroundShapes from 'pl-fe/features/ui/components/background-shapes';
|
||||
import { useSystemTheme } from 'pl-fe/hooks';
|
||||
import { normalizePlFeConfig } from 'pl-fe/normalizers';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
import { generateThemeCss } from 'pl-fe/utils/theme';
|
||||
|
||||
interface ISitePreview {
|
||||
@@ -16,9 +16,9 @@ interface ISitePreview {
|
||||
/** Renders a preview of the website's style with the configuration applied. */
|
||||
const SitePreview: React.FC<ISitePreview> = ({ plFe }) => {
|
||||
const plFeConfig = useMemo(() => normalizePlFeConfig(plFe), [plFe]);
|
||||
const settings = defaultSettings.mergeDeep(plFeConfig.defaultSettings);
|
||||
const { defaultSettings } = useSettingsStore();
|
||||
|
||||
const userTheme = settings.get('themeMode');
|
||||
const userTheme = defaultSettings.themeMode;
|
||||
const systemTheme = useSystemTheme();
|
||||
|
||||
const dark = ['dark', 'black'].includes(userTheme as string) || (userTheme === 'system' && systemTheme === 'black');
|
||||
|
||||
@@ -2,10 +2,10 @@ import React from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { cancelScheduledStatus } from 'pl-fe/actions/scheduled-statuses';
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { Button, HStack } from 'pl-fe/components/ui';
|
||||
import { useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
|
||||
import type { Status as StatusEntity } from 'pl-fe/normalizers';
|
||||
|
||||
@@ -25,11 +25,12 @@ const ScheduledStatusActionBar: React.FC<IScheduledStatusActionBar> = ({ status
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { openModal } = useModalsStore();
|
||||
const { settings } = useSettingsStore();
|
||||
|
||||
const handleCancelClick = () => {
|
||||
dispatch((_, getState) => {
|
||||
|
||||
const deleteModal = getSettings(getState()).get('deleteModal');
|
||||
const deleteModal = settings.deleteModal;
|
||||
if (!deleteModal) {
|
||||
dispatch(cancelScheduledStatus(status.id));
|
||||
} else {
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { type ComposeReplyAction, mentionCompose, replyCompose } from 'pl-fe/actions/compose';
|
||||
import { reblog, toggleFavourite, unreblog } from 'pl-fe/actions/interactions';
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import StatusActionBar from 'pl-fe/components/status-action-bar';
|
||||
@@ -20,6 +19,7 @@ import PendingStatus from 'pl-fe/features/ui/components/pending-status';
|
||||
import { useAppDispatch, useAppSelector } from 'pl-fe/hooks';
|
||||
import { RootState } from 'pl-fe/store';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
import { textForScreenReader } from 'pl-fe/utils/status';
|
||||
|
||||
import DetailedStatus from './detailed-status';
|
||||
@@ -93,6 +93,7 @@ const Thread: React.FC<IThread> = ({
|
||||
const intl = useIntl();
|
||||
|
||||
const { openModal } = useModalsStore();
|
||||
const { settings } = useSettingsStore();
|
||||
|
||||
const { ancestorsIds, descendantsIds } = useAppSelector((state) => {
|
||||
let ancestorsIds = ImmutableOrderedSet<string>();
|
||||
@@ -136,7 +137,7 @@ const Thread: React.FC<IThread> = ({
|
||||
|
||||
const handleReblogClick = (status: SelectedStatus, e?: React.MouseEvent) => {
|
||||
dispatch((_, getState) => {
|
||||
const boostModal = getSettings(getState()).get('boostModal');
|
||||
const boostModal = settings.boostModal;
|
||||
if (status.reblogged) {
|
||||
dispatch(unreblog(status));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user