Always use static/cached selectors in useSelector
This commit is contained in:
parent
d85a1d5074
commit
1e275a917c
13 changed files with 105 additions and 70 deletions
|
@ -10,3 +10,5 @@ export const getHasInitialLoadCompleted = createSelector(
|
|||
getApp,
|
||||
({ hasInitialLoadCompleted }) => hasInitialLoadCompleted
|
||||
);
|
||||
|
||||
export const getAppView = createSelector(getApp, ({ appView }) => appView);
|
||||
|
|
|
@ -208,6 +208,12 @@ export const getTargetedMessage = createSelector(
|
|||
};
|
||||
}
|
||||
);
|
||||
export const getTargetedMessageSource = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): string | undefined => {
|
||||
return state.targetedMessageSource;
|
||||
}
|
||||
);
|
||||
export const getSelectedMessageIds = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): ReadonlyArray<string> | undefined => {
|
||||
|
|
17
ts/state/selectors/inbox.ts
Normal file
17
ts/state/selectors/inbox.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { createSelector } from 'reselect';
|
||||
import type { StateType } from '../reducer';
|
||||
|
||||
const getInboxState = (state: StateType) => state.inbox;
|
||||
|
||||
export const getInboxEnvelopeTimestamp = createSelector(
|
||||
getInboxState,
|
||||
({ envelopeTimestamp }) => envelopeTimestamp
|
||||
);
|
||||
|
||||
export const getInboxFirstEnvelopeTimestamp = createSelector(
|
||||
getInboxState,
|
||||
({ firstEnvelopeTimestamp }) => firstEnvelopeTimestamp
|
||||
);
|
|
@ -25,3 +25,12 @@ export const getContactSafetyNumber = createSelector(
|
|||
contactID: string
|
||||
): SafetyNumberContactType | void => contacts[contactID]
|
||||
);
|
||||
|
||||
export const getContactSafetyNumberSelector = createSelector(
|
||||
[getSafetyNumber],
|
||||
({ contacts }) => {
|
||||
return (contactId: string) => {
|
||||
return contacts[contactId];
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -16,20 +16,20 @@ import {
|
|||
getIsMainWindowFullScreen,
|
||||
} from '../selectors/user';
|
||||
import { hasSelectedStoryData } from '../selectors/stories';
|
||||
import type { StateType } from '../reducer';
|
||||
import { useAppActions } from '../ducks/app';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import { useStoriesActions } from '../ducks/stories';
|
||||
import { ErrorBoundary } from '../../components/ErrorBoundary';
|
||||
import { ModalContainer } from '../../components/ModalContainer';
|
||||
import { SmartInbox } from './Inbox';
|
||||
import { getAppView } from '../selectors/app';
|
||||
|
||||
function renderInbox(): JSX.Element {
|
||||
return <SmartInbox />;
|
||||
}
|
||||
|
||||
export const SmartApp = memo(function SmartApp() {
|
||||
const app = useSelector((state: StateType) => state.app);
|
||||
const appView = useSelector(getAppView);
|
||||
|
||||
const { openInbox } = useAppActions();
|
||||
const { scrollToMessage } = useConversationsActions();
|
||||
|
@ -37,7 +37,7 @@ export const SmartApp = memo(function SmartApp() {
|
|||
|
||||
return (
|
||||
<App
|
||||
{...app}
|
||||
appView={appView}
|
||||
isMaximized={useSelector(getIsMainWindowMaximized)}
|
||||
isFullScreen={useSelector(getIsMainWindowFullScreen)}
|
||||
osClassName={OS.getClassName()}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { usePrevious } from '../../hooks/usePrevious';
|
|||
import { TargetedMessageSource } from '../ducks/conversationsEnums';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import { useToastActions } from '../ducks/toast';
|
||||
import type { StateType } from '../reducer';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import { ToastType } from '../../types/Toast';
|
||||
import { getNavTabsCollapsed } from '../selectors/items';
|
||||
|
@ -21,6 +20,11 @@ import { useItemsActions } from '../ducks/items';
|
|||
import { getHasAnyFailedStorySends } from '../selectors/stories';
|
||||
import { getHasPendingUpdate } from '../selectors/updates';
|
||||
import { getOtherTabsUnreadStats } from '../selectors/nav';
|
||||
import {
|
||||
getSelectedConversationId,
|
||||
getTargetedMessage,
|
||||
getTargetedMessageSource,
|
||||
} from '../selectors/conversations';
|
||||
|
||||
function renderConversationView() {
|
||||
return <SmartConversationView />;
|
||||
|
@ -40,9 +44,9 @@ export const SmartChatsTab = memo(function SmartChatsTab() {
|
|||
const hasFailedStorySends = useSelector(getHasAnyFailedStorySends);
|
||||
const hasPendingUpdate = useSelector(getHasPendingUpdate);
|
||||
const otherTabsUnreadStats = useSelector(getOtherTabsUnreadStats);
|
||||
|
||||
const { selectedConversationId, targetedMessage, targetedMessageSource } =
|
||||
useSelector((state: StateType) => state.conversations);
|
||||
const selectedConversationId = useSelector(getSelectedConversationId);
|
||||
const targetedMessage = useSelector(getTargetedMessage);
|
||||
const targetedMessageSource = useSelector(getTargetedMessageSource);
|
||||
|
||||
const {
|
||||
onConversationClosed,
|
||||
|
@ -60,14 +64,14 @@ export const SmartChatsTab = memo(function SmartChatsTab() {
|
|||
if (selectedConversationId !== lastOpenedConversationId.current) {
|
||||
lastOpenedConversationId.current = selectedConversationId;
|
||||
if (selectedConversationId) {
|
||||
onConversationOpened(selectedConversationId, targetedMessage);
|
||||
onConversationOpened(selectedConversationId, targetedMessage?.id);
|
||||
}
|
||||
} else if (
|
||||
selectedConversationId &&
|
||||
targetedMessage &&
|
||||
targetedMessageSource !== TargetedMessageSource.Focus
|
||||
) {
|
||||
scrollToMessage(selectedConversationId, targetedMessage);
|
||||
scrollToMessage(selectedConversationId, targetedMessage?.id);
|
||||
}
|
||||
}, [onConversationOpened, selectedConversationId, scrollToMessage, targetedMessage, targetedMessageSource]);
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPe
|
|||
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
||||
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
||||
import { isSignalConversation } from '../../util/isSignalConversation';
|
||||
import type { StateType } from '../reducer';
|
||||
import {
|
||||
getErrorDialogAudioRecorderType,
|
||||
getRecordingState,
|
||||
|
@ -163,15 +162,23 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
|
|||
};
|
||||
}
|
||||
|
||||
const quotedMessageProps = useSelector((state: StateType) => {
|
||||
const ourConversationId = useSelector(getUserConversationId);
|
||||
const defaultConversationColor = useSelector(getDefaultConversationColor);
|
||||
|
||||
const quotedMessageProps = useMemo(() => {
|
||||
return quotedMessage
|
||||
? getPropsForQuote(quotedMessage, {
|
||||
conversationSelector,
|
||||
ourConversationId: getUserConversationId(state),
|
||||
defaultConversationColor: getDefaultConversationColor(state),
|
||||
ourConversationId,
|
||||
defaultConversationColor,
|
||||
})
|
||||
: undefined;
|
||||
});
|
||||
}, [
|
||||
quotedMessage,
|
||||
conversationSelector,
|
||||
ourConversationId,
|
||||
defaultConversationColor,
|
||||
]);
|
||||
|
||||
const { putItem, removeItem } = useItemsActions();
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import React, { memo, useMemo } from 'react';
|
|||
import { useSelector } from 'react-redux';
|
||||
import { pick } from 'lodash';
|
||||
import type { ConversationType } from '../ducks/conversations';
|
||||
import type { StateType } from '../reducer';
|
||||
import {
|
||||
ConversationHeader,
|
||||
OutgoingCallButtonStyle,
|
||||
|
@ -19,14 +18,13 @@ import {
|
|||
isMissingRequiredProfileSharing,
|
||||
} from '../selectors/conversations';
|
||||
import { CallMode } from '../../types/Calling';
|
||||
import { getActiveCall, useCallingActions } from '../ducks/calling';
|
||||
import { useCallingActions } from '../ducks/calling';
|
||||
import { isAnybodyElseInGroupCall } from '../ducks/callingHelpers';
|
||||
import {
|
||||
getConversationCallMode,
|
||||
useConversationsActions,
|
||||
} from '../ducks/conversations';
|
||||
import { getHasStoriesSelector } from '../selectors/stories2';
|
||||
import { getOwn } from '../../util/getOwn';
|
||||
import { getUserACI, getIntl, getTheme } from '../selectors/user';
|
||||
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
||||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
|
@ -39,20 +37,21 @@ import { getGroupMemberships } from '../../util/getGroupMemberships';
|
|||
import { isGroupOrAdhocCallState } from '../../util/isGroupOrAdhocCall';
|
||||
import { useContactNameData } from '../../components/conversation/ContactName';
|
||||
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
|
||||
import { getActiveCallState, getCallSelector } from '../selectors/calling';
|
||||
|
||||
export type OwnProps = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const getOutgoingCallButtonStyle = (
|
||||
conversation: ConversationType,
|
||||
state: StateType
|
||||
const useOutgoingCallButtonStyle = (
|
||||
conversation: ConversationType
|
||||
): OutgoingCallButtonStyle => {
|
||||
const { calling } = state;
|
||||
const ourAci = getUserACI(state);
|
||||
strictAssert(ourAci, 'getOutgoingCallButtonStyle missing our uuid');
|
||||
const ourAci = useSelector(getUserACI);
|
||||
const activeCall = useSelector(getActiveCallState);
|
||||
const callSelector = useSelector(getCallSelector);
|
||||
strictAssert(ourAci, 'useOutgoingCallButtonStyle missing our uuid');
|
||||
|
||||
if (getActiveCall(calling)) {
|
||||
if (activeCall != null) {
|
||||
return OutgoingCallButtonStyle.None;
|
||||
}
|
||||
|
||||
|
@ -64,7 +63,7 @@ const getOutgoingCallButtonStyle = (
|
|||
return OutgoingCallButtonStyle.Both;
|
||||
case CallMode.Group:
|
||||
case CallMode.Adhoc: {
|
||||
const call = getOwn(calling.callsByConversation, conversation.id);
|
||||
const call = callSelector(conversation.id);
|
||||
if (
|
||||
isGroupOrAdhocCallState(call) &&
|
||||
isAnybodyElseInGroupCall(call.peekInfo, ourAci)
|
||||
|
@ -94,9 +93,7 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
|
|||
const badge = badgeSelector(conversation.badges);
|
||||
const i18n = useSelector(getIntl);
|
||||
const hasPanelShowing = useSelector(getHasPanelOpen);
|
||||
const outgoingCallButtonStyle = useSelector((state: StateType) => {
|
||||
return getOutgoingCallButtonStyle(conversation, state);
|
||||
});
|
||||
const outgoingCallButtonStyle = useOutgoingCallButtonStyle(conversation);
|
||||
const theme = useSelector(getTheme);
|
||||
|
||||
const {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import type { StateType } from '../reducer';
|
||||
import { ConversationPanel } from './ConversationPanel';
|
||||
import { ConversationView } from '../../components/conversation/ConversationView';
|
||||
import { SmartCompositionArea } from './CompositionArea';
|
||||
|
@ -17,6 +16,7 @@ import {
|
|||
} from '../selectors/conversations';
|
||||
import { useComposerActions } from '../ducks/composer';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import { isShowingAnyModal } from '../selectors/globalModals';
|
||||
|
||||
function renderCompositionArea(conversationId: string) {
|
||||
return <SmartCompositionArea id={conversationId} />;
|
||||
|
@ -48,19 +48,10 @@ export const SmartConversationView = memo(
|
|||
|
||||
const { processAttachments } = useComposerActions();
|
||||
|
||||
const hasOpenModal = useSelector((state: StateType) => {
|
||||
return (
|
||||
state.globalModals.forwardMessagesProps != null ||
|
||||
state.globalModals.deleteMessagesProps != null ||
|
||||
state.globalModals.hasConfirmationModal
|
||||
);
|
||||
});
|
||||
|
||||
const shouldHideConversationView = useSelector((state: StateType) => {
|
||||
const activePanel = getActivePanel(state);
|
||||
const isAnimating = getIsPanelAnimating(state);
|
||||
return activePanel && !isAnimating;
|
||||
});
|
||||
const hasOpenModal = useSelector(isShowingAnyModal);
|
||||
const activePanel = useSelector(getActivePanel);
|
||||
const isPanelAnimating = useSelector(getIsPanelAnimating);
|
||||
const shouldHideConversationView = activePanel && !isPanelAnimating;
|
||||
|
||||
const onExitSelectMode = useCallback(() => {
|
||||
toggleSelectMode(false);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import type { StateType } from '../reducer';
|
||||
import { getIntl } from '../selectors/user';
|
||||
|
@ -25,13 +25,17 @@ export const SmartDeleteMessagesModal = memo(
|
|||
'Cannot render delete messages modal without messages'
|
||||
);
|
||||
const { conversationId, messageIds, onDelete } = deleteMessagesProps;
|
||||
const isMe = useSelector((state: StateType) => {
|
||||
return getConversationSelector(state)(conversationId).isMe;
|
||||
});
|
||||
const conversationSelector = useSelector(getConversationSelector);
|
||||
const conversation = conversationSelector(conversationId);
|
||||
const { isMe } = conversation;
|
||||
|
||||
const canDeleteForEveryone = useSelector((state: StateType) => {
|
||||
return canDeleteMessagesForEveryone(state, { messageIds, isMe });
|
||||
});
|
||||
const getCanDeleteForEveryone = useCallback(
|
||||
(state: StateType) => {
|
||||
return canDeleteMessagesForEveryone(state, { messageIds, isMe });
|
||||
},
|
||||
[messageIds, isMe]
|
||||
);
|
||||
const canDeleteForEveryone = useSelector(getCanDeleteForEveryone);
|
||||
const lastSelectedMessage = useSelector(getLastSelectedMessage);
|
||||
const i18n = useSelector(getIntl);
|
||||
const { toggleDeleteMessagesModal } = useGlobalModalActions();
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
import React, { memo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import type { StateType } from '../reducer';
|
||||
import { Inbox } from '../../components/Inbox';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { SmartCustomizingPreferredReactionsModal } from './CustomizingPreferredReactionsModal';
|
||||
|
@ -15,6 +14,11 @@ import { SmartCallsTab } from './CallsTab';
|
|||
import { useItemsActions } from '../ducks/items';
|
||||
import { getNavTabsCollapsed } from '../selectors/items';
|
||||
import { SmartChatsTab } from './ChatsTab';
|
||||
import { getHasInitialLoadCompleted } from '../selectors/app';
|
||||
import {
|
||||
getInboxEnvelopeTimestamp,
|
||||
getInboxFirstEnvelopeTimestamp,
|
||||
} from '../selectors/inbox';
|
||||
|
||||
function renderChatsTab() {
|
||||
return <SmartChatsTab />;
|
||||
|
@ -41,17 +45,11 @@ export const SmartInbox = memo(function SmartInbox(): JSX.Element {
|
|||
const isCustomizingPreferredReactions = useSelector(
|
||||
getIsCustomizingPreferredReactions
|
||||
);
|
||||
const envelopeTimestamp = useSelector(
|
||||
(state: StateType) => state.inbox.envelopeTimestamp
|
||||
);
|
||||
const firstEnvelopeTimestamp = useSelector(
|
||||
(state: StateType) => state.inbox.firstEnvelopeTimestamp
|
||||
);
|
||||
const { hasInitialLoadCompleted } = useSelector(
|
||||
(state: StateType) => state.app
|
||||
);
|
||||
|
||||
const envelopeTimestamp = useSelector(getInboxEnvelopeTimestamp);
|
||||
const firstEnvelopeTimestamp = useSelector(getInboxFirstEnvelopeTimestamp);
|
||||
const hasInitialLoadCompleted = useSelector(getHasInitialLoadCompleted);
|
||||
const navTabsCollapsed = useSelector(getNavTabsCollapsed);
|
||||
|
||||
const { toggleNavTabsCollapse } = useItemsActions();
|
||||
|
||||
return (
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
import React, { memo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { SafetyNumberModal } from '../../components/SafetyNumberModal';
|
||||
import type { StateType } from '../reducer';
|
||||
import { getContactSafetyNumber } from '../selectors/safetyNumber';
|
||||
import { getContactSafetyNumberSelector } from '../selectors/safetyNumber';
|
||||
import { getConversationSelector } from '../selectors/conversations';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { useSafetyNumberActions } from '../ducks/safetyNumber';
|
||||
|
@ -20,9 +19,10 @@ export const SmartSafetyNumberModal = memo(function SmartSafetyNumberModal({
|
|||
const i18n = useSelector(getIntl);
|
||||
const conversationSelector = useSelector(getConversationSelector);
|
||||
const contact = conversationSelector(contactID);
|
||||
const contactSafetyNumber = useSelector((state: StateType) => {
|
||||
return getContactSafetyNumber(state, { contactID });
|
||||
});
|
||||
const contactSafetyNumberSelector = useSelector(
|
||||
getContactSafetyNumberSelector
|
||||
);
|
||||
const contactSafetyNumber = contactSafetyNumberSelector(contactID);
|
||||
const { generateSafetyNumber, toggleVerified } = useSafetyNumberActions();
|
||||
const { toggleSafetyNumberModal } = useGlobalModalActions();
|
||||
return (
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
import React, { memo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { SafetyNumberViewer } from '../../components/SafetyNumberViewer';
|
||||
import type { StateType } from '../reducer';
|
||||
import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog';
|
||||
import { getContactSafetyNumber } from '../selectors/safetyNumber';
|
||||
import { getContactSafetyNumberSelector } from '../selectors/safetyNumber';
|
||||
import { getConversationSelector } from '../selectors/conversations';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { useSafetyNumberActions } from '../ducks/safetyNumber';
|
||||
|
@ -16,9 +15,10 @@ export const SmartSafetyNumberViewer = memo(function SmartSafetyNumberViewer({
|
|||
onClose,
|
||||
}: SafetyNumberProps) {
|
||||
const i18n = useSelector(getIntl);
|
||||
const safetyNumberContact = useSelector((state: StateType) => {
|
||||
return getContactSafetyNumber(state, { contactID });
|
||||
});
|
||||
const contactSafetyNumberSelector = useSelector(
|
||||
getContactSafetyNumberSelector
|
||||
);
|
||||
const safetyNumberContact = contactSafetyNumberSelector(contactID);
|
||||
const conversationSelector = useSelector(getConversationSelector);
|
||||
const contact = conversationSelector(contactID);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue