Skip to content

Commit

Permalink
refactor(components): implement memoized selectors for denormalized c…
Browse files Browse the repository at this point in the history
…onversations (#2743)

* refactor(components): implement memoized selectors

* refactor: remove logs

* refactor(components): implement memoized selectors for denormalized conversations
  • Loading branch information
domw30 authored Mar 5, 2025
1 parent 922a82d commit 03a541f
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 37 deletions.
41 changes: 17 additions & 24 deletions src/apps/feed/components/sidekick/lib/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '../../../../../store/reducer';
import { denormalizeConversations } from '../../../../../store/channels-list';
import { UnreadCount } from './useSidekick';
import { DefaultRoomLabels } from '../../../../../store/channels';
import { denormalizedConversationsSelector } from '../../../../../store/channels-list/selectors';

export const selectSocialChannelsUnreadCounts = createSelector(
[(state: RootState) => denormalizeConversations(state)],
(conversations) => {
return conversations
.filter((c) => c.isSocialChannel && c.zid)
.reduce((acc, channel) => {
acc[channel.zid!] = { total: channel.unreadCount?.total || 0, highlight: channel.unreadCount?.highlight || 0 };
return acc;
}, {} as { [zid: string]: UnreadCount });
}
);
export const selectSocialChannelsUnreadCounts = createSelector([denormalizedConversationsSelector], (conversations) => {
return conversations
.filter((c) => c.isSocialChannel && c.zid)
.reduce((acc, channel) => {
acc[channel.zid!] = { total: channel.unreadCount?.total || 0, highlight: channel.unreadCount?.highlight || 0 };
return acc;
}, {} as { [zid: string]: UnreadCount });
});

export const selectMutedChannels = createSelector(
[(state: RootState) => denormalizeConversations(state)],
(conversations) => {
return conversations
.filter((c) => c.isSocialChannel && c.zid)
.reduce((acc, channel) => {
acc[channel.zid!] = channel.labels?.includes(DefaultRoomLabels.MUTE);
return acc;
}, {} as { [zid: string]: boolean });
}
);
export const selectMutedChannels = createSelector([denormalizedConversationsSelector], (conversations) => {
return conversations
.filter((c) => c.isSocialChannel && c.zid)
.reduce((acc, channel) => {
acc[channel.zid!] = channel.labels?.includes(DefaultRoomLabels.MUTE);
return acc;
}, {} as { [zid: string]: boolean });
});
6 changes: 3 additions & 3 deletions src/components/app-bar/container.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useRouteMatch } from 'react-router-dom';
import { AppBar as AppBarComponent } from './';
import { denormalizeConversations } from '../../store/channels-list';
import { useSelector } from 'react-redux';
import { RootState } from '../../store';
import { DefaultRoomLabels } from '../../store/channels';
import { denormalizedConversationsSelector } from '../../store/channels-list/selectors';

export const AppBar = () => {
const { activeApp, hasUnreadNotifications, hasUnreadHighlights } = useAppBar();
Expand All @@ -21,7 +21,7 @@ const useAppBar = () => {
const match = useRouteMatch('/:app');

const hasUnreadNotifications = useSelector((state: RootState) => {
const conversations = denormalizeConversations(state);
const conversations = denormalizedConversationsSelector(state);
return conversations.some(
(channel) =>
channel.unreadCount?.total > 0 &&
Expand All @@ -31,7 +31,7 @@ const useAppBar = () => {
});

const hasUnreadHighlights = useSelector((state: RootState) => {
const conversations = denormalizeConversations(state);
const conversations = denormalizedConversationsSelector(state);
return conversations.some(
(channel) =>
channel.unreadCount?.highlight > 0 &&
Expand Down
9 changes: 5 additions & 4 deletions src/components/messenger/group-management/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ import { Option } from '../lib/types';
import { GroupManagement } from '.';
import { RootState } from '../../../store/reducer';
import { GroupManagementErrors, EditConversationState } from '../../../store/group-management/types';
import { User, denormalize as denormalizeChannel, openConversation, Channel } from '../../../store/channels';
import { User, openConversation, Channel } from '../../../store/channels';
import { currentUserSelector } from '../../../store/authentication/selectors';
import { MemberManagementDialogContainer } from '../../group-management/member-management-dialog/container';
import { getUserSubHandle } from '../../../lib/user';
import { MemberNetworks } from '../../../store/users/types';
import { searchMyNetworksByName } from '../../../platform-apps/channels/util/api';
import { receiveSearchResults } from '../../../store/users';
import { denormalizeConversations } from '../../../store/channels-list';
import { CreateMessengerConversation } from '../../../store/channels-list/types';
import { createConversation } from '../../../store/create-conversation';
import { openUserProfile } from '../../../store/user-profile';
import { denormalizedChannelSelector } from '../../../store/channels/selectors';
import { denormalizedConversationsSelector } from '../../../store/channels-list/selectors';

export interface PublicProperties {}

Expand Down Expand Up @@ -73,13 +74,13 @@ export class Container extends React.Component<Properties> {
chat: { activeConversationId },
} = state;

const conversation = denormalizeChannel(activeConversationId, state);
const conversation = denormalizedChannelSelector(state, activeConversationId);
const currentUser = currentUserSelector(state);
const conversationAdminIds = conversation?.adminMatrixIds;
const conversationModeratorIds = conversation?.moderatorIds;
const isCurrentUserRoomAdmin = conversationAdminIds?.includes(currentUser?.matrixId) ?? false;
const isCurrentUserRoomModerator = conversationModeratorIds?.includes(currentUser?.id) ?? false;
const existingConversations = denormalizeConversations(state);
const existingConversations = denormalizedConversationsSelector(state);

return {
activeConversationId,
Expand Down
5 changes: 3 additions & 2 deletions src/components/messenger/list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import { connectContainer } from '../../../store/redux-container';
import { RootState } from '../../../store/reducer';
import { Channel, onAddLabel, onRemoveLabel, openConversation, User } from '../../../store/channels';
import { denormalizeConversations } from '../../../store/channels-list';
import { compareDatesDesc } from '../../../lib/date';
import { MemberNetworks } from '../../../store/users/types';
import { searchMyNetworksByName } from '../../../platform-apps/channels/util/api';
Expand Down Expand Up @@ -40,6 +39,7 @@ import { IconPlus } from '@zero-tech/zui/icons';
import { GroupTypeDialog } from './group-details-panel/group-type-dialog';
import { AdminMessageType } from '../../../store/messages';
import { Header } from '../../sidekick/components/header';
import { denormalizedConversationsSelector } from '../../../store/channels-list/selectors';

import { bemClassName } from '../../../lib/bem';
import './styles.scss';
Expand Down Expand Up @@ -94,10 +94,11 @@ export class Container extends React.Component<Properties, State> {
rewards,
} = state;

const conversations = denormalizeConversations(state)
const conversations = denormalizedConversationsSelector(state)
.filter((c) => !c.isSocialChannel)
.map(addLastMessageMeta(state))
.sort(byLastMessageOrCreation);

const userHandle = getUserSubHandle(user?.data?.primaryZID, user?.data?.primaryWalletAddress);
return {
conversations,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { RootState } from '../../../../store/reducer';
import { connectContainer } from '../../../../store/redux-container';
import { openConversation } from '../../../../store/channels';
import { createConversation } from '../../../../store/create-conversation';
import { denormalizeConversations } from '../../../../store/channels-list';
import { Channel } from '../../../../store/channels';
import { config } from '../../../../config';
import { LinkedAccountsPanel } from '.';
import { denormalizedConversationsSelector } from '../../../../store/channels-list/selectors';

export interface PublicProperties {
onClose?: () => void;
Expand All @@ -24,7 +24,7 @@ export interface Properties extends PublicProperties {
export class Container extends React.Component<Properties> {
static mapState(state: RootState) {
const telegramBotUserId = config.telegramBotUserId;
const existingConversations = denormalizeConversations(state);
const existingConversations = denormalizedConversationsSelector(state);

return { telegramBotUserId, existingConversations };
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/notifications-feed/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import React from 'react';
import { RootState } from '../../store/reducer';
import { connectContainer } from '../../store/redux-container';
import { Channel, DefaultRoomLabels } from '../../store/channels';
import { denormalizeConversations } from '../../store/channels-list';
import { IconBell1 } from '@zero-tech/zui/icons';
import { NotificationItem } from './notification-item';
import { Spinner } from '@zero-tech/zui/components/LoadingIndicator';
import { openNotificationConversation } from '../../store/notifications';
import { ToggleGroup } from '@zero-tech/zui/components/ToggleGroup';
import { Panel, PanelBody, PanelHeader, PanelTitle } from '../layout/panel';
import { denormalizedConversationsSelector } from '../../store/channels-list/selectors';

import styles from './styles.module.scss';

Expand Down Expand Up @@ -53,7 +53,7 @@ export class Container extends React.Component<Properties, State> {
chat: { isConversationsLoaded },
} = state;

const conversations = denormalizeConversations(state).filter(
const conversations = denormalizedConversationsSelector(state).filter(
(conversation) =>
(conversation.unreadCount.total > 0 || conversation.unreadCount?.highlight > 0) &&
!conversation.labels?.includes(DefaultRoomLabels.ARCHIVED)
Expand Down
7 changes: 7 additions & 0 deletions src/store/channels-list/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import getDeepProperty from 'lodash.get';
import { denormalize } from '../channels';
import { compareDatesDesc } from '../../lib/date';
import { AsyncListStatus } from '../normalized';
import { createSelector } from 'reselect';
import { denormalizeConversations } from '.';
import { RootState } from '..';

export function channelListStatus(state) {
return getDeepProperty(state, 'channelsList.status', AsyncListStatus.Idle);
Expand Down Expand Up @@ -32,3 +35,7 @@ function byLastMessageOrCreation(a, b) {
const bDate = b.lastMessage?.createdAt || b.createdAt;
return compareDatesDesc(aDate, bDate);
}

export const denormalizedConversationsSelector = createSelector([(state: RootState) => state], (state) => {
return denormalizeConversations(state);
});

0 comments on commit 03a541f

Please sign in to comment.