From 817843c77a181bd5061985003df9415ffcf6ec51 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 6 Jul 2021 13:06:21 -0500 Subject: [PATCH 1/2] Performance: memoize getSoapboxConfig(), getSettings(), and getFeatures() --- app/soapbox/actions/settings.js | 11 +++++++---- app/soapbox/actions/soapbox.js | 14 +++++++------- app/soapbox/utils/features.js | 10 ++++++---- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/app/soapbox/actions/settings.js b/app/soapbox/actions/settings.js index e0612f75a..5c0cfe47b 100644 --- a/app/soapbox/actions/settings.js +++ b/app/soapbox/actions/settings.js @@ -4,6 +4,7 @@ import { patchMe } from 'soapbox/actions/me'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { isLoggedIn } from 'soapbox/utils/auth'; import uuid from '../uuid'; +import { createSelector } from 'reselect'; export const SETTING_CHANGE = 'SETTING_CHANGE'; export const SETTING_SAVE = 'SETTING_SAVE'; @@ -136,12 +137,14 @@ export const defaultSettings = ImmutableMap({ ]), }); -export function getSettings(state) { - const soapboxSettings = state.getIn(['soapbox', 'defaultSettings']); +export const getSettings = createSelector([ + state => state.getIn(['soapbox', 'defaultSettings']), + state => state.get('settings'), +], (soapboxSettings, settings) => { return defaultSettings .mergeDeep(soapboxSettings) - .mergeDeep(state.get('settings')); -} + .mergeDeep(settings); +}); export function changeSetting(path, value) { return dispatch => { diff --git a/app/soapbox/actions/soapbox.js b/app/soapbox/actions/soapbox.js index 015ab0049..89461da40 100644 --- a/app/soapbox/actions/soapbox.js +++ b/app/soapbox/actions/soapbox.js @@ -1,6 +1,7 @@ import api from '../api'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { getFeatures } from 'soapbox/utils/features'; +import { createSelector } from 'reselect'; export const SOAPBOX_CONFIG_REQUEST_SUCCESS = 'SOAPBOX_CONFIG_REQUEST_SUCCESS'; export const SOAPBOX_CONFIG_REQUEST_FAIL = 'SOAPBOX_CONFIG_REQUEST_FAIL'; @@ -50,20 +51,19 @@ export const defaultConfig = ImmutableMap({ aboutPages: ImmutableMap(), }); -export function getSoapboxConfig(state) { - const instance = state.get('instance'); - const soapbox = state.get('soapbox'); - const features = getFeatures(instance); - +export const getSoapboxConfig = createSelector([ + state => state.get('soapbox'), + state => getFeatures(state.get('instance')).emojiReactsRGI, +], (soapbox, emojiReactsRGI) => { // https://git.pleroma.social/pleroma/pleroma/-/issues/2355 - if (features.emojiReactsRGI) { + if (emojiReactsRGI) { return defaultConfig .set('allowedEmoji', allowedEmojiRGI) .merge(soapbox); } else { return defaultConfig.merge(soapbox); } -} +}); export function fetchSoapboxConfig() { return (dispatch, getState) => { diff --git a/app/soapbox/utils/features.js b/app/soapbox/utils/features.js index 59d2767d8..5ec07ae87 100644 --- a/app/soapbox/utils/features.js +++ b/app/soapbox/utils/features.js @@ -1,10 +1,12 @@ // Detect backend features to conditionally render elements import gte from 'semver/functions/gte'; import { List as ImmutableList } from 'immutable'; +import { createSelector } from 'reselect'; -export const getFeatures = instance => { - const v = parseVersion(instance.get('version')); - const f = instance.getIn(['pleroma', 'metadata', 'features'], ImmutableList()); +export const getFeatures = createSelector([ + instance => parseVersion(instance.get('version')), + instance => instance.getIn(['pleroma', 'metadata', 'features'], ImmutableList()), +], (v, f) => { return { suggestions: v.software === 'Mastodon' && gte(v.compatVersion, '2.4.3'), trends: v.software === 'Mastodon' && gte(v.compatVersion, '3.0.0'), @@ -15,7 +17,7 @@ export const getFeatures = instance => { importMutes: v.software === 'Pleroma' && gte(v.version, '2.2.0'), emailList: f.includes('email_list'), }; -}; +}); export const parseVersion = version => { let regex = /^([\w\.]*)(?: \(compatible; ([\w]*) (.*)\))?$/; From 569c6e83ab732baee0fe51e42268d56d811a4e6c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 6 Jul 2021 13:07:54 -0500 Subject: [PATCH 2/2] Normalize chat panes (again), fixes #648 --- .../features/chats/components/chat_panes.js | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/app/soapbox/features/chats/components/chat_panes.js b/app/soapbox/features/chats/components/chat_panes.js index b1672c127..ddd0338c7 100644 --- a/app/soapbox/features/chats/components/chat_panes.js +++ b/app/soapbox/features/chats/components/chat_panes.js @@ -10,18 +10,39 @@ import { openChat, toggleMainWindow } from 'soapbox/actions/chats'; import ChatWindow from './chat_window'; import { shortNumberFormat } from 'soapbox/utils/numbers'; import AudioToggle from 'soapbox/features/chats/components/audio_toggle'; +import { List as ImmutableList } from 'immutable'; +import { createSelector } from 'reselect'; -const mapStateToProps = state => { - const settings = getSettings(state); - - return { - panes: settings.getIn(['chats', 'panes']), - mainWindowState: settings.getIn(['chats', 'mainWindow']), - unreadCount: state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), - }; +const getChatsUnreadCount = state => { + const chats = state.get('chats'); + return chats.reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0); }; -export default @connect(mapStateToProps) +// Filter out invalid chats +const normalizePanes = (chats, panes = ImmutableList()) => ( + panes.filter(pane => chats.get(pane.get('chat_id'))) +); + +const makeNormalizeChatPanes = () => createSelector([ + state => state.get('chats'), + state => getSettings(state).getIn(['chats', 'panes']), +], normalizePanes); + +const makeMapStateToProps = () => { + const mapStateToProps = state => { + const normalizeChatPanes = makeNormalizeChatPanes(); + + return { + panes: normalizeChatPanes(state), + mainWindowState: getSettings(state).getIn(['chats', 'mainWindow']), + unreadCount: getChatsUnreadCount(state), + }; + }; + + return mapStateToProps; +}; + +export default @connect(makeMapStateToProps) class ChatPanes extends ImmutablePureComponent { static propTypes = {