diff --git a/lib/components/report-handler.react.js b/lib/components/report-handler.react.js --- a/lib/components/report-handler.react.js +++ b/lib/components/report-handler.react.js @@ -78,20 +78,22 @@ } } -const ConnectedReportHandler: React.ComponentType = - React.memo(function ConnectedReportHandler(props) { - const queuedReports = useSelector(queuedReportsSelector); - const dispatchActionPromise = useDispatchActionPromise(); - const callSendReports = useSendReports(); +const ConnectedReportHandler: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedReportHandler(props) { + const queuedReports = useSelector(queuedReportsSelector); + const dispatchActionPromise = useDispatchActionPromise(); + const callSendReports = useSendReports(); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedReportHandler; diff --git a/lib/components/thread-draft-updater.react.js b/lib/components/thread-draft-updater.react.js --- a/lib/components/thread-draft-updater.react.js +++ b/lib/components/thread-draft-updater.react.js @@ -9,7 +9,7 @@ import type { AppState } from '../types/redux-types.js'; import { useSelector, useDispatch } from '../utils/redux-utils.js'; -const ThreadDraftUpdater: React.ComponentType<{}> = React.memo<{}>( +const ThreadDraftUpdater: React.ComponentType<{}> = React.memo<{}, void>( function ThreadDraftUpdater() { const pendingToRealizedThreadIDs = useSelector((state: AppState) => pendingToRealizedThreadIDsSelector(state.threadStore.threadInfos), diff --git a/lib/keyserver-conn/keyserver-connection-handler.js b/lib/keyserver-conn/keyserver-connection-handler.js --- a/lib/keyserver-conn/keyserver-connection-handler.js +++ b/lib/keyserver-conn/keyserver-connection-handler.js @@ -282,7 +282,7 @@ return ; } -const Handler: React.ComponentType = React.memo( +const Handler: React.ComponentType = React.memo( KeyserverConnectionHandler, ); diff --git a/lib/shared/transforms/keyserver-store-transform.js b/lib/shared/transforms/keyserver-store-transform.js --- a/lib/shared/transforms/keyserver-store-transform.js +++ b/lib/shared/transforms/keyserver-store-transform.js @@ -7,10 +7,7 @@ KeyserverInfo, KeyserverStore, } from '../../types/keyserver-types.js'; -import { - type ConnectionInfo, - defaultConnectionInfo, -} from '../../types/socket-types.js'; +import { defaultConnectionInfo } from '../../types/socket-types.js'; export type PersistedKeyserverInfo = Omit< KeyserverInfo, diff --git a/lib/socket/api-request-handler.react.js b/lib/socket/api-request-handler.react.js --- a/lib/socket/api-request-handler.react.js +++ b/lib/socket/api-request-handler.react.js @@ -104,18 +104,20 @@ }; } -const ConnectedAPIRequestHandler: React.ComponentType = - React.memo(function ConnectedAPIRequestHandler(props) { - const connection = useSelector(connectionSelector(props.keyserverID)); - invariant(connection, 'keyserver missing from keyserverStore'); - const { registerActiveSocket } = useCallKeyserverEndpointContext(); - return ( - - ); - }); +const ConnectedAPIRequestHandler: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedAPIRequestHandler(props) { + const connection = useSelector(connectionSelector(props.keyserverID)); + invariant(connection, 'keyserver missing from keyserverStore'); + const { registerActiveSocket } = useCallKeyserverEndpointContext(); + return ( + + ); +}); export default ConnectedAPIRequestHandler; diff --git a/lib/socket/calendar-query-handler.react.js b/lib/socket/calendar-query-handler.react.js --- a/lib/socket/calendar-query-handler.react.js +++ b/lib/socket/calendar-query-handler.react.js @@ -128,7 +128,7 @@ } const ConnectedCalendarQueryHandler: React.ComponentType = - React.memo(function ConnectedCalendarQueryHandler(props) { + React.memo(function ConnectedCalendarQueryHandler(props) { const { currentCalendarQuery, keyserverID } = props; const keyserverInfo = useSelector( state => state.keyserverStore.keyserverInfos[keyserverID], diff --git a/lib/socket/request-response-handler.react.js b/lib/socket/request-response-handler.react.js --- a/lib/socket/request-response-handler.react.js +++ b/lib/socket/request-response-handler.react.js @@ -139,7 +139,7 @@ } const ConnectedRequestResponseHandler: React.ComponentType = - React.memo(function ConnectedRequestResponseHandler(props) { + React.memo(function ConnectedRequestResponseHandler(props) { const connection = useSelector(connectionSelector(props.keyserverID)); invariant(connection, 'keyserver missing from keyserverStore'); diff --git a/native/account/log-in-panel.react.js b/native/account/log-in-panel.react.js --- a/native/account/log-in-panel.react.js +++ b/native/account/log-in-panel.react.js @@ -348,21 +348,23 @@ const olmSessionInitializationDataLoadingStatusSelector = createLoadingStatusSelector(getOlmSessionInitializationDataActionTypes); -const ConnectedLogInPanel: React.ComponentType = - React.memo(function ConnectedLogInPanel(props: BaseProps) { - const loadingStatus = useSelector( - olmSessionInitializationDataLoadingStatusSelector, - ); +const ConnectedLogInPanel: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedLogInPanel(props: BaseProps) { + const loadingStatus = useSelector( + olmSessionInitializationDataLoadingStatusSelector, + ); - const callIdentityPasswordLogIn = usePasswordLogIn(); + const callIdentityPasswordLogIn = usePasswordLogIn(); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedLogInPanel; diff --git a/native/account/logged-out-modal.react.js b/native/account/logged-out-modal.react.js --- a/native/account/logged-out-modal.react.js +++ b/native/account/logged-out-modal.react.js @@ -604,7 +604,9 @@ ); } -const MemoizedLoggedOutModal: React.ComponentType = - React.memo(LoggedOutModal); +const MemoizedLoggedOutModal: React.ComponentType = React.memo< + Props, + void, +>(LoggedOutModal); export default MemoizedLoggedOutModal; diff --git a/native/account/registration/registration-text-input.react.js b/native/account/registration/registration-text-input.react.js --- a/native/account/registration/registration-text-input.react.js +++ b/native/account/registration/registration-text-input.react.js @@ -99,7 +99,7 @@ const MemoizedRegistrationTextInput: typeof RegistrationTextInput = React.memo< Props, - React.ElementRef, + React.RefSetter>, >(RegistrationTextInput); export default MemoizedRegistrationTextInput; diff --git a/native/account/registration/siwe-backup-message-creation.react.js b/native/account/registration/siwe-backup-message-creation.react.js --- a/native/account/registration/siwe-backup-message-creation.react.js +++ b/native/account/registration/siwe-backup-message-creation.react.js @@ -39,7 +39,7 @@ }; const CreateSIWEBackupMessageBase: React.ComponentType = - React.memo( + React.memo( function CreateSIWEBackupMessageBase( props: CreateSIWEBackupMessageBaseProps, ): React.Node { diff --git a/native/calendar/calendar-screen.react.js b/native/calendar/calendar-screen.react.js --- a/native/calendar/calendar-screen.react.js +++ b/native/calendar/calendar-screen.react.js @@ -1077,44 +1077,46 @@ ThreadPickerModalRouteName, ); -const ConnectedCalendarScreen: React.ComponentType = - React.memo(function ConnectedCalendarScreen(props: BaseProps) { - const navContext = React.useContext(NavContext); - const calendarActive = - activeTabSelector(navContext) || activeThreadPickerSelector(navContext); - - const listData = useSelector(calendarListData); - const startDate = useSelector(state => state.navInfo.startDate); - const endDate = useSelector(state => state.navInfo.endDate); - const calendarFilters = useSelector(state => state.calendarFilters); - const dimensions = useSelector(derivedDimensionsInfoSelector); - const loadingStatus = useSelector(loadingStatusSelector); - const connected = useSelector(state => state.connectivity.connected); - const colors = useColors(); - const styles = useStyles(unboundStyles); - const indicatorStyle = useIndicatorStyle(); - - const dispatchActionPromise = useDispatchActionPromise(); - const callUpdateCalendarQuery = useUpdateCalendarQuery(); - - return ( - - ); - }); +const ConnectedCalendarScreen: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedCalendarScreen(props: BaseProps) { + const navContext = React.useContext(NavContext); + const calendarActive = + activeTabSelector(navContext) || activeThreadPickerSelector(navContext); + + const listData = useSelector(calendarListData); + const startDate = useSelector(state => state.navInfo.startDate); + const endDate = useSelector(state => state.navInfo.endDate); + const calendarFilters = useSelector(state => state.calendarFilters); + const dimensions = useSelector(derivedDimensionsInfoSelector); + const loadingStatus = useSelector(loadingStatusSelector); + const connected = useSelector(state => state.connectivity.connected); + const colors = useColors(); + const styles = useStyles(unboundStyles); + const indicatorStyle = useIndicatorStyle(); + + const dispatchActionPromise = useDispatchActionPromise(); + const callUpdateCalendarQuery = useUpdateCalendarQuery(); + + return ( + + ); +}); export default ConnectedCalendarScreen; diff --git a/native/calendar/entry.react.js b/native/calendar/entry.react.js --- a/native/calendar/entry.react.js +++ b/native/calendar/entry.react.js @@ -809,7 +809,7 @@ ThreadPickerModalRouteName, ); -const Entry: React.ComponentType = React.memo( +const Entry: React.ComponentType = React.memo( function ConnectedEntry(props: BaseProps) { const navContext = React.useContext(NavContext); const threadPickerActive = activeThreadPickerSelector(navContext); diff --git a/native/calendar/section-footer.react.js b/native/calendar/section-footer.react.js --- a/native/calendar/section-footer.react.js +++ b/native/calendar/section-footer.react.js @@ -71,12 +71,14 @@ }; } -const ConnectedSectionFooter: React.ComponentType = - React.memo(function ConnectedSectionFooter(props: BaseProps) { - const styles = useStyles(unboundStyles); - const colors = useColors(); +const ConnectedSectionFooter: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedSectionFooter(props: BaseProps) { + const styles = useStyles(unboundStyles); + const colors = useColors(); - return ; - }); + return ; +}); export default ConnectedSectionFooter; diff --git a/native/chat/chat-context-provider.react.js b/native/chat/chat-context-provider.react.js --- a/native/chat/chat-context-provider.react.js +++ b/native/chat/chat-context-provider.react.js @@ -167,7 +167,9 @@ ); } -const MemoizedChatContextProvider: React.ComponentType = - React.memo(ChatContextProvider); +const MemoizedChatContextProvider: React.ComponentType = React.memo< + Props, + void, +>(ChatContextProvider); export default MemoizedChatContextProvider; diff --git a/native/chat/chat-header.react.js b/native/chat/chat-header.react.js --- a/native/chat/chat-header.react.js +++ b/native/chat/chat-header.react.js @@ -10,11 +10,13 @@ const activeTabSelector = createActiveTabSelector(ChatRouteName); -const ChatHeader: React.ComponentType = - React.memo(function ChatHeader(props: StackHeaderProps) { - const navContext = React.useContext(NavContext); - const activeTab = activeTabSelector(navContext); - return
; - }); +const ChatHeader: React.ComponentType = React.memo< + StackHeaderProps, + void, +>(function ChatHeader(props: StackHeaderProps) { + const navContext = React.useContext(NavContext); + const activeTab = activeTabSelector(navContext); + return
; +}); export default ChatHeader; diff --git a/native/chat/chat-input-bar.react.js b/native/chat/chat-input-bar.react.js --- a/native/chat/chat-input-bar.react.js +++ b/native/chat/chat-input-bar.react.js @@ -1225,7 +1225,7 @@ +route: NavigationRoute<'MessageList'>, }; const ConnectedChatInputBar: React.ComponentType = - React.memo(function ConnectedChatInputBar( + React.memo(function ConnectedChatInputBar( props: ChatInputBarProps, ) { const { navigation, route, ...restProps } = props; diff --git a/native/chat/chat-item-height-measurer.react.js b/native/chat/chat-item-height-measurer.react.js --- a/native/chat/chat-item-height-measurer.react.js +++ b/native/chat/chat-item-height-measurer.react.js @@ -262,7 +262,9 @@ ); } -const MemoizedChatItemHeightMeasurer: React.ComponentType = - React.memo(ChatItemHeightMeasurer); +const MemoizedChatItemHeightMeasurer: React.ComponentType = React.memo< + Props, + void, +>(ChatItemHeightMeasurer); export default MemoizedChatItemHeightMeasurer; diff --git a/native/chat/chat-list.react.js b/native/chat/chat-list.react.js --- a/native/chat/chat-list.react.js +++ b/native/chat/chat-list.react.js @@ -334,22 +334,23 @@ }, }); -const ConnectedChatList: React.ComponentType = React.memo( - function ConnectedChatList(props: BaseProps) { - const keyboardState = React.useContext(KeyboardContext); - const inputState = React.useContext(InputStateContext); - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); - return ( - - ); - }, -); +const ConnectedChatList: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedChatList(props: BaseProps) { + const keyboardState = React.useContext(KeyboardContext); + const inputState = React.useContext(InputStateContext); + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + return ( + + ); +}); export default ConnectedChatList; diff --git a/native/chat/compose-subchannel.react.js b/native/chat/compose-subchannel.react.js --- a/native/chat/compose-subchannel.react.js +++ b/native/chat/compose-subchannel.react.js @@ -368,7 +368,9 @@ }, }; -const MemoizedComposeSubchannel: React.ComponentType = - React.memo(ComposeSubchannel); +const MemoizedComposeSubchannel: React.ComponentType = React.memo< + Props, + void, +>(ComposeSubchannel); export default MemoizedComposeSubchannel; diff --git a/native/chat/compose-thread-button.react.js b/native/chat/compose-thread-button.react.js --- a/native/chat/compose-thread-button.react.js +++ b/native/chat/compose-thread-button.react.js @@ -55,7 +55,9 @@ }, }); -const MemoizedComposeThreadButton: React.ComponentType = - React.memo(ComposeThreadButton); +const MemoizedComposeThreadButton: React.ComponentType = React.memo< + Props, + void, +>(ComposeThreadButton); export default MemoizedComposeThreadButton; diff --git a/native/chat/composed-message.react.js b/native/chat/composed-message.react.js --- a/native/chat/composed-message.react.js +++ b/native/chat/composed-message.react.js @@ -48,331 +48,312 @@ +children: React.Node, }; -const ConnectedComposedMessage: React.ComponentType = React.memo( - function ConnectedComposedMessage(props: Props) { - const composedMessageMaxWidth = useComposedMessageMaxWidth(); - const colors = useColors(); - const inputState = React.useContext(InputStateContext); - const navigateToSidebar = useNavigateToSidebar(props.item); - const contentAndHeaderOpacity = useContentAndHeaderOpacity(props.item); - const deliveryIconOpacity = useDeliveryIconOpacity(props.item); - - const messageEditingContext = React.useContext(MessageEditingContext); - const progress = useDerivedValue(() => { - const isThisThread = - messageEditingContext?.editState.editedMessage?.threadID === - props.item.threadInfo.id; - const isHighlighted = - messageEditingContext?.editState.editedMessage?.id === - props.item.messageInfo.id && isThisThread; - return withTiming(isHighlighted ? 1 : 0); +const ConnectedComposedMessage: React.ComponentType = React.memo< + Props, + void, +>(function ConnectedComposedMessage(props: Props) { + const composedMessageMaxWidth = useComposedMessageMaxWidth(); + const colors = useColors(); + const inputState = React.useContext(InputStateContext); + const navigateToSidebar = useNavigateToSidebar(props.item); + const contentAndHeaderOpacity = useContentAndHeaderOpacity(props.item); + const deliveryIconOpacity = useDeliveryIconOpacity(props.item); + + const messageEditingContext = React.useContext(MessageEditingContext); + const progress = useDerivedValue(() => { + const isThisThread = + messageEditingContext?.editState.editedMessage?.threadID === + props.item.threadInfo.id; + const isHighlighted = + messageEditingContext?.editState.editedMessage?.id === + props.item.messageInfo.id && isThisThread; + return withTiming(isHighlighted ? 1 : 0); + }); + + const editedMessageStyle = useAnimatedStyle(() => { + const backgroundColor = interpolateColor( + progress.value, + [0, 1], + ['transparent', `#${props.item.threadInfo.color}40`], + ); + return { + backgroundColor, + }; + }); + + assertComposableMessageType(props.item.messageInfo.type); + const { + item, + sendFailed, + swipeOptions, + shouldDisplayPinIndicator, + children, + focused, + ...viewProps + } = props; + + const { hasBeenEdited, isPinned } = item; + const { id, creator } = item.messageInfo; + + const { isViewer } = creator; + const alignStyle = isViewer ? styles.rightChatBubble : styles.leftChatBubble; + + const containerStyle = React.useMemo(() => { + let containerMarginBottom = 5; + if (item.endsCluster) { + containerMarginBottom += clusterEndHeight; + } + return { marginBottom: containerMarginBottom }; + }, [item.endsCluster]); + + const messageBoxContainerStyle = React.useMemo( + () => [ + styles.messageBoxContainer, + isViewer ? styles.rightChatContainer : styles.leftChatContainer, + ], + [isViewer], + ); + + const deliveryIconAnimatedStyle = useAnimatedStyle(() => ({ + opacity: deliveryIconOpacity.value, + })); + + const deliveryIcon = React.useMemo(() => { + if (!isViewer) { + return undefined; + } + + let deliveryIconName; + let deliveryIconColor = `#${item.threadInfo.color}`; + + const notDeliveredP2PMessages = + item?.localMessageInfo?.outboundP2PMessageIDs ?? []; + if ( + id !== null && + id !== undefined && + notDeliveredP2PMessages.length === 0 + ) { + deliveryIconName = 'check-circle'; + } else if (sendFailed) { + deliveryIconName = 'x-circle'; + deliveryIconColor = colors.redText; + } else { + deliveryIconName = 'circle'; + } + + return ( + + + + ); + }, [ + colors.redText, + deliveryIconAnimatedStyle, + id, + isViewer, + item?.localMessageInfo?.outboundP2PMessageIDs, + item.threadInfo.color, + sendFailed, + ]); + + const editInputMessage = inputState?.editInputMessage; + const reply = React.useCallback(() => { + invariant(editInputMessage, 'editInputMessage should be set in reply'); + invariant(item.messageInfo.text, 'text should be set in reply'); + editInputMessage({ + message: createMessageReply(item.messageInfo.text), + mode: 'prepend', }); + }, [editInputMessage, item.messageInfo.text]); - const editedMessageStyle = useAnimatedStyle(() => { - const backgroundColor = interpolateColor( - progress.value, - [0, 1], - ['transparent', `#${props.item.threadInfo.color}40`], - ); - return { - backgroundColor, - }; - }); + const triggerReply = + swipeOptions === 'reply' || swipeOptions === 'both' ? reply : undefined; - assertComposableMessageType(props.item.messageInfo.type); - const { - item, - sendFailed, - swipeOptions, - shouldDisplayPinIndicator, - children, - focused, - ...viewProps - } = props; - - const { hasBeenEdited, isPinned } = item; - const { id, creator } = item.messageInfo; - - const { isViewer } = creator; - const alignStyle = isViewer - ? styles.rightChatBubble - : styles.leftChatBubble; - - const containerStyle = React.useMemo(() => { - let containerMarginBottom = 5; - if (item.endsCluster) { - containerMarginBottom += clusterEndHeight; - } - return { marginBottom: containerMarginBottom }; - }, [item.endsCluster]); - - const messageBoxContainerStyle = React.useMemo( - () => [ - styles.messageBoxContainer, - isViewer ? styles.rightChatContainer : styles.leftChatContainer, - ], - [isViewer], - ); + const triggerSidebar = + swipeOptions === 'sidebar' || swipeOptions === 'both' + ? navigateToSidebar + : undefined; + + const navigateToUserProfileBottomSheet = + useNavigateToUserProfileBottomSheet(); - const deliveryIconAnimatedStyle = useAnimatedStyle(() => ({ - opacity: deliveryIconOpacity.value, - })); - - const deliveryIcon = React.useMemo(() => { - if (!isViewer) { - return undefined; - } - - let deliveryIconName; - let deliveryIconColor = `#${item.threadInfo.color}`; - - const notDeliveredP2PMessages = - item?.localMessageInfo?.outboundP2PMessageIDs ?? []; - if ( - id !== null && - id !== undefined && - notDeliveredP2PMessages.length === 0 - ) { - deliveryIconName = 'check-circle'; - } else if (sendFailed) { - deliveryIconName = 'x-circle'; - deliveryIconColor = colors.redText; - } else { - deliveryIconName = 'circle'; - } + const onPressAvatar = React.useCallback( + () => navigateToUserProfileBottomSheet(item.messageInfo.creator.id), + [item.messageInfo.creator.id, navigateToUserProfileBottomSheet], + ); + const avatar = React.useMemo(() => { + if (!isViewer && item.endsCluster) { return ( - - - + + + ); - }, [ - colors.redText, - deliveryIconAnimatedStyle, - id, - isViewer, - item?.localMessageInfo?.outboundP2PMessageIDs, - item.threadInfo.color, - sendFailed, - ]); - - const editInputMessage = inputState?.editInputMessage; - const reply = React.useCallback(() => { - invariant(editInputMessage, 'editInputMessage should be set in reply'); - invariant(item.messageInfo.text, 'text should be set in reply'); - editInputMessage({ - message: createMessageReply(item.messageInfo.text), - mode: 'prepend', - }); - }, [editInputMessage, item.messageInfo.text]); - - const triggerReply = - swipeOptions === 'reply' || swipeOptions === 'both' ? reply : undefined; - - const triggerSidebar = - swipeOptions === 'sidebar' || swipeOptions === 'both' - ? navigateToSidebar - : undefined; - - const navigateToUserProfileBottomSheet = - useNavigateToUserProfileBottomSheet(); - - const onPressAvatar = React.useCallback( - () => navigateToUserProfileBottomSheet(item.messageInfo.creator.id), - [item.messageInfo.creator.id, navigateToUserProfileBottomSheet], + } else if (!isViewer) { + return ; + } else { + return undefined; + } + }, [isViewer, item.endsCluster, item.messageInfo.creator.id, onPressAvatar]); + + const pinIconPositioning = isViewer ? 'left' : 'right'; + const pinIconName = pinIconPositioning === 'left' ? 'pin-mirror' : 'pin'; + const messageBoxTopLevelContainerStyle = + pinIconPositioning === 'left' + ? styles.rightMessageBoxTopLevelContainerStyle + : styles.leftMessageBoxTopLevelContainerStyle; + + const pinIcon = React.useMemo(() => { + if (!isPinned || !shouldDisplayPinIndicator) { + return undefined; + } + return ( + + + ); + }, [isPinned, item.threadInfo.color, pinIconName, shouldDisplayPinIndicator]); - const avatar = React.useMemo(() => { - if (!isViewer && item.endsCluster) { - return ( - ({ + opacity: contentAndHeaderOpacity.value, + maxWidth: composedMessageMaxWidth, + }), + [composedMessageMaxWidth], + ); + + const messageBox = React.useMemo( + () => ( + + {pinIcon} + + - - - ); - } else if (!isViewer) { - return ; - } else { - return undefined; - } - }, [ - isViewer, - item.endsCluster, - item.messageInfo.creator.id, - onPressAvatar, - ]); - - const pinIconPositioning = isViewer ? 'left' : 'right'; - const pinIconName = pinIconPositioning === 'left' ? 'pin-mirror' : 'pin'; - const messageBoxTopLevelContainerStyle = - pinIconPositioning === 'left' - ? styles.rightMessageBoxTopLevelContainerStyle - : styles.leftMessageBoxTopLevelContainerStyle; - - const pinIcon = React.useMemo(() => { - if (!isPinned || !shouldDisplayPinIndicator) { - return undefined; - } - return ( - - + {avatar} + {children} + - ); - }, [ - isPinned, + + ), + [ + avatar, + children, + isViewer, item.threadInfo.color, - pinIconName, - shouldDisplayPinIndicator, - ]); - - const messageBoxStyle = useAnimatedStyle( - () => ({ - opacity: contentAndHeaderOpacity.value, - maxWidth: composedMessageMaxWidth, - }), - [composedMessageMaxWidth], - ); - - const messageBox = React.useMemo( - () => ( - - {pinIcon} - - - {avatar} - {children} - - - - ), - [ - avatar, - children, - isViewer, - item.threadInfo.color, - messageBoxContainerStyle, - messageBoxStyle, - messageBoxTopLevelContainerStyle, - pinIcon, - triggerReply, - triggerSidebar, - ], + messageBoxContainerStyle, + messageBoxStyle, + messageBoxTopLevelContainerStyle, + pinIcon, + triggerReply, + triggerSidebar, + ], + ); + + const label = getMessageLabel(hasBeenEdited, item.threadInfo.id); + const inlineEngagement = React.useMemo(() => { + if (!chatMessageItemHasEngagement(item, item.threadInfo.id)) { + return undefined; + } + const positioning = isViewer ? 'right' : 'left'; + return ( + ); + }, [label, isViewer, item]); - const label = getMessageLabel(hasBeenEdited, item.threadInfo.id); - const inlineEngagement = React.useMemo(() => { - if (!chatMessageItemHasEngagement(item, item.threadInfo.id)) { - return undefined; - } - const positioning = isViewer ? 'right' : 'left'; - return ( - - ); - }, [label, isViewer, item]); - - const viewStyle = React.useMemo(() => { - const baseStyle: Array = [styles.alignment]; - if (__DEV__) { - // We don't force view height in dev mode because we - // want to measure it in Message to see if it's correct - return baseStyle; - } - if (item.messageShapeType === 'text') { - baseStyle.push({ height: item.contentHeight }); - } else if (item.messageShapeType === 'multimedia') { - const height = item.inlineEngagementHeight - ? item.contentHeight + item.inlineEngagementHeight - : item.contentHeight; - baseStyle.push({ height }); - } + const viewStyle = React.useMemo(() => { + const baseStyle: Array = [styles.alignment]; + if (__DEV__) { + // We don't force view height in dev mode because we + // want to measure it in Message to see if it's correct return baseStyle; - }, [ - item.contentHeight, - item.inlineEngagementHeight, - item.messageShapeType, - ]); - - const messageHeaderStyle = useAnimatedStyle(() => ({ - opacity: contentAndHeaderOpacity.value, - })); - - const animatedContainerStyle = React.useMemo( - () => [containerStyle, editedMessageStyle], - [containerStyle, editedMessageStyle], - ); - - const contentStyle = React.useMemo( - () => [styles.content, alignStyle], - [alignStyle], - ); - - const failedSend = React.useMemo( - () => (sendFailed ? : undefined), - [item, sendFailed], - ); - - const composedMessage = React.useMemo(() => { - return ( - - - - - - - - {deliveryIcon} - {messageBox} - - {inlineEngagement} + } + if (item.messageShapeType === 'text') { + baseStyle.push({ height: item.contentHeight }); + } else if (item.messageShapeType === 'multimedia') { + const height = item.inlineEngagementHeight + ? item.contentHeight + item.inlineEngagementHeight + : item.contentHeight; + baseStyle.push({ height }); + } + return baseStyle; + }, [item.contentHeight, item.inlineEngagementHeight, item.messageShapeType]); + + const messageHeaderStyle = useAnimatedStyle(() => ({ + opacity: contentAndHeaderOpacity.value, + })); + + const animatedContainerStyle = React.useMemo( + () => [containerStyle, editedMessageStyle], + [containerStyle, editedMessageStyle], + ); + + const contentStyle = React.useMemo( + () => [styles.content, alignStyle], + [alignStyle], + ); + + const failedSend = React.useMemo( + () => (sendFailed ? : undefined), + [item, sendFailed], + ); + + const composedMessage = React.useMemo(() => { + return ( + + + + + + + + {deliveryIcon} + {messageBox} - {failedSend} - - - ); - }, [ - animatedContainerStyle, - contentStyle, - deliveryIcon, - failedSend, - focused, - inlineEngagement, - item, - messageBox, - messageHeaderStyle, - viewProps, - viewStyle, - ]); - - return composedMessage; - }, -); + {inlineEngagement} + + {failedSend} + + + ); + }, [ + animatedContainerStyle, + contentStyle, + deliveryIcon, + failedSend, + focused, + inlineEngagement, + item, + messageBox, + messageHeaderStyle, + viewProps, + viewStyle, + ]); + + return composedMessage; +}); const styles = StyleSheet.create({ alignment: { diff --git a/native/chat/failed-send.react.js b/native/chat/failed-send.react.js --- a/native/chat/failed-send.react.js +++ b/native/chat/failed-send.react.js @@ -152,29 +152,31 @@ }; } -const ConnectedFailedSend: React.ComponentType = - React.memo(function ConnectedFailedSend(props: BaseProps) { - const id = messageID(props.item.messageInfo); - const rawMessageInfo = useSelector(state => { - const message = state.messageStore.messages[id]; - return message ? assertComposableRawMessage(message) : null; - }); - const styles = useStyles(unboundStyles); - const inputState = React.useContext(InputStateContext); - const { parentThreadID } = props.item.threadInfo; - const parentThreadInfo = useSelector(state => - parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, - ); - - return ( - - ); +const ConnectedFailedSend: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedFailedSend(props: BaseProps) { + const id = messageID(props.item.messageInfo); + const rawMessageInfo = useSelector(state => { + const message = state.messageStore.messages[id]; + return message ? assertComposableRawMessage(message) : null; }); + const styles = useStyles(unboundStyles); + const inputState = React.useContext(InputStateContext); + const { parentThreadID } = props.item.threadInfo; + const parentThreadInfo = useSelector(state => + parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, + ); + + return ( + + ); +}); export { ConnectedFailedSend as FailedSend, failedSendHeight }; diff --git a/native/chat/inner-robotext-message.react.js b/native/chat/inner-robotext-message.react.js --- a/native/chat/inner-robotext-message.react.js +++ b/native/chat/inner-robotext-message.react.js @@ -174,7 +174,7 @@ }; const MemoizedInnerRobotextMessage: React.ComponentType = - React.memo(InnerRobotextMessage); + React.memo(InnerRobotextMessage); export { dummyNodeForRobotextMessageHeightMeasurement, diff --git a/native/chat/message-editing-context-provider.react.js b/native/chat/message-editing-context-provider.react.js --- a/native/chat/message-editing-context-provider.react.js +++ b/native/chat/message-editing-context-provider.react.js @@ -74,6 +74,6 @@ } const MemoizedMessageEditingContextProvider: React.ComponentType = - React.memo(MessageEditingContextProvider); + React.memo(MessageEditingContextProvider); export default MemoizedMessageEditingContextProvider; diff --git a/native/chat/message-list-container.react.js b/native/chat/message-list-container.react.js --- a/native/chat/message-list-container.react.js +++ b/native/chat/message-list-container.react.js @@ -247,7 +247,7 @@ } const ConnectedMessageListContainer: React.ComponentType = - React.memo(function ConnectedMessageListContainer( + React.memo(function ConnectedMessageListContainer( props: BaseProps, ) { const [usernameInputText, setUsernameInputText] = React.useState(''); diff --git a/native/chat/message-list-header-title.react.js b/native/chat/message-list-header-title.react.js --- a/native/chat/message-list-header-title.react.js +++ b/native/chat/message-list-header-title.react.js @@ -88,7 +88,7 @@ } const ConnectedMessageListHeaderTitle: React.ComponentType = - React.memo(function ConnectedMessageListHeaderTitle( + React.memo(function ConnectedMessageListHeaderTitle( props: BaseProps, ) { const styles = useStyles(unboundStyles); diff --git a/native/chat/message-list-thread-search.react.js b/native/chat/message-list-thread-search.react.js --- a/native/chat/message-list-thread-search.react.js +++ b/native/chat/message-list-thread-search.react.js @@ -29,112 +29,113 @@ returnKeyType: 'go', }; -const MessageListThreadSearch: React.ComponentType = React.memo( - function MessageListThreadSearch({ - usernameInputText, - updateUsernameInput, - userInfoInputArray, - updateTagInput, - resolveToUser, - userSearchResults, - }) { - const styles = useStyles(unboundStyles); - - const [userListItems, nonFriends] = React.useMemo(() => { - const nonFriendsSet = new Set(); - if (userInfoInputArray.length > 0) { - return [userSearchResults, nonFriendsSet]; - } +const MessageListThreadSearch: React.ComponentType = React.memo< + Props, + void, +>(function MessageListThreadSearch({ + usernameInputText, + updateUsernameInput, + userInfoInputArray, + updateTagInput, + resolveToUser, + userSearchResults, +}) { + const styles = useStyles(unboundStyles); + + const [userListItems, nonFriends] = React.useMemo(() => { + const nonFriendsSet = new Set(); + if (userInfoInputArray.length > 0) { + return [userSearchResults, nonFriendsSet]; + } - const userListItemsArr = []; - for (const searchResult of userSearchResults) { - if (searchResult.notice !== notFriendNotice) { - userListItemsArr.push(searchResult); - continue; - } - nonFriendsSet.add(searchResult.id); - const { alert, ...rest } = searchResult; - userListItemsArr.push(rest); + const userListItemsArr = []; + for (const searchResult of userSearchResults) { + if (searchResult.notice !== notFriendNotice) { + userListItemsArr.push(searchResult); + continue; } - return [userListItemsArr, nonFriendsSet]; - }, [userSearchResults, userInfoInputArray]); - - const viewerID = useSelector(state => state.currentUserInfo?.id); - const onUserSelect = React.useCallback( - async (userInfo: AccountUserInfo) => { - for (const existingUserInfo of userInfoInputArray) { - if (userInfo.id === existingUserInfo.id) { - return; - } - } - if (nonFriends.has(userInfo.id) || userInfo.id === viewerID) { - await resolveToUser(userInfo); + nonFriendsSet.add(searchResult.id); + const { alert, ...rest } = searchResult; + userListItemsArr.push(rest); + } + return [userListItemsArr, nonFriendsSet]; + }, [userSearchResults, userInfoInputArray]); + + const viewerID = useSelector(state => state.currentUserInfo?.id); + const onUserSelect = React.useCallback( + async (userInfo: AccountUserInfo) => { + for (const existingUserInfo of userInfoInputArray) { + if (userInfo.id === existingUserInfo.id) { return; } - const newUserInfoInputArray = [...userInfoInputArray, userInfo]; - updateUsernameInput(''); - updateTagInput(newUserInfoInputArray); - }, - [ - userInfoInputArray, - nonFriends, - updateTagInput, - resolveToUser, - updateUsernameInput, - viewerID, - ], - ); - - const tagDataLabelExtractor = React.useCallback( - (userInfo: AccountUserInfo) => userInfo.username, - [], + } + if (nonFriends.has(userInfo.id) || userInfo.id === viewerID) { + await resolveToUser(userInfo); + return; + } + const newUserInfoInputArray = [...userInfoInputArray, userInfo]; + updateUsernameInput(''); + updateTagInput(newUserInfoInputArray); + }, + [ + userInfoInputArray, + nonFriends, + updateTagInput, + resolveToUser, + updateUsernameInput, + viewerID, + ], + ); + + const tagDataLabelExtractor = React.useCallback( + (userInfo: AccountUserInfo) => userInfo.username, + [], + ); + + const isSearchResultVisible = + (userInfoInputArray.length === 0 || usernameInputText.length > 0) && + userSearchResults.length > 0; + + let separator = null; + let userList = null; + let userSelectionAdditionalStyles = styles.userSelectionLimitedHeight; + const userListItemsWithENSNames = useENSNames(userListItems); + if (isSearchResultVisible) { + userList = ( + + + ); - - const isSearchResultVisible = - (userInfoInputArray.length === 0 || usernameInputText.length > 0) && - userSearchResults.length > 0; - - let separator = null; - let userList = null; - let userSelectionAdditionalStyles = styles.userSelectionLimitedHeight; - const userListItemsWithENSNames = useENSNames(userListItems); - if (isSearchResultVisible) { - userList = ( - - - - ); - separator = ; - userSelectionAdditionalStyles = null; - } - - const userInfoInputArrayWithENSNames = useENSNames(userInfoInputArray); - return ( - <> - - - To: - - - + separator = ; + userSelectionAdditionalStyles = null; + } + + const userInfoInputArrayWithENSNames = useENSNames(userInfoInputArray); + return ( + <> + + + To: + + - {userList} - {separator} - - ); - }, -); + {userList} + + {separator} + + ); +}); const unboundStyles = { userSelection: { diff --git a/native/chat/message-list.react.js b/native/chat/message-list.react.js --- a/native/chat/message-list.react.js +++ b/native/chat/message-list.react.js @@ -289,38 +289,40 @@ }; } -const ConnectedMessageList: React.ComponentType = - React.memo(function ConnectedMessageList(props: BaseProps) { - const keyboardState = React.useContext(KeyboardContext); - const overlayContext = React.useContext(OverlayContext); +const ConnectedMessageList: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedMessageList(props: BaseProps) { + const keyboardState = React.useContext(KeyboardContext); + const overlayContext = React.useContext(OverlayContext); - const threadID = props.threadInfo.id; - const startReached = useSelector( - state => - !!( - state.messageStore.threads[threadID] && - state.messageStore.threads[threadID].startReached - ), - ); + const threadID = props.threadInfo.id; + const startReached = useSelector( + state => + !!( + state.messageStore.threads[threadID] && + state.messageStore.threads[threadID].startReached + ), + ); - const styles = useStyles(unboundStyles); - const indicatorStyle = useIndicatorStyle(); + const styles = useStyles(unboundStyles); + const indicatorStyle = useIndicatorStyle(); - const fetchMessages = useFetchMessages(props.threadInfo); + const fetchMessages = useFetchMessages(props.threadInfo); - useWatchThread(props.threadInfo); + useWatchThread(props.threadInfo); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedMessageList; diff --git a/native/chat/multimedia-message-multimedia.react.js b/native/chat/multimedia-message-multimedia.react.js --- a/native/chat/multimedia-message-multimedia.react.js +++ b/native/chat/multimedia-message-multimedia.react.js @@ -50,123 +50,125 @@ }, }); -const MultimediaMessageMultimedia: React.ComponentType = - React.memo(function MultimediaMessageMultimedia(props: Props) { - const keyboardState = React.useContext(KeyboardContext); - const overlayContext = React.useContext(OverlayContext); - invariant( - overlayContext, - 'MultimediaMessageMultimedia should have OverlayContext', - ); - const route = useRoute(); - const viewRef = React.useRef>(); - - const dismissKeyboardIfShowing = React.useCallback((): boolean => { - return !!(keyboardState && keyboardState.dismissKeyboardIfShowing()); - }, [keyboardState]); - - const onPress = React.useCallback(() => { - const { - clickable, - verticalBounds, - onPressMultimedia, - setClickable, - mediaInfo, - } = props; - if (!clickable) { - return; - } - - if (dismissKeyboardIfShowing()) { - return; - } - - const view = viewRef.current; - if (!view || !verticalBounds) { - return; - } - - const measureCallback = onPressMultimedia; - if (!measureCallback) { - return; - } - - setClickable(false); - - overlayContext.setScrollBlockingModalStatus('open'); - - view.measure((x, y, width, height, pageX, pageY) => { - const coordinates = { x: pageX, y: pageY, width, height }; - measureCallback(mediaInfo, coordinates); - }); - }, [dismissKeyboardIfShowing, overlayContext, props]); - - const onLayout = React.useCallback(() => {}, []); - - const overlayPosition = (() => { - const { visibleOverlays } = overlayContext; - for (const overlay of visibleOverlays) { - if ( - overlay.routeName === ImageModalRouteName && - overlay.presentedFrom === route.key && - overlay.routeKey === getMediaKey(props.item, props.mediaInfo) - ) { - return overlay.position; - } - } - return undefined; - })(); - - const animatedWrapperStyle = useAnimatedStyle(() => { - let opacity; - if (overlayPosition) { - opacity = - 1 - - interpolate( - overlayPosition.value, - [0.1, 0.11], - [0, 1], - Extrapolate.CLAMP, - ); - } else { - opacity = 1; - } - return { - opacity, - }; +const MultimediaMessageMultimedia: React.ComponentType = React.memo< + Props, + void, +>(function MultimediaMessageMultimedia(props: Props) { + const keyboardState = React.useContext(KeyboardContext); + const overlayContext = React.useContext(OverlayContext); + invariant( + overlayContext, + 'MultimediaMessageMultimedia should have OverlayContext', + ); + const route = useRoute(); + const viewRef = React.useRef>(); + + const dismissKeyboardIfShowing = React.useCallback((): boolean => { + return !!(keyboardState && keyboardState.dismissKeyboardIfShowing()); + }, [keyboardState]); + + const onPress = React.useCallback(() => { + const { + clickable, + verticalBounds, + onPressMultimedia, + setClickable, + mediaInfo, + } = props; + if (!clickable) { + return; + } + + if (dismissKeyboardIfShowing()) { + return; + } + + const view = viewRef.current; + if (!view || !verticalBounds) { + return; + } + + const measureCallback = onPressMultimedia; + if (!measureCallback) { + return; + } + + setClickable(false); + + overlayContext.setScrollBlockingModalStatus('open'); + + view.measure((x, y, width, height, pageX, pageY) => { + const coordinates = { x: pageX, y: pageY, width, height }; + measureCallback(mediaInfo, coordinates); }); - - const scrollWasDisabled = React.useRef(); - - React.useEffect(() => { - const scrollIsDisabled = - overlayContext.scrollBlockingModalStatus !== 'closed'; - if (!scrollIsDisabled && scrollWasDisabled.current) { - props.setClickable(true); + }, [dismissKeyboardIfShowing, overlayContext, props]); + + const onLayout = React.useCallback(() => {}, []); + + const overlayPosition = (() => { + const { visibleOverlays } = overlayContext; + for (const overlay of visibleOverlays) { + if ( + overlay.routeName === ImageModalRouteName && + overlay.presentedFrom === route.key && + overlay.routeKey === getMediaKey(props.item, props.mediaInfo) + ) { + return overlay.position; } - scrollWasDisabled.current = scrollIsDisabled; - }, [overlayContext.scrollBlockingModalStatus, props]); - - const { mediaInfo, postInProgress, pendingUpload, item, style } = props; - - const wrapperStyles = React.useMemo( - () => [styles.container, animatedWrapperStyle, style], - [animatedWrapperStyle, style], - ); - - return ( - - - - - - ); + } + return undefined; + })(); + + const animatedWrapperStyle = useAnimatedStyle(() => { + let opacity; + if (overlayPosition) { + opacity = + 1 - + interpolate( + overlayPosition.value, + [0.1, 0.11], + [0, 1], + Extrapolate.CLAMP, + ); + } else { + opacity = 1; + } + return { + opacity, + }; }); + const scrollWasDisabled = React.useRef(); + + React.useEffect(() => { + const scrollIsDisabled = + overlayContext.scrollBlockingModalStatus !== 'closed'; + if (!scrollIsDisabled && scrollWasDisabled.current) { + props.setClickable(true); + } + scrollWasDisabled.current = scrollIsDisabled; + }, [overlayContext.scrollBlockingModalStatus, props]); + + const { mediaInfo, postInProgress, pendingUpload, item, style } = props; + + const wrapperStyles = React.useMemo( + () => [styles.container, animatedWrapperStyle, style], + [animatedWrapperStyle, style], + ); + + return ( + + + + + + ); +}); + export default MultimediaMessageMultimedia; diff --git a/native/chat/multimedia-message.react.js b/native/chat/multimedia-message.react.js --- a/native/chat/multimedia-message.react.js +++ b/native/chat/multimedia-message.react.js @@ -241,37 +241,39 @@ } } -const ConnectedMultimediaMessage: React.ComponentType = - React.memo(function ConnectedMultimediaMessage(props: BaseProps) { - const navigation = useNavigation(); - const route = useRoute(); - const overlayContext = React.useContext(OverlayContext); - const chatContext = React.useContext(ChatContext); - const canCreateSidebarFromMessage = useCanCreateSidebarFromMessage( - props.item.threadInfo, - props.item.messageInfo, - ); - const canCreateReactionFromMessage = useCanCreateReactionFromMessage( - props.item.threadInfo, - props.item.messageInfo, - ); - const canDeleteMessage = useCanDeleteMessage( - props.item.threadInfo, - props.item.messageInfo, - !!props.item.threadCreatedFromMessage, - ); - return ( - - ); - }); +const ConnectedMultimediaMessage: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedMultimediaMessage(props: BaseProps) { + const navigation = useNavigation(); + const route = useRoute(); + const overlayContext = React.useContext(OverlayContext); + const chatContext = React.useContext(ChatContext); + const canCreateSidebarFromMessage = useCanCreateSidebarFromMessage( + props.item.threadInfo, + props.item.messageInfo, + ); + const canCreateReactionFromMessage = useCanCreateReactionFromMessage( + props.item.threadInfo, + props.item.messageInfo, + ); + const canDeleteMessage = useCanDeleteMessage( + props.item.threadInfo, + props.item.messageInfo, + !!props.item.threadCreatedFromMessage, + ); + return ( + + ); +}); export default ConnectedMultimediaMessage; diff --git a/native/chat/relationship-prompt.react.js b/native/chat/relationship-prompt.react.js --- a/native/chat/relationship-prompt.react.js +++ b/native/chat/relationship-prompt.react.js @@ -19,7 +19,7 @@ +threadInfo: ThreadInfo, }; -const RelationshipPrompt: React.ComponentType = React.memo( +const RelationshipPrompt: React.ComponentType = React.memo( function RelationshipPrompt({ pendingPersonalThreadUserInfo, threadInfo, diff --git a/native/chat/settings/add-users-modal.react.js b/native/chat/settings/add-users-modal.react.js --- a/native/chat/settings/add-users-modal.react.js +++ b/native/chat/settings/add-users-modal.react.js @@ -288,7 +288,9 @@ }, }; -const MemoizedAddUsersModal: React.ComponentType = - React.memo(AddUsersModal); +const MemoizedAddUsersModal: React.ComponentType = React.memo< + Props, + void, +>(AddUsersModal); export default MemoizedAddUsersModal; diff --git a/native/chat/settings/color-selector-modal.react.js b/native/chat/settings/color-selector-modal.react.js --- a/native/chat/settings/color-selector-modal.react.js +++ b/native/chat/settings/color-selector-modal.react.js @@ -172,25 +172,27 @@ ); } -const ConnectedColorSelectorModal: React.ComponentType = - React.memo(function ConnectedColorSelectorModal(props: BaseProps) { - const styles = useStyles(unboundStyles); - const colors = useColors(); - const windowWidth = useSelector(state => state.dimensions.width); - - const dispatchActionPromise = useDispatchActionPromise(); - const callChangeThreadSettings = useChangeThreadSettings(); - - return ( - - ); - }); +const ConnectedColorSelectorModal: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedColorSelectorModal(props: BaseProps) { + const styles = useStyles(unboundStyles); + const colors = useColors(); + const windowWidth = useSelector(state => state.dimensions.width); + + const dispatchActionPromise = useDispatchActionPromise(); + const callChangeThreadSettings = useChangeThreadSettings(); + + return ( + + ); +}); export default ConnectedColorSelectorModal; diff --git a/native/chat/settings/compose-subchannel-modal.react.js b/native/chat/settings/compose-subchannel-modal.react.js --- a/native/chat/settings/compose-subchannel-modal.react.js +++ b/native/chat/settings/compose-subchannel-modal.react.js @@ -138,7 +138,7 @@ } const ConnectedComposeSubchannelModal: React.ComponentType = - React.memo(function ConnectedComposeSubchannelModal( + React.memo(function ConnectedComposeSubchannelModal( props: BaseProps, ) { const styles = useStyles(unboundStyles); diff --git a/native/chat/settings/delete-thread.react.js b/native/chat/settings/delete-thread.react.js --- a/native/chat/settings/delete-thread.react.js +++ b/native/chat/settings/delete-thread.react.js @@ -232,53 +232,55 @@ deleteThreadActionTypes, ); -const ConnectedDeleteThread: React.ComponentType = - React.memo(function ConnectedDeleteThread(props: BaseProps) { - const threadID = props.route.params.threadInfo.id; - const reduxThreadInfo = useSelector( - state => threadInfoSelector(state)[threadID], - ); - const reduxContainedThreadInfos = useSelector( - state => containedThreadInfos(state)[threadID], - ); +const ConnectedDeleteThread: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedDeleteThread(props: BaseProps) { + const threadID = props.route.params.threadInfo.id; + const reduxThreadInfo = useSelector( + state => threadInfoSelector(state)[threadID], + ); + const reduxContainedThreadInfos = useSelector( + state => containedThreadInfos(state)[threadID], + ); - const { setParams } = props.navigation; - React.useEffect(() => { - if (reduxThreadInfo) { - setParams({ threadInfo: reduxThreadInfo }); - } - }, [reduxThreadInfo, setParams]); - const threadInfo = reduxThreadInfo ?? props.route.params.threadInfo; - const resolvedThreadInfo = useResolvedThreadInfo(threadInfo); + const { setParams } = props.navigation; + React.useEffect(() => { + if (reduxThreadInfo) { + setParams({ threadInfo: reduxThreadInfo }); + } + }, [reduxThreadInfo, setParams]); + const threadInfo = reduxThreadInfo ?? props.route.params.threadInfo; + const resolvedThreadInfo = useResolvedThreadInfo(threadInfo); - const loadingStatus = useSelector(loadingStatusSelector); + const loadingStatus = useSelector(loadingStatusSelector); - const colors = useColors(); - const styles = useStyles(unboundStyles); + const colors = useColors(); + const styles = useStyles(unboundStyles); - const dispatchActionPromise = useDispatchActionPromise(); - const callDeleteThread = useDeleteThread(); + const dispatchActionPromise = useDispatchActionPromise(); + const callDeleteThread = useDeleteThread(); - const navContext = React.useContext(NavContext); - invariant(navContext, 'NavContext should be set in DeleteThread'); - const navDispatch = navContext.dispatch; + const navContext = React.useContext(NavContext); + invariant(navContext, 'NavContext should be set in DeleteThread'); + const navDispatch = navContext.dispatch; - const shouldUseDeleteConfirmationAlert = - reduxContainedThreadInfos && reduxContainedThreadInfos.length > 0; + const shouldUseDeleteConfirmationAlert = + reduxContainedThreadInfos && reduxContainedThreadInfos.length > 0; - return ( - - ); - }); + return ( + + ); +}); export default ConnectedDeleteThread; diff --git a/native/chat/settings/thread-settings-avatar.react.js b/native/chat/settings/thread-settings-avatar.react.js --- a/native/chat/settings/thread-settings-avatar.react.js +++ b/native/chat/settings/thread-settings-avatar.react.js @@ -33,7 +33,9 @@ }, }; -const MemoizedThreadSettingsAvatar: React.ComponentType = - React.memo(ThreadSettingsAvatar); +const MemoizedThreadSettingsAvatar: React.ComponentType = React.memo< + Props, + void, +>(ThreadSettingsAvatar); export default MemoizedThreadSettingsAvatar; diff --git a/native/chat/settings/thread-settings-color.react.js b/native/chat/settings/thread-settings-color.react.js --- a/native/chat/settings/thread-settings-color.react.js +++ b/native/chat/settings/thread-settings-color.react.js @@ -99,27 +99,27 @@ }; } -const ConnectedThreadSettingsColor: React.ComponentType = - React.memo(function ConnectedThreadSettingsColor( - props: BaseProps, - ) { - const threadID = props.threadInfo.id; - const loadingStatus = useSelector( - createLoadingStatusSelector( - changeThreadSettingsActionTypes, - `${changeThreadSettingsActionTypes.started}:${threadID}:color`, - ), - ); - const colors = useColors(); - const styles = useStyles(unboundStyles); - return ( - - ); - }); +const ConnectedThreadSettingsColor: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedThreadSettingsColor(props: BaseProps) { + const threadID = props.threadInfo.id; + const loadingStatus = useSelector( + createLoadingStatusSelector( + changeThreadSettingsActionTypes, + `${changeThreadSettingsActionTypes.started}:${threadID}:color`, + ), + ); + const colors = useColors(); + const styles = useStyles(unboundStyles); + return ( + + ); +}); export default ConnectedThreadSettingsColor; diff --git a/native/chat/settings/thread-settings-description.react.js b/native/chat/settings/thread-settings-description.react.js --- a/native/chat/settings/thread-settings-description.react.js +++ b/native/chat/settings/thread-settings-description.react.js @@ -291,7 +291,7 @@ } const ConnectedThreadSettingsDescription: React.ComponentType = - React.memo(function ConnectedThreadSettingsDescription( + React.memo(function ConnectedThreadSettingsDescription( props: BaseProps, ) { const threadID = props.threadInfo.id; diff --git a/native/chat/settings/thread-settings-edit-relationship.react.js b/native/chat/settings/thread-settings-edit-relationship.react.js --- a/native/chat/settings/thread-settings-edit-relationship.react.js +++ b/native/chat/settings/thread-settings-edit-relationship.react.js @@ -32,84 +32,85 @@ +relationshipButton: RelationshipButton, }; -const ThreadSettingsEditRelationship: React.ComponentType = - React.memo(function ThreadSettingsEditRelationship(props: Props) { - const otherUserInfoFromRedux = useSelector(state => { - const currentUserID = state.currentUserInfo?.id; - const otherUserID = getSingleOtherUser(props.threadInfo, currentUserID); - invariant(otherUserID, 'Other user should be specified'); - - const { userInfos } = state.userStore; - return userInfos[otherUserID]; - }); - invariant(otherUserInfoFromRedux, 'Other user info should be specified'); - - const ensNames = React.useMemo( - () => [otherUserInfoFromRedux], - [otherUserInfoFromRedux], - ); - const [otherUserInfo] = useENSNames(ensNames); - - const updateRelationships = useUpdateRelationships(); - const updateRelationship = React.useCallback( - async (action: TraditionalRelationshipAction) => { - try { - return await updateRelationships(action, [otherUserInfo.id]); - } catch (e) { - Alert.alert( - unknownErrorAlertDetails.title, - unknownErrorAlertDetails.message, - [{ text: 'OK' }], - { - cancelable: true, - }, - ); - throw e; - } - }, - [updateRelationships, otherUserInfo], - ); - - const { relationshipButton } = props; - const relationshipAction = React.useMemo( - () => getRelationshipDispatchAction(relationshipButton), - [relationshipButton], - ); - - const dispatchActionPromise = useDispatchActionPromise(); - const onButtonPress = React.useCallback(() => { - void dispatchActionPromise( - updateRelationshipsActionTypes, - updateRelationship(relationshipAction), - ); - }, [dispatchActionPromise, relationshipAction, updateRelationship]); - - const colors = useColors(); - const { panelIosHighlightUnderlay } = colors; - - const styles = useStyles(unboundStyles); - const otherUserInfoUsername = otherUserInfo.username; - invariant(otherUserInfoUsername, 'Other user username should be specified'); - - const relationshipButtonText = React.useMemo( - () => - getRelationshipActionText(relationshipButton, otherUserInfoUsername), - [otherUserInfoUsername, relationshipButton], - ); - - return ( - - - - ); +const ThreadSettingsEditRelationship: React.ComponentType = React.memo< + Props, + void, +>(function ThreadSettingsEditRelationship(props: Props) { + const otherUserInfoFromRedux = useSelector(state => { + const currentUserID = state.currentUserInfo?.id; + const otherUserID = getSingleOtherUser(props.threadInfo, currentUserID); + invariant(otherUserID, 'Other user should be specified'); + + const { userInfos } = state.userStore; + return userInfos[otherUserID]; }); + invariant(otherUserInfoFromRedux, 'Other user info should be specified'); + + const ensNames = React.useMemo( + () => [otherUserInfoFromRedux], + [otherUserInfoFromRedux], + ); + const [otherUserInfo] = useENSNames(ensNames); + + const updateRelationships = useUpdateRelationships(); + const updateRelationship = React.useCallback( + async (action: TraditionalRelationshipAction) => { + try { + return await updateRelationships(action, [otherUserInfo.id]); + } catch (e) { + Alert.alert( + unknownErrorAlertDetails.title, + unknownErrorAlertDetails.message, + [{ text: 'OK' }], + { + cancelable: true, + }, + ); + throw e; + } + }, + [updateRelationships, otherUserInfo], + ); + + const { relationshipButton } = props; + const relationshipAction = React.useMemo( + () => getRelationshipDispatchAction(relationshipButton), + [relationshipButton], + ); + + const dispatchActionPromise = useDispatchActionPromise(); + const onButtonPress = React.useCallback(() => { + void dispatchActionPromise( + updateRelationshipsActionTypes, + updateRelationship(relationshipAction), + ); + }, [dispatchActionPromise, relationshipAction, updateRelationship]); + + const colors = useColors(); + const { panelIosHighlightUnderlay } = colors; + + const styles = useStyles(unboundStyles); + const otherUserInfoUsername = otherUserInfo.username; + invariant(otherUserInfoUsername, 'Other user username should be specified'); + + const relationshipButtonText = React.useMemo( + () => getRelationshipActionText(relationshipButton, otherUserInfoUsername), + [otherUserInfoUsername, relationshipButton], + ); + + return ( + + + + ); +}); const unboundStyles = { button: { diff --git a/native/chat/settings/thread-settings-leave-thread.react.js b/native/chat/settings/thread-settings-leave-thread.react.js --- a/native/chat/settings/thread-settings-leave-thread.react.js +++ b/native/chat/settings/thread-settings-leave-thread.react.js @@ -135,7 +135,7 @@ } const ConnectedThreadSettingsLeaveThread: React.ComponentType = - React.memo(function ConnectedThreadSettingsLeaveThread( + React.memo(function ConnectedThreadSettingsLeaveThread( props: BaseProps, ) { const threadID = props.threadInfo.id; diff --git a/native/chat/settings/thread-settings-member.react.js b/native/chat/settings/thread-settings-member.react.js --- a/native/chat/settings/thread-settings-member.react.js +++ b/native/chat/settings/thread-settings-member.react.js @@ -242,7 +242,7 @@ } const ConnectedThreadSettingsMember: React.ComponentType = - React.memo(function ConnectedThreadSettingsMember( + React.memo(function ConnectedThreadSettingsMember( props: BaseProps, ) { const memberID = props.memberInfo.id; diff --git a/native/chat/settings/thread-settings-name.react.js b/native/chat/settings/thread-settings-name.react.js --- a/native/chat/settings/thread-settings-name.react.js +++ b/native/chat/settings/thread-settings-name.react.js @@ -215,32 +215,34 @@ }; } -const ConnectedThreadSettingsName: React.ComponentType = - React.memo(function ConnectedThreadSettingsName(props: BaseProps) { - const styles = useStyles(unboundStyles); - const colors = useColors(); - - const threadID = props.threadInfo.id; - const loadingStatus = useSelector( - createLoadingStatusSelector( - changeThreadSettingsActionTypes, - `${changeThreadSettingsActionTypes.started}:${threadID}:name`, - ), - ); - - const dispatchActionPromise = useDispatchActionPromise(); - const callChangeThreadSettings = useChangeThreadSettings(); - - return ( - - ); - }); +const ConnectedThreadSettingsName: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedThreadSettingsName(props: BaseProps) { + const styles = useStyles(unboundStyles); + const colors = useColors(); + + const threadID = props.threadInfo.id; + const loadingStatus = useSelector( + createLoadingStatusSelector( + changeThreadSettingsActionTypes, + `${changeThreadSettingsActionTypes.started}:${threadID}:name`, + ), + ); + + const dispatchActionPromise = useDispatchActionPromise(); + const callChangeThreadSettings = useChangeThreadSettings(); + + return ( + + ); +}); export default ConnectedThreadSettingsName; diff --git a/native/chat/settings/thread-settings-parent.react.js b/native/chat/settings/thread-settings-parent.react.js --- a/native/chat/settings/thread-settings-parent.react.js +++ b/native/chat/settings/thread-settings-parent.react.js @@ -110,6 +110,6 @@ }; const ConnectedThreadSettingsParent: React.ComponentType = - React.memo(ThreadSettingsParent); + React.memo(ThreadSettingsParent); export default ConnectedThreadSettingsParent; diff --git a/native/chat/settings/thread-settings-promote-sidebar.react.js b/native/chat/settings/thread-settings-promote-sidebar.react.js --- a/native/chat/settings/thread-settings-promote-sidebar.react.js +++ b/native/chat/settings/thread-settings-promote-sidebar.react.js @@ -95,7 +95,7 @@ }; const ConnectedThreadSettingsPromoteSidebar: React.ComponentType = - React.memo(function ConnectedThreadSettingsPromoteSidebar( + React.memo(function ConnectedThreadSettingsPromoteSidebar( props: BaseProps, ) { const { threadInfo } = props; diff --git a/native/chat/settings/thread-settings-push-notifs.react.js b/native/chat/settings/thread-settings-push-notifs.react.js --- a/native/chat/settings/thread-settings-push-notifs.react.js +++ b/native/chat/settings/thread-settings-push-notifs.react.js @@ -140,7 +140,7 @@ } const ConnectedThreadSettingsPushNotifs: React.ComponentType = - React.memo(function ConnectedThreadSettingsPushNotifs( + React.memo(function ConnectedThreadSettingsPushNotifs( props: BaseProps, ) { const keyserverID = extractKeyserverIDFromIDOptional(props.threadInfo.id); diff --git a/native/chat/settings/thread-settings.react.js b/native/chat/settings/thread-settings.react.js --- a/native/chat/settings/thread-settings.react.js +++ b/native/chat/settings/thread-settings.react.js @@ -1150,202 +1150,204 @@ return false; }; -const ConnectedThreadSettings: React.ComponentType = - React.memo(function ConnectedThreadSettings(props: BaseProps) { - const userInfos = useSelector(state => state.userStore.userInfos); - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, +const ConnectedThreadSettings: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedThreadSettings(props: BaseProps) { + const userInfos = useSelector(state => state.userStore.userInfos); + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const threadID = props.route.params.threadInfo.id; + + const reduxThreadInfo: ?ThreadInfo = useSelector( + state => threadInfoSelector(state)[threadID], + ); + React.useEffect(() => { + invariant( + reduxThreadInfo, + 'ReduxThreadInfo should exist when ThreadSettings is opened', ); - const threadID = props.route.params.threadInfo.id; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - const reduxThreadInfo: ?ThreadInfo = useSelector( - state => threadInfoSelector(state)[threadID], - ); - React.useEffect(() => { - invariant( - reduxThreadInfo, - 'ReduxThreadInfo should exist when ThreadSettings is opened', - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const { setParams } = props.navigation; - React.useEffect(() => { - if (reduxThreadInfo) { - setParams({ threadInfo: reduxThreadInfo }); - } - }, [reduxThreadInfo, setParams]); - const threadInfo: ThreadInfo = - reduxThreadInfo ?? props.route.params.threadInfo; - const resolvedThreadInfo = useResolvedThreadInfo(threadInfo); - - const isThreadInChatList = useIsThreadInChatList(threadInfo); - React.useEffect(() => { - if (isThreadInChatList) { - return undefined; - } - threadWatcher.watchID(threadInfo.id); - return () => { - threadWatcher.removeID(threadInfo.id); - }; - }, [isThreadInChatList, threadInfo.id]); - - const parentThreadID = threadInfo.parentThreadID; - const parentThreadInfo: ?ThreadInfo = useSelector(state => - parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, - ); - const resolvedParentThreadInfo = - useResolvedOptionalThreadInfo(parentThreadInfo); - const threadMembers = threadInfo.members; - const boundChildThreadInfos = useSelector( - state => childThreadInfos(state)[threadID], - ); - const resolvedChildThreadInfos = useResolvedOptionalThreadInfos( - boundChildThreadInfos, - ); - - const somethingIsSaving = useSelector(state => { - const editNameLoadingStatus = createLoadingStatusSelector( - changeThreadSettingsActionTypes, - `${changeThreadSettingsActionTypes.started}:${threadID}:name`, - )(state); - - const editColorLoadingStatus = createLoadingStatusSelector( - changeThreadSettingsActionTypes, - `${changeThreadSettingsActionTypes.started}:${threadID}:color`, - )(state); - - const editDescriptionLoadingStatus = createLoadingStatusSelector( - changeThreadSettingsActionTypes, - `${changeThreadSettingsActionTypes.started}:${threadID}:description`, - )(state); - - const leaveThreadLoadingStatus = createLoadingStatusSelector( - leaveThreadActionTypes, - `${leaveThreadActionTypes.started}:${threadID}`, - )(state); - - const boundThreadMembersChangeIsSaving = threadMembersChangeIsSaving( - state, - threadMembers, - ); - - return ( - boundThreadMembersChangeIsSaving || - editNameLoadingStatus === 'loading' || - editColorLoadingStatus === 'loading' || - editDescriptionLoadingStatus === 'loading' || - leaveThreadLoadingStatus === 'loading' - ); - }); - - const { navigation } = props; - React.useEffect(() => { - const tabNavigation = navigation.getParent< - ScreenParamList, - 'Chat', - TabNavigationState, - BottomTabOptions, - BottomTabNavigationEventMap, - TabNavigationProp<'Chat'>, - >(); - invariant(tabNavigation, 'ChatNavigator should be within TabNavigator'); - - const onTabPress = () => { - if (navigation.isFocused() && !somethingIsSaving) { - navigation.popToTop(); - } - }; - - tabNavigation.addListener('tabPress', onTabPress); - return () => tabNavigation.removeListener('tabPress', onTabPress); - }, [navigation, somethingIsSaving]); - - const styles = useStyles(unboundStyles); - const indicatorStyle = useIndicatorStyle(); - const overlayContext = React.useContext(OverlayContext); - const keyboardState = React.useContext(KeyboardContext); - const { canPromoteSidebar } = usePromoteSidebar(threadInfo); - - const canEditThreadAvatar = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_AVATAR, - ); - - const canEditThreadName = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_NAME, - ); + const { setParams } = props.navigation; + React.useEffect(() => { + if (reduxThreadInfo) { + setParams({ threadInfo: reduxThreadInfo }); + } + }, [reduxThreadInfo, setParams]); + const threadInfo: ThreadInfo = + reduxThreadInfo ?? props.route.params.threadInfo; + const resolvedThreadInfo = useResolvedThreadInfo(threadInfo); + + const isThreadInChatList = useIsThreadInChatList(threadInfo); + React.useEffect(() => { + if (isThreadInChatList) { + return undefined; + } + threadWatcher.watchID(threadInfo.id); + return () => { + threadWatcher.removeID(threadInfo.id); + }; + }, [isThreadInChatList, threadInfo.id]); + + const parentThreadID = threadInfo.parentThreadID; + const parentThreadInfo: ?ThreadInfo = useSelector(state => + parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, + ); + const resolvedParentThreadInfo = + useResolvedOptionalThreadInfo(parentThreadInfo); + const threadMembers = threadInfo.members; + const boundChildThreadInfos = useSelector( + state => childThreadInfos(state)[threadID], + ); + const resolvedChildThreadInfos = useResolvedOptionalThreadInfos( + boundChildThreadInfos, + ); + + const somethingIsSaving = useSelector(state => { + const editNameLoadingStatus = createLoadingStatusSelector( + changeThreadSettingsActionTypes, + `${changeThreadSettingsActionTypes.started}:${threadID}:name`, + )(state); - const canEditThreadDescription = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_DESCRIPTION, - ); + const editColorLoadingStatus = createLoadingStatusSelector( + changeThreadSettingsActionTypes, + `${changeThreadSettingsActionTypes.started}:${threadID}:color`, + )(state); - const canEditThreadColor = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_COLOR, - ); + const editDescriptionLoadingStatus = createLoadingStatusSelector( + changeThreadSettingsActionTypes, + `${changeThreadSettingsActionTypes.started}:${threadID}:description`, + )(state); - const canCreateSubchannels = useThreadHasPermission( - threadInfo, - threadPermissions.CREATE_SUBCHANNELS, - ); + const leaveThreadLoadingStatus = createLoadingStatusSelector( + leaveThreadActionTypes, + `${leaveThreadActionTypes.started}:${threadID}`, + )(state); - const canLeaveThread = useThreadHasPermission( - threadInfo, - threadPermissions.LEAVE_THREAD, + const boundThreadMembersChangeIsSaving = threadMembersChangeIsSaving( + state, + threadMembers, ); - const canDeleteThread = useThreadHasPermission( - threadInfo, - threadPermissions.DELETE_THREAD, + return ( + boundThreadMembersChangeIsSaving || + editNameLoadingStatus === 'loading' || + editColorLoadingStatus === 'loading' || + editDescriptionLoadingStatus === 'loading' || + leaveThreadLoadingStatus === 'loading' ); + }); - const { inviteLink, canManageLinks, canAddMembers, isCommunityRoot } = - useAddUsersPermissions(threadInfo); - - const callFetchPrimaryLinks = useFetchPrimaryInviteLinks(); - const dispatchActionPromise = useDispatchActionPromise(); - // Because we don't support updates and persistance for invite links, - // we have to fetch them whenever we want to display them. - // Here we need invite links for the "Add users" button in ThreadSettings - React.useEffect(() => { - if (!isCommunityRoot) { - return; + const { navigation } = props; + React.useEffect(() => { + const tabNavigation = navigation.getParent< + ScreenParamList, + 'Chat', + TabNavigationState, + BottomTabOptions, + BottomTabNavigationEventMap, + TabNavigationProp<'Chat'>, + >(); + invariant(tabNavigation, 'ChatNavigator should be within TabNavigator'); + + const onTabPress = () => { + if (navigation.isFocused() && !somethingIsSaving) { + navigation.popToTop(); } - void dispatchActionPromise( - fetchPrimaryInviteLinkActionTypes, - callFetchPrimaryLinks(), - ); - }, [callFetchPrimaryLinks, dispatchActionPromise, isCommunityRoot]); + }; - return ( - + tabNavigation.addListener('tabPress', onTabPress); + return () => tabNavigation.removeListener('tabPress', onTabPress); + }, [navigation, somethingIsSaving]); + + const styles = useStyles(unboundStyles); + const indicatorStyle = useIndicatorStyle(); + const overlayContext = React.useContext(OverlayContext); + const keyboardState = React.useContext(KeyboardContext); + const { canPromoteSidebar } = usePromoteSidebar(threadInfo); + + const canEditThreadAvatar = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_AVATAR, + ); + + const canEditThreadName = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_NAME, + ); + + const canEditThreadDescription = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_DESCRIPTION, + ); + + const canEditThreadColor = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_COLOR, + ); + + const canCreateSubchannels = useThreadHasPermission( + threadInfo, + threadPermissions.CREATE_SUBCHANNELS, + ); + + const canLeaveThread = useThreadHasPermission( + threadInfo, + threadPermissions.LEAVE_THREAD, + ); + + const canDeleteThread = useThreadHasPermission( + threadInfo, + threadPermissions.DELETE_THREAD, + ); + + const { inviteLink, canManageLinks, canAddMembers, isCommunityRoot } = + useAddUsersPermissions(threadInfo); + + const callFetchPrimaryLinks = useFetchPrimaryInviteLinks(); + const dispatchActionPromise = useDispatchActionPromise(); + // Because we don't support updates and persistance for invite links, + // we have to fetch them whenever we want to display them. + // Here we need invite links for the "Add users" button in ThreadSettings + React.useEffect(() => { + if (!isCommunityRoot) { + return; + } + void dispatchActionPromise( + fetchPrimaryInviteLinkActionTypes, + callFetchPrimaryLinks(), ); - }); + }, [callFetchPrimaryLinks, dispatchActionPromise, isCommunityRoot]); + + return ( + + ); +}); export default ConnectedThreadSettings; diff --git a/native/chat/text-message.react.js b/native/chat/text-message.react.js --- a/native/chat/text-message.react.js +++ b/native/chat/text-message.react.js @@ -257,67 +257,69 @@ }; } -const ConnectedTextMessage: React.ComponentType = - React.memo(function ConnectedTextMessage(props: BaseProps) { - const overlayContext = React.useContext(OverlayContext); - const chatContext = React.useContext(ChatContext); - const markdownContext = React.useContext(MarkdownContext); - invariant(markdownContext, 'markdownContext should be set'); - - const { - linkModalActive, - userProfileBottomSheetActive, - clearMarkdownContextData, - } = markdownContext; - - const key = messageKey(props.item.messageInfo); - - // We check if there is an key in the object - if not, we - // default to false. The likely situation where the former statement - // evaluates to null is when the thread is opened for the first time. - const isLinkModalActive = linkModalActive[key] ?? false; - const isUserProfileBottomSheetActive = - userProfileBottomSheetActive[key] ?? false; - - const canCreateSidebarFromMessage = useCanCreateSidebarFromMessage( - props.item.threadInfo, - props.item.messageInfo, - ); - - const messageEditingContext = React.useContext(MessageEditingContext); - const editMessageID = messageEditingContext?.editState.editedMessage?.id; - const isThisMessageEdited = editMessageID === props.item.messageInfo.id; - - const canEditMessage = - useCanEditMessageNative(props.item.threadInfo, props.item.messageInfo) && - !isThisMessageEdited; - - React.useEffect(() => clearMarkdownContextData, [clearMarkdownContextData]); - - const currentUserCanReply = useThreadHasPermission( - props.item.threadInfo, - threadPermissions.VOICED, - ); - - const canDeleteMessage = useCanDeleteMessage( - props.item.threadInfo, - props.item.messageInfo, - !!props.item.threadCreatedFromMessage, - ); - - return ( - - ); - }); +const ConnectedTextMessage: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedTextMessage(props: BaseProps) { + const overlayContext = React.useContext(OverlayContext); + const chatContext = React.useContext(ChatContext); + const markdownContext = React.useContext(MarkdownContext); + invariant(markdownContext, 'markdownContext should be set'); + + const { + linkModalActive, + userProfileBottomSheetActive, + clearMarkdownContextData, + } = markdownContext; + + const key = messageKey(props.item.messageInfo); + + // We check if there is an key in the object - if not, we + // default to false. The likely situation where the former statement + // evaluates to null is when the thread is opened for the first time. + const isLinkModalActive = linkModalActive[key] ?? false; + const isUserProfileBottomSheetActive = + userProfileBottomSheetActive[key] ?? false; + + const canCreateSidebarFromMessage = useCanCreateSidebarFromMessage( + props.item.threadInfo, + props.item.messageInfo, + ); + + const messageEditingContext = React.useContext(MessageEditingContext); + const editMessageID = messageEditingContext?.editState.editedMessage?.id; + const isThisMessageEdited = editMessageID === props.item.messageInfo.id; + + const canEditMessage = + useCanEditMessageNative(props.item.threadInfo, props.item.messageInfo) && + !isThisMessageEdited; + + React.useEffect(() => clearMarkdownContextData, [clearMarkdownContextData]); + + const currentUserCanReply = useThreadHasPermission( + props.item.threadInfo, + threadPermissions.VOICED, + ); + + const canDeleteMessage = useCanDeleteMessage( + props.item.threadInfo, + props.item.messageInfo, + !!props.item.threadCreatedFromMessage, + ); + + return ( + + ); +}); export { ConnectedTextMessage as TextMessage }; diff --git a/native/chat/thread-screen-pruner.react.js b/native/chat/thread-screen-pruner.react.js --- a/native/chat/thread-screen-pruner.react.js +++ b/native/chat/thread-screen-pruner.react.js @@ -22,7 +22,7 @@ import type { AppState } from '../redux/state-types.js'; import Alert from '../utils/alert.js'; -const ThreadScreenPruner: React.ComponentType<{}> = React.memo<{}>( +const ThreadScreenPruner: React.ComponentType<{}> = React.memo<{}, void>( function ThreadScreenPruner() { const rawThreadInfos = useSelector( (state: AppState) => state.threadStore.threadInfos, diff --git a/native/chat/thread-settings-button.react.js b/native/chat/thread-settings-button.react.js --- a/native/chat/thread-settings-button.react.js +++ b/native/chat/thread-settings-button.react.js @@ -50,7 +50,7 @@ } const ConnectedThreadSettingsButton: React.ComponentType = - React.memo(function ConnectedThreadSettingsButton( + React.memo(function ConnectedThreadSettingsButton( props: BaseProps, ) { const styles = useStyles(unboundStyles); diff --git a/native/chat/thread-settings-header-title.react.js b/native/chat/thread-settings-header-title.react.js --- a/native/chat/thread-settings-header-title.react.js +++ b/native/chat/thread-settings-header-title.react.js @@ -19,6 +19,6 @@ } const MemoizedThreadSettingsHeaderTitle: React.ComponentType = - React.memo(ThreadSettingsHeaderTitle); + React.memo(ThreadSettingsHeaderTitle); export default MemoizedThreadSettingsHeaderTitle; diff --git a/native/components/community-list-item.react.js b/native/components/community-list-item.react.js --- a/native/components/community-list-item.react.js +++ b/native/components/community-list-item.react.js @@ -190,8 +190,10 @@ ); } -const MemoizedCommunityListItem: React.ComponentType = - React.memo(CommunityListItem); +const MemoizedCommunityListItem: React.ComponentType = React.memo< + Props, + void, +>(CommunityListItem); const unboundStyles = { activityIndicatorContainer: { diff --git a/native/components/full-screen-view-modal.react.js b/native/components/full-screen-view-modal.react.js --- a/native/components/full-screen-view-modal.react.js +++ b/native/components/full-screen-view-modal.react.js @@ -876,7 +876,9 @@ }, }); -const MemoizedFullScreenViewModal: React.ComponentType = - React.memo(FullScreenViewModal); +const MemoizedFullScreenViewModal: React.ComponentType = React.memo< + Props, + void, +>(FullScreenViewModal); export default MemoizedFullScreenViewModal; diff --git a/native/components/keyboard-avoiding-view.react.js b/native/components/keyboard-avoiding-view.react.js --- a/native/components/keyboard-avoiding-view.react.js +++ b/native/components/keyboard-avoiding-view.react.js @@ -29,13 +29,13 @@ +behavior: 'height' | 'position' | 'padding', +contentContainerStyle?: ?ViewStyle, }; -const KeyboardAvoidingView: React.ComponentType = - React.memo(function KeyboardAvoidingView(props: BaseProps) { - const keyboardState = React.useContext(KeyboardContext); - return ( - - ); - }); +const KeyboardAvoidingView: React.ComponentType = React.memo< + BaseProps, + void, +>(function KeyboardAvoidingView(props: BaseProps) { + const keyboardState = React.useContext(KeyboardContext); + return ; +}); type Props = { ...BaseProps, diff --git a/native/components/link-button.react.js b/native/components/link-button.react.js --- a/native/components/link-button.react.js +++ b/native/components/link-button.react.js @@ -49,11 +49,13 @@ } } -const ConnectedLinkButton: React.ComponentType = - React.memo(function ConnectedLinkButton(props: BaseProps) { - const styles = useStyles(unboundStyles); +const ConnectedLinkButton: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedLinkButton(props: BaseProps) { + const styles = useStyles(unboundStyles); - return ; - }); + return ; +}); export default ConnectedLinkButton; diff --git a/native/components/search.react.js b/native/components/search.react.js --- a/native/components/search.react.js +++ b/native/components/search.react.js @@ -144,7 +144,7 @@ const MemoizedSearch: SearchComponentType = React.memo< Props, - React.ElementRef, + React.RefSetter>, >(Search); export default MemoizedSearch; diff --git a/native/components/selectable-text-input.react.ios.js b/native/components/selectable-text-input.react.ios.js --- a/native/components/selectable-text-input.react.ios.js +++ b/native/components/selectable-text-input.react.ios.js @@ -137,7 +137,7 @@ ); const MemoizedSelectableTextInput: MemoizedSelectableTextInputComponent = - React.memo( + React.memo>( SelectableTextInput, ); diff --git a/native/components/selectable-text-input.react.js b/native/components/selectable-text-input.react.js --- a/native/components/selectable-text-input.react.js +++ b/native/components/selectable-text-input.react.js @@ -82,7 +82,7 @@ ); const MemoizedSelectableTextInput: MemoizedSelectableTextInputComponent = - React.memo( + React.memo>( SelectableTextInput, ); diff --git a/native/components/thread-list-thread.react.js b/native/components/thread-list-thread.react.js --- a/native/components/thread-list-thread.react.js +++ b/native/components/thread-list-thread.react.js @@ -69,21 +69,23 @@ }; } -const ConnectedThreadListThread: React.ComponentType = - React.memo(function ConnectedThreadListThread(props: BaseProps) { - const { threadInfo, ...rest } = props; - const styles = useStyles(unboundStyles); - const colors = useColors(); - const resolvedThreadInfo = useResolvedThreadInfo(threadInfo); +const ConnectedThreadListThread: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedThreadListThread(props: BaseProps) { + const { threadInfo, ...rest } = props; + const styles = useStyles(unboundStyles); + const colors = useColors(); + const resolvedThreadInfo = useResolvedThreadInfo(threadInfo); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedThreadListThread; diff --git a/native/components/thread-list.react.js b/native/components/thread-list.react.js --- a/native/components/thread-list.react.js +++ b/native/components/thread-list.react.js @@ -144,14 +144,16 @@ }; } -const ConnectedThreadList: React.ComponentType = - React.memo(function ConnectedThreadList(props: BaseProps) { - const styles = useStyles(unboundStyles); - const indicatorStyle = useIndicatorStyle(); - - return ( - - ); - }); +const ConnectedThreadList: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedThreadList(props: BaseProps) { + const styles = useStyles(unboundStyles); + const indicatorStyle = useIndicatorStyle(); + + return ( + + ); +}); export default ConnectedThreadList; diff --git a/native/components/user-list-user.react.js b/native/components/user-list-user.react.js --- a/native/components/user-list-user.react.js +++ b/native/components/user-list-user.react.js @@ -88,11 +88,13 @@ }; } -const ConnectedUserListUser: React.ComponentType = - React.memo(function ConnectedUserListUser(props: BaseProps) { - const colors = useColors(); - const styles = useStyles(unboundStyles); - return ; - }); +const ConnectedUserListUser: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedUserListUser(props: BaseProps) { + const colors = useColors(); + const styles = useStyles(unboundStyles); + return ; +}); export { ConnectedUserListUser as UserListUser, getUserListItemHeight }; diff --git a/native/components/user-list.react.js b/native/components/user-list.react.js --- a/native/components/user-list.react.js +++ b/native/components/user-list.react.js @@ -68,11 +68,12 @@ }; } -const ConnectedUserList: React.ComponentType = React.memo( - function ConnectedUserList(props: BaseProps) { - const indicatorStyle = useIndicatorStyle(); - return ; - }, -); +const ConnectedUserList: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedUserList(props: BaseProps) { + const indicatorStyle = useIndicatorStyle(); + return ; +}); export default ConnectedUserList; diff --git a/native/crash.react.js b/native/crash.react.js --- a/native/crash.react.js +++ b/native/crash.react.js @@ -281,20 +281,21 @@ }, }); -const ConnectedCrash: React.ComponentType = React.memo( - function ConnectedCrash(props: BaseProps) { - const dispatchActionPromise = useDispatchActionPromise(); - const callLogOut = useLogOut(); - const crashReportingEnabled = useIsReportEnabled('crashReports'); - return ( - - ); - }, -); +const ConnectedCrash: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedCrash(props: BaseProps) { + const dispatchActionPromise = useDispatchActionPromise(); + const callLogOut = useLogOut(); + const crashReportingEnabled = useIsReportEnabled('crashReports'); + return ( + + ); +}); export default ConnectedCrash; diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js --- a/native/input/input-state-container.react.js +++ b/native/input/input-state-container.react.js @@ -1656,57 +1656,57 @@ sendTextMessageActionTypes, ); -const ConnectedInputStateContainer: React.ComponentType = - React.memo(function ConnectedInputStateContainer( - props: BaseProps, - ) { - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); - const messageStoreMessages = useSelector( - state => state.messageStore.messages, - ); - const ongoingMessageCreation = useSelector( - state => - combineLoadingStatuses( - mediaCreationLoadingStatusSelector(state), - textCreationLoadingStatusSelector(state), - ) === 'loading', - ); - const hasWiFi = useSelector(state => state.connectivity.hasWiFi); - const calendarQuery = useCalendarQuery(); - const callBlobServiceUpload = useBlobServiceUpload(); - const callSendMultimediaMessage = - useInputStateContainerSendMultimediaMessage(); - const callSendTextMessage = useInputStateContainerSendTextMessage(); - const callNewThinThread = useNewThinThread(); - const callNewThickThread = useNewThickThread(); - const dispatchActionPromise = useDispatchActionPromise(); - const dispatch = useDispatch(); - const mediaReportsEnabled = useIsReportEnabled('mediaReports'); - const staffCanSee = useStaffCanSee(); - const callInvalidTokenLogOut = useInvalidCSATLogOut(); - - return ( - - ); - }); +const ConnectedInputStateContainer: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedInputStateContainer(props: BaseProps) { + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const messageStoreMessages = useSelector( + state => state.messageStore.messages, + ); + const ongoingMessageCreation = useSelector( + state => + combineLoadingStatuses( + mediaCreationLoadingStatusSelector(state), + textCreationLoadingStatusSelector(state), + ) === 'loading', + ); + const hasWiFi = useSelector(state => state.connectivity.hasWiFi); + const calendarQuery = useCalendarQuery(); + const callBlobServiceUpload = useBlobServiceUpload(); + const callSendMultimediaMessage = + useInputStateContainerSendMultimediaMessage(); + const callSendTextMessage = useInputStateContainerSendTextMessage(); + const callNewThinThread = useNewThinThread(); + const callNewThickThread = useNewThickThread(); + const dispatchActionPromise = useDispatchActionPromise(); + const dispatch = useDispatch(); + const mediaReportsEnabled = useIsReportEnabled('mediaReports'); + const staffCanSee = useStaffCanSee(); + const callInvalidTokenLogOut = useInvalidCSATLogOut(); + + return ( + + ); +}); export default ConnectedInputStateContainer; diff --git a/native/keyboard/keyboard-input-host.react.js b/native/keyboard/keyboard-input-host.react.js --- a/native/keyboard/keyboard-input-host.react.js +++ b/native/keyboard/keyboard-input-host.react.js @@ -100,24 +100,26 @@ }; } -const ConnectedKeyboardInputHost: React.ComponentType = - React.memo(function ConnectedKeyboardInputHost(props: BaseProps) { - const inputState = React.useContext(InputStateContext); - const keyboardState = React.useContext(KeyboardContext); - invariant(keyboardState, 'keyboardState should be initialized'); - const navContext = React.useContext(NavContext); - const styles = useStyles(unboundStyles); - const activeMessageList = activeMessageListSelector(navContext); +const ConnectedKeyboardInputHost: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedKeyboardInputHost(props: BaseProps) { + const inputState = React.useContext(InputStateContext); + const keyboardState = React.useContext(KeyboardContext); + invariant(keyboardState, 'keyboardState should be initialized'); + const navContext = React.useContext(NavContext); + const styles = useStyles(unboundStyles); + const activeMessageList = activeMessageListSelector(navContext); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedKeyboardInputHost; diff --git a/native/lifecycle/lifecycle-handler.react.js b/native/lifecycle/lifecycle-handler.react.js --- a/native/lifecycle/lifecycle-handler.react.js +++ b/native/lifecycle/lifecycle-handler.react.js @@ -10,7 +10,7 @@ import { appBecameInactive } from '../redux/redux-setup.js'; import { useSelector } from '../redux/redux-utils.js'; -const LifecycleHandler: React.ComponentType<{}> = React.memo<{}>( +const LifecycleHandler: React.ComponentType<{}> = React.memo<{}, void>( function LifecycleHandler() { const dispatch = useDispatch(); diff --git a/native/media/camera-modal.react.js b/native/media/camera-modal.react.js --- a/native/media/camera-modal.react.js +++ b/native/media/camera-modal.react.js @@ -259,7 +259,7 @@ }, }); -const CameraModal: React.ComponentType = React.memo( +const CameraModal: React.ComponentType = React.memo( function CameraModal(props: Props) { const deviceCameraInfo = useSelector(state => state.deviceCameraInfo); const deviceOrientation = useSelector(state => state.deviceOrientation); diff --git a/native/media/multimedia.react.js b/native/media/multimedia.react.js --- a/native/media/multimedia.react.js +++ b/native/media/multimedia.react.js @@ -230,10 +230,12 @@ }, }); -const ConnectedMultimedia: React.ComponentType = - React.memo(function ConnectedMultimedia(props: BaseProps) { - const inputState = React.useContext(InputStateContext); - return ; - }); +const ConnectedMultimedia: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedMultimedia(props: BaseProps) { + const inputState = React.useContext(InputStateContext); + return ; +}); export default ConnectedMultimedia; diff --git a/native/navigation/nav-from-redux-handler.react.js b/native/navigation/nav-from-redux-handler.react.js --- a/native/navigation/nav-from-redux-handler.react.js +++ b/native/navigation/nav-from-redux-handler.react.js @@ -7,7 +7,7 @@ import type { MonitorActionState } from '../redux/dev-tools.react.js'; import { useSelector } from '../redux/redux-utils.js'; -const NavFromReduxHandler: React.ComponentType<{}> = React.memo<{}>( +const NavFromReduxHandler: React.ComponentType<{}> = React.memo<{}, void>( function NavFromReduxHandler() { const navContext = React.useContext(NavContext); diff --git a/native/navigation/navigation-handler.react.js b/native/navigation/navigation-handler.react.js --- a/native/navigation/navigation-handler.react.js +++ b/native/navigation/navigation-handler.react.js @@ -16,7 +16,7 @@ import { MissingRegistrationDataHandler } from '../account/registration/missing-registration-data/missing-registration-data-handler.react.js'; import DevTools from '../redux/dev-tools.react.js'; -const NavigationHandler: React.ComponentType<{}> = React.memo<{}>( +const NavigationHandler: React.ComponentType<{}> = React.memo<{}, void>( function NavigationHandler() { const navContext = React.useContext(NavContext); const persistedStateLoaded = usePersistedStateLoaded(); @@ -54,7 +54,7 @@ type LogInHandlerProps = { +dispatch: (action: NavAction) => void, }; -const LogInHandler = React.memo(function LogInHandler( +const LogInHandler = React.memo(function LogInHandler( props: LogInHandlerProps, ) { const { dispatch } = props; diff --git a/native/navigation/orientation-handler.react.js b/native/navigation/orientation-handler.react.js --- a/native/navigation/orientation-handler.react.js +++ b/native/navigation/orientation-handler.react.js @@ -47,18 +47,19 @@ } } -const ConnectedOrientationHandler: React.ComponentType<{}> = React.memo<{}>( - function ConnectedOrientationHandler() { - const deviceOrientation = useSelector(state => state.deviceOrientation); - const dispatch = useDispatch(); +const ConnectedOrientationHandler: React.ComponentType<{}> = React.memo< + {}, + void, +>(function ConnectedOrientationHandler() { + const deviceOrientation = useSelector(state => state.deviceOrientation); + const dispatch = useDispatch(); - return ( - - ); - }, -); + return ( + + ); +}); export default ConnectedOrientationHandler; diff --git a/native/navigation/overlay-navigator.react.js b/native/navigation/overlay-navigator.react.js --- a/native/navigation/overlay-navigator.react.js +++ b/native/navigation/overlay-navigator.react.js @@ -113,7 +113,7 @@ OverlayNavigationHelpers<>, >, >; -const OverlayNavigator = React.memo( +const OverlayNavigator = React.memo( ({ initialRouteName, children, screenOptions, screenListeners }: Props) => { const { state, descriptors, navigation } = useNavigationBuilder< StackNavigationState, diff --git a/native/navigation/tab-navigator.react.js b/native/navigation/tab-navigator.react.js --- a/native/navigation/tab-navigator.react.js +++ b/native/navigation/tab-navigator.react.js @@ -89,7 +89,7 @@ CustomBottomTabNavigationHelpers<>, >; -const TabNavigator = React.memo(function TabNavigator({ +const TabNavigator = React.memo(function TabNavigator({ id, initialRouteName, backBehavior, diff --git a/native/navigation/thread-screen-tracker.react.js b/native/navigation/thread-screen-tracker.react.js --- a/native/navigation/thread-screen-tracker.react.js +++ b/native/navigation/thread-screen-tracker.react.js @@ -7,7 +7,7 @@ import { useActiveMessageList } from './nav-selectors.js'; -const ThreadScreenTracker: React.ComponentType<{}> = React.memo<{}>( +const ThreadScreenTracker: React.ComponentType<{}> = React.memo<{}, void>( function ThreadScreenTracker() { const activeThread = useActiveMessageList(); const reduxDispatch = useDispatch(); diff --git a/native/profile/appearance-preferences.react.js b/native/profile/appearance-preferences.react.js --- a/native/profile/appearance-preferences.react.js +++ b/native/profile/appearance-preferences.react.js @@ -140,7 +140,7 @@ +route: NavigationRoute<'AppearancePreferences'>, }; const ConnectedAppearancePreferences: React.ComponentType = - React.memo(function ConnectedAppearancePreferences( + React.memo(function ConnectedAppearancePreferences( props: BaseProps, ) { const globalThemeInfo = useSelector(state => state.globalThemeInfo); diff --git a/native/profile/custom-server-modal.react.js b/native/profile/custom-server-modal.react.js --- a/native/profile/custom-server-modal.react.js +++ b/native/profile/custom-server-modal.react.js @@ -118,23 +118,25 @@ }; } -const ConnectedCustomServerModal: React.ComponentType = - React.memo(function ConnectedCustomServerModal(props: BaseProps) { - const urlPrefix = useSelector(urlPrefixSelector(authoritativeKeyserverID)); - invariant(urlPrefix, "missing urlPrefix for ashoat's keyserver"); - const customServer = useSelector(state => state.customServer); - const styles = useStyles(unboundStyles); - const dispatch = useDispatch(); +const ConnectedCustomServerModal: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedCustomServerModal(props: BaseProps) { + const urlPrefix = useSelector(urlPrefixSelector(authoritativeKeyserverID)); + invariant(urlPrefix, "missing urlPrefix for ashoat's keyserver"); + const customServer = useSelector(state => state.customServer); + const styles = useStyles(unboundStyles); + const dispatch = useDispatch(); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedCustomServerModal; diff --git a/native/profile/default-notifications-preferences.react.js b/native/profile/default-notifications-preferences.react.js --- a/native/profile/default-notifications-preferences.react.js +++ b/native/profile/default-notifications-preferences.react.js @@ -181,7 +181,7 @@ registerFetchKey(setUserSettingsActionTypes); const ConnectedDefaultNotificationPreferences: React.ComponentType = - React.memo(function ConnectedDefaultNotificationPreferences( + React.memo(function ConnectedDefaultNotificationPreferences( props: BaseProps, ) { const styles = useStyles(unboundStyles); diff --git a/native/profile/delete-account.react.js b/native/profile/delete-account.react.js --- a/native/profile/delete-account.react.js +++ b/native/profile/delete-account.react.js @@ -36,7 +36,7 @@ +navigation: ProfileNavigationProp<'DeleteAccount'>, +route: NavigationRoute<'DeleteAccount'>, }; -const DeleteAccount: React.ComponentType = React.memo( +const DeleteAccount: React.ComponentType = React.memo( function DeleteAccount() { const deleteAccountLoadingStatus = useSelector( deleteAccountLoadingStatusSelector, diff --git a/native/profile/dev-tools.react.js b/native/profile/dev-tools.react.js --- a/native/profile/dev-tools.react.js +++ b/native/profile/dev-tools.react.js @@ -235,26 +235,27 @@ }; } -const ConnectedDevTools: React.ComponentType = React.memo( - function ConnectedDevTools(props: BaseProps) { - const urlPrefix = useSelector(urlPrefixSelector(authoritativeKeyserverID)); - invariant(urlPrefix, "missing urlPrefix for ashoat's keyserver"); - const customServer = useSelector(state => state.customServer); - const colors = useColors(); - const styles = useStyles(unboundStyles); - const dispatch = useDispatch(); +const ConnectedDevTools: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedDevTools(props: BaseProps) { + const urlPrefix = useSelector(urlPrefixSelector(authoritativeKeyserverID)); + invariant(urlPrefix, "missing urlPrefix for ashoat's keyserver"); + const customServer = useSelector(state => state.customServer); + const colors = useColors(); + const styles = useStyles(unboundStyles); + const dispatch = useDispatch(); - return ( - - ); - }, -); + return ( + + ); +}); export default ConnectedDevTools; diff --git a/native/profile/edit-password.react.js b/native/profile/edit-password.react.js --- a/native/profile/edit-password.react.js +++ b/native/profile/edit-password.react.js @@ -348,32 +348,34 @@ changeIdentityUserPasswordActionTypes, ); -const ConnectedEditPassword: React.ComponentType = - React.memo(function ConnectedEditPassword(props: BaseProps) { - const loadingStatus = useSelector(loadingStatusSelector); - const username = useSelector(state => { - if (state.currentUserInfo && !state.currentUserInfo.anonymous) { - return state.currentUserInfo.username; - } - return undefined; - }); - const colors = useColors(); - const styles = useStyles(unboundStyles); +const ConnectedEditPassword: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedEditPassword(props: BaseProps) { + const loadingStatus = useSelector(loadingStatusSelector); + const username = useSelector(state => { + if (state.currentUserInfo && !state.currentUserInfo.anonymous) { + return state.currentUserInfo.username; + } + return undefined; + }); + const colors = useColors(); + const styles = useStyles(unboundStyles); - const dispatchActionPromise = useDispatchActionPromise(); - const callChangeIdentityUserPassword = useChangeIdentityUserPassword(); + const dispatchActionPromise = useDispatchActionPromise(); + const callChangeIdentityUserPassword = useChangeIdentityUserPassword(); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedEditPassword; diff --git a/native/profile/profile-header.react.js b/native/profile/profile-header.react.js --- a/native/profile/profile-header.react.js +++ b/native/profile/profile-header.react.js @@ -10,11 +10,13 @@ const activeTabSelector = createActiveTabSelector(ProfileRouteName); -const ProfileHeader: React.ComponentType = - React.memo(function ProfileHeader(props: StackHeaderProps) { - const navContext = React.useContext(NavContext); - const activeTab = activeTabSelector(navContext); - return
; - }); +const ProfileHeader: React.ComponentType = React.memo< + StackHeaderProps, + void, +>(function ProfileHeader(props: StackHeaderProps) { + const navContext = React.useContext(NavContext); + const activeTab = activeTabSelector(navContext); + return
; +}); export default ProfileHeader; diff --git a/native/profile/profile-screen.react.js b/native/profile/profile-screen.react.js --- a/native/profile/profile-screen.react.js +++ b/native/profile/profile-screen.react.js @@ -552,91 +552,92 @@ const logOutLoadingStatusSelector = createLoadingStatusSelector(logOutActionTypes); -const ConnectedProfileScreen: React.ComponentType = - React.memo(function ConnectedProfileScreen(props: BaseProps) { - const currentUserInfo = useSelector(state => state.currentUserInfo); - const logOutLoading = - useSelector(logOutLoadingStatusSelector) === 'loading'; - const colors = useColors(); - const styles = useStyles(unboundStyles); - const callPrimaryDeviceLogOut = usePrimaryDeviceLogOut(); - const callSecondaryDeviceLogOut = useSecondaryDeviceLogOut(); - const dispatchActionPromise = useDispatchActionPromise(); - const staffCanSee = useStaffCanSee(); - const stringForUser = useStringForUser(currentUserInfo); - const isAccountWithPassword = useSelector(state => - accountHasPassword(state.currentUserInfo), - ); - const currentUserID = useCurrentUserFID(); - const [isPrimaryDevice, setIsPrimaryDevice] = React.useState(false); - const checkIfPrimaryDevice = useCheckIfPrimaryDevice(); - - const showVersionUnsupportedAlert = useShowVersionUnsupportedAlert(false); - const legacyLogOutOptions = React.useMemo( - () => ({ - logOutType: 'legacy', - handleUseNewFlowResponse: showVersionUnsupportedAlert, - }), - [showVersionUnsupportedAlert], - ); - const callLegayLogOut = useBaseLogOut(legacyLogOutOptions); - - const userID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); - const processAndSendDMOperation = useProcessAndSendDMOperation(); - - const onCreateDMThread = React.useCallback(async () => { - invariant(userID, 'userID should be set'); - const op: DMCreateThreadOperation = { - type: 'create_thread', - threadID: uuid.v4(), - creatorID: userID, - time: Date.now(), - threadType: thickThreadTypes.LOCAL, - memberIDs: [], - roleID: uuid.v4(), - newMessageID: uuid.v4(), - }; - const specification: OutboundDMOperationSpecification = { - type: dmOperationSpecificationTypes.OUTBOUND, - op, - recipients: { - type: 'self_devices', - }, - }; - await processAndSendDMOperation(specification); - }, [processAndSendDMOperation, userID]); +const ConnectedProfileScreen: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedProfileScreen(props: BaseProps) { + const currentUserInfo = useSelector(state => state.currentUserInfo); + const logOutLoading = useSelector(logOutLoadingStatusSelector) === 'loading'; + const colors = useColors(); + const styles = useStyles(unboundStyles); + const callPrimaryDeviceLogOut = usePrimaryDeviceLogOut(); + const callSecondaryDeviceLogOut = useSecondaryDeviceLogOut(); + const dispatchActionPromise = useDispatchActionPromise(); + const staffCanSee = useStaffCanSee(); + const stringForUser = useStringForUser(currentUserInfo); + const isAccountWithPassword = useSelector(state => + accountHasPassword(state.currentUserInfo), + ); + const currentUserID = useCurrentUserFID(); + const [isPrimaryDevice, setIsPrimaryDevice] = React.useState(false); + const checkIfPrimaryDevice = useCheckIfPrimaryDevice(); + + const showVersionUnsupportedAlert = useShowVersionUnsupportedAlert(false); + const legacyLogOutOptions = React.useMemo( + () => ({ + logOutType: 'legacy', + handleUseNewFlowResponse: showVersionUnsupportedAlert, + }), + [showVersionUnsupportedAlert], + ); + const callLegayLogOut = useBaseLogOut(legacyLogOutOptions); - React.useEffect(() => { - void (async () => { - const checkIfPrimaryDeviceResult = await checkIfPrimaryDevice(); - setIsPrimaryDevice(checkIfPrimaryDeviceResult); - })(); - }, [checkIfPrimaryDevice]); + const userID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const processAndSendDMOperation = useProcessAndSendDMOperation(); + + const onCreateDMThread = React.useCallback(async () => { + invariant(userID, 'userID should be set'); + const op: DMCreateThreadOperation = { + type: 'create_thread', + threadID: uuid.v4(), + creatorID: userID, + time: Date.now(), + threadType: thickThreadTypes.LOCAL, + memberIDs: [], + roleID: uuid.v4(), + newMessageID: uuid.v4(), + }; + const specification: OutboundDMOperationSpecification = { + type: dmOperationSpecificationTypes.OUTBOUND, + op, + recipients: { + type: 'self_devices', + }, + }; + await processAndSendDMOperation(specification); + }, [processAndSendDMOperation, userID]); + + React.useEffect(() => { + void (async () => { + const checkIfPrimaryDeviceResult = await checkIfPrimaryDevice(); + setIsPrimaryDevice(checkIfPrimaryDeviceResult); + })(); + }, [checkIfPrimaryDevice]); - const usingRestoreFlow = useIsRestoreFlowEnabled(); + const usingRestoreFlow = useIsRestoreFlowEnabled(); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedProfileScreen; diff --git a/native/profile/relationship-list-item.react.js b/native/profile/relationship-list-item.react.js --- a/native/profile/relationship-list-item.react.js +++ b/native/profile/relationship-list-item.react.js @@ -322,7 +322,7 @@ } const ConnectedRelationshipListItem: React.ComponentType = - React.memo(function ConnectedRelationshipListItem( + React.memo(function ConnectedRelationshipListItem( props: BaseProps, ) { const removeUserLoadingStatus = useSelector(state => diff --git a/native/profile/relationship-list.react.js b/native/profile/relationship-list.react.js --- a/native/profile/relationship-list.react.js +++ b/native/profile/relationship-list.react.js @@ -495,8 +495,10 @@ registerFetchKey(updateRelationshipsActionTypes); -const MemoizedRelationshipList: React.ComponentType = - React.memo(RelationshipList); +const MemoizedRelationshipList: React.ComponentType = React.memo< + Props, + void, +>(RelationshipList); MemoizedRelationshipList.displayName = 'RelationshipList'; export default MemoizedRelationshipList; diff --git a/native/push/push-handler.react.js b/native/push/push-handler.react.js --- a/native/push/push-handler.react.js +++ b/native/push/push-handler.react.js @@ -805,54 +805,56 @@ } } -const ConnectedPushHandler: React.ComponentType = - React.memo(function ConnectedPushHandler(props: BaseProps) { - const navContext = React.useContext(NavContext); - const activeThread = activeMessageListSelector(navContext); - const thinThreadsUnreadCount = useSelector(thinThreadsUnreadCountSelector); - const unreadThickThreadIDs = useSelector(unreadThickThreadIDsSelector); - const connection = useSelector(allConnectionInfosSelector); - const deviceTokens = useSelector(deviceTokensSelector); - const threadInfos = useSelector(threadInfoSelector); - const notifPermissionAlertInfo = useSelector( - state => state.alertStore.alertInfos[alertTypes.NOTIF_PERMISSION], - ); - const allUpdatesCurrentAsOf = useSelector(allUpdatesCurrentAsOfSelector); - const activeTheme = useSelector(state => state.globalThemeInfo.activeTheme); - const loggedIn = useSelector(isLoggedIn); - const localToken = useSelector( - state => state.tunnelbrokerDeviceToken.localToken, - ); - const navigateToThread = useNavigateToThread(); - const dispatch = useDispatch(); - const dispatchActionPromise = useDispatchActionPromise(); - const callSetDeviceToken = useSetDeviceToken(); - const callSetDeviceTokenFanout = useSetDeviceTokenFanout(); - const rootContext = React.useContext(RootContext); - const { socketState: tunnelbrokerSocketState } = useTunnelbroker(); - return ( - - ); - }); +const ConnectedPushHandler: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedPushHandler(props: BaseProps) { + const navContext = React.useContext(NavContext); + const activeThread = activeMessageListSelector(navContext); + const thinThreadsUnreadCount = useSelector(thinThreadsUnreadCountSelector); + const unreadThickThreadIDs = useSelector(unreadThickThreadIDsSelector); + const connection = useSelector(allConnectionInfosSelector); + const deviceTokens = useSelector(deviceTokensSelector); + const threadInfos = useSelector(threadInfoSelector); + const notifPermissionAlertInfo = useSelector( + state => state.alertStore.alertInfos[alertTypes.NOTIF_PERMISSION], + ); + const allUpdatesCurrentAsOf = useSelector(allUpdatesCurrentAsOfSelector); + const activeTheme = useSelector(state => state.globalThemeInfo.activeTheme); + const loggedIn = useSelector(isLoggedIn); + const localToken = useSelector( + state => state.tunnelbrokerDeviceToken.localToken, + ); + const navigateToThread = useNavigateToThread(); + const dispatch = useDispatch(); + const dispatchActionPromise = useDispatchActionPromise(); + const callSetDeviceToken = useSetDeviceToken(); + const callSetDeviceTokenFanout = useSetDeviceTokenFanout(); + const rootContext = React.useContext(RootContext); + const { socketState: tunnelbrokerSocketState } = useTunnelbroker(); + return ( + + ); +}); export default ConnectedPushHandler; diff --git a/native/redux/dev-tools.react.js b/native/redux/dev-tools.react.js --- a/native/redux/dev-tools.react.js +++ b/native/redux/dev-tools.react.js @@ -20,124 +20,126 @@ +navState: NavigationState, }>; -const DevTools: React.ComponentType<{}> = React.memo<{}>(function DevTools() { - const devToolsRef = React.useRef(); - if ( - global.__REDUX_DEVTOOLS_EXTENSION__ && - devToolsRef.current === undefined - ) { - devToolsRef.current = global.__REDUX_DEVTOOLS_EXTENSION__.connect({ - name: 'Comm', - features: { - pause: false, - lock: false, - persist: false, - export: false, - import: false, - jump: true, - skip: false, - reorder: false, - dispatch: false, - test: false, - }, - }); - } - const devTools = devToolsRef.current; - - const navContext = React.useContext(NavContext); - const initialReduxState = useSelector(state => state); - - React.useEffect(() => { - if (devTools && navContext) { - devTools.init({ ...initialReduxState, navState: navContext.state }); +const DevTools: React.ComponentType<{}> = React.memo<{}, void>( + function DevTools() { + const devToolsRef = React.useRef(); + if ( + global.__REDUX_DEVTOOLS_EXTENSION__ && + devToolsRef.current === undefined + ) { + devToolsRef.current = global.__REDUX_DEVTOOLS_EXTENSION__.connect({ + name: 'Comm', + features: { + pause: false, + lock: false, + persist: false, + export: false, + import: false, + jump: true, + skip: false, + reorder: false, + dispatch: false, + test: false, + }, + }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [devTools, !navContext]); + const devTools = devToolsRef.current; - const postActionToMonitor = React.useCallback( - (action: Object, state: Object) => { - if (!devTools) { - return; - } else if ( - (action.type === setNavStateActionType || - action.type === setReduxStateActionType) && - action.payload.hideFromMonitor - ) { - // Triggered by handleActionFromMonitor below when somebody is stepping - // through actions in the Comm monitor in Redux dev tools - return; - } else if (action.type === setNavStateActionType) { - // Triggered by NavFromReduxHandler when somebody imports state into the - // Redux monitor in Redux dev tools - devTools.init(state); - } else { - devTools.send(action, state); + const navContext = React.useContext(NavContext); + const initialReduxState = useSelector(state => state); + + React.useEffect(() => { + if (devTools && navContext) { + devTools.init({ ...initialReduxState, navState: navContext.state }); } - }, - [devTools], - ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [devTools, !navContext]); + + const postActionToMonitor = React.useCallback( + (action: Object, state: Object) => { + if (!devTools) { + return; + } else if ( + (action.type === setNavStateActionType || + action.type === setReduxStateActionType) && + action.payload.hideFromMonitor + ) { + // Triggered by handleActionFromMonitor below when somebody is + // stepping through actions in the Comm monitor in Redux dev tools + return; + } else if (action.type === setNavStateActionType) { + // Triggered by NavFromReduxHandler when somebody imports state into + // the Redux monitor in Redux dev tools + devTools.init(state); + } else { + devTools.send(action, state); + } + }, + [devTools], + ); - React.useEffect(() => { - actionLogger.subscribe(postActionToMonitor); - return () => actionLogger.unsubscribe(postActionToMonitor); - }, [postActionToMonitor]); + React.useEffect(() => { + actionLogger.subscribe(postActionToMonitor); + return () => actionLogger.unsubscribe(postActionToMonitor); + }, [postActionToMonitor]); - const reduxDispatch = useDispatch(); - const navDispatch = React.useMemo( - () => (navContext ? navContext.dispatch : null), - [navContext], - ); + const reduxDispatch = useDispatch(); + const navDispatch = React.useMemo( + () => (navContext ? navContext.dispatch : null), + [navContext], + ); - const setInternalState = React.useCallback( - (state: MonitorActionState) => { - const { navState, ...reduxState } = state; - if (navDispatch) { - navDispatch({ - type: setNavStateActionType, + const setInternalState = React.useCallback( + (state: MonitorActionState) => { + const { navState, ...reduxState } = state; + if (navDispatch) { + navDispatch({ + type: setNavStateActionType, + payload: { + state: navState, + hideFromMonitor: true, + }, + }); + } else { + console.log('could not set state in ReactNav'); + } + reduxDispatch({ + type: setReduxStateActionType, payload: { - state: navState, + state: reduxState, hideFromMonitor: true, }, }); - } else { - console.log('could not set state in ReactNav'); - } - reduxDispatch({ - type: setReduxStateActionType, - payload: { - state: reduxState, - hideFromMonitor: true, - }, - }); - }, - [reduxDispatch, navDispatch], - ); + }, + [reduxDispatch, navDispatch], + ); - const handleActionFromMonitor = React.useCallback( - (monitorAction: MonitorAction) => { + const handleActionFromMonitor = React.useCallback( + (monitorAction: MonitorAction) => { + if (!devTools) { + return; + } + if (monitorAction.type === 'DISPATCH') { + const state = JSON.parse(monitorAction.state); + setInternalState(state); + } else if (monitorAction.type === 'IMPORT') { + console.log('you should import using the Redux monitor!'); + } + }, + [devTools, setInternalState], + ); + + React.useEffect(() => { if (!devTools) { - return; - } - if (monitorAction.type === 'DISPATCH') { - const state = JSON.parse(monitorAction.state); - setInternalState(state); - } else if (monitorAction.type === 'IMPORT') { - console.log('you should import using the Redux monitor!'); + return undefined; } - }, - [devTools, setInternalState], - ); - - React.useEffect(() => { - if (!devTools) { - return undefined; - } - const unsubscribe = devTools.subscribe(handleActionFromMonitor); - return unsubscribe; - }, [devTools, handleActionFromMonitor]); + const unsubscribe = devTools.subscribe(handleActionFromMonitor); + return unsubscribe; + }, [devTools, handleActionFromMonitor]); - return null; -}); + return null; + }, +); DevTools.displayName = 'DevTools'; export default DevTools; diff --git a/native/redux/persist.js b/native/redux/persist.js --- a/native/redux/persist.js +++ b/native/redux/persist.js @@ -107,10 +107,7 @@ RawThreadInfo, ThinRawThreadInfo, } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import type { - ReportStore, - ClientReportCreationRequest, -} from 'lib/types/report-types.js'; +import type { ReportStore } from 'lib/types/report-types.js'; import { defaultConnectionInfo } from 'lib/types/socket-types.js'; import { syncedMetadataNames } from 'lib/types/synced-metadata-types.js'; import { defaultGlobalThemeInfo } from 'lib/types/theme-types.js'; diff --git a/native/socket.react.js b/native/socket.react.js --- a/native/socket.react.js +++ b/native/socket.react.js @@ -30,97 +30,99 @@ } from './selectors/socket-selectors.js'; import { decompressMessage } from './utils/decompress.js'; -const NativeSocket: React.ComponentType = - React.memo(function NativeSocket(props: BaseSocketProps) { - const navContext = React.useContext(NavContext); - - const { keyserverID } = props; - - const cookie = useSelector(cookieSelector(keyserverID)); - const connection = useSelector(connectionSelector(keyserverID)); - invariant(connection, 'keyserver missing from keyserverStore'); - const frozen = useSelector(state => state.frozen); - const active = useSelector( - state => isLoggedIn(state) && state.lifecycleState !== 'background', - ); - - const openSocket = useSelector(openSocketSelector(keyserverID)); - invariant(openSocket, 'openSocket failed to be created'); - const sessionIdentification = useSelector( - sessionIdentificationSelector(keyserverID), - ); - const preRequestUserState = useSelector( - preRequestUserStateForSingleKeyserverSelector(keyserverID), - ); - - const getInitialNotificationsEncryptedMessage = - useInitialNotificationsEncryptedMessage(keyserverID); - - const getClientResponses = useSelector(state => - nativeGetClientResponsesSelector({ - redux: state, - navContext, - getInitialNotificationsEncryptedMessage, - keyserverID, - }), - ); - const sessionStateFunc = useSelector(state => - nativeSessionStateFuncSelector(keyserverID)({ - redux: state, - navContext, - }), - ); - const currentCalendarQuery = useSelector(state => - nativeCalendarQuery({ - redux: state, - navContext, - }), - ); - - const activeThread = useForegroundActiveThread(); - - const lastCommunicatedPlatformDetails = useSelector( - lastCommunicatedPlatformDetailsSelector(keyserverID), - ); - - const dispatch = useDispatch(); - const dispatchActionPromise = useDispatchActionPromise(); - - const activeSessionRecovery = useSelector( - state => - state.keyserverStore.keyserverInfos[keyserverID]?.connection - .activeSessionRecovery, - ); - - const fetchPendingUpdates = useFetchPendingUpdates(); - - const isConnectedToInternet = useSelector( - state => state.connectivity.connected, - ); - - return ( - - ); - }); +const NativeSocket: React.ComponentType = React.memo< + BaseSocketProps, + void, +>(function NativeSocket(props: BaseSocketProps) { + const navContext = React.useContext(NavContext); + + const { keyserverID } = props; + + const cookie = useSelector(cookieSelector(keyserverID)); + const connection = useSelector(connectionSelector(keyserverID)); + invariant(connection, 'keyserver missing from keyserverStore'); + const frozen = useSelector(state => state.frozen); + const active = useSelector( + state => isLoggedIn(state) && state.lifecycleState !== 'background', + ); + + const openSocket = useSelector(openSocketSelector(keyserverID)); + invariant(openSocket, 'openSocket failed to be created'); + const sessionIdentification = useSelector( + sessionIdentificationSelector(keyserverID), + ); + const preRequestUserState = useSelector( + preRequestUserStateForSingleKeyserverSelector(keyserverID), + ); + + const getInitialNotificationsEncryptedMessage = + useInitialNotificationsEncryptedMessage(keyserverID); + + const getClientResponses = useSelector(state => + nativeGetClientResponsesSelector({ + redux: state, + navContext, + getInitialNotificationsEncryptedMessage, + keyserverID, + }), + ); + const sessionStateFunc = useSelector(state => + nativeSessionStateFuncSelector(keyserverID)({ + redux: state, + navContext, + }), + ); + const currentCalendarQuery = useSelector(state => + nativeCalendarQuery({ + redux: state, + navContext, + }), + ); + + const activeThread = useForegroundActiveThread(); + + const lastCommunicatedPlatformDetails = useSelector( + lastCommunicatedPlatformDetailsSelector(keyserverID), + ); + + const dispatch = useDispatch(); + const dispatchActionPromise = useDispatchActionPromise(); + + const activeSessionRecovery = useSelector( + state => + state.keyserverStore.keyserverInfos[keyserverID]?.connection + .activeSessionRecovery, + ); + + const fetchPendingUpdates = useFetchPendingUpdates(); + + const isConnectedToInternet = useSelector( + state => state.connectivity.connected, + ); + + return ( + + ); +}); export default NativeSocket; diff --git a/native/tooltip/nux-tips-overlay.react.js b/native/tooltip/nux-tips-overlay.react.js --- a/native/tooltip/nux-tips-overlay.react.js +++ b/native/tooltip/nux-tips-overlay.react.js @@ -468,7 +468,7 @@ return shouldRenderScreenContent ? : null; } - return React.memo>(NUXTipsOverlayWrapper); + return React.memo, void>(NUXTipsOverlayWrapper); } export { createNUXTipsOverlay, animationDuration }; diff --git a/native/tooltip/tooltip.react.js b/native/tooltip/tooltip.react.js --- a/native/tooltip/tooltip.react.js +++ b/native/tooltip/tooltip.react.js @@ -528,7 +528,7 @@ ); } - return React.memo(MemoizedTooltip); + return React.memo(MemoizedTooltip); } function getTooltipHeight(numEntries: number): number { diff --git a/web/app.react.js b/web/app.react.js --- a/web/app.react.js +++ b/web/app.react.js @@ -525,99 +525,97 @@ updateCalendarQueryActionTypes, ); -const ConnectedApp: React.ComponentType = React.memo( - function ConnectedApp(props) { - const activeChatThreadID = useSelector( - state => state.navInfo.activeChatThreadID, - ); - const navInfo = useSelector(state => state.navInfo); +const ConnectedApp: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedApp(props) { + const activeChatThreadID = useSelector( + state => state.navInfo.activeChatThreadID, + ); + const navInfo = useSelector(state => state.navInfo); - const fetchEntriesLoadingStatus = useSelector( - fetchEntriesLoadingStatusSelector, - ); - const updateCalendarQueryLoadingStatus = useSelector( - updateCalendarQueryLoadingStatusSelector, - ); - const entriesLoadingStatus = combineLoadingStatuses( - fetchEntriesLoadingStatus, - updateCalendarQueryLoadingStatus, - ); + const fetchEntriesLoadingStatus = useSelector( + fetchEntriesLoadingStatusSelector, + ); + const updateCalendarQueryLoadingStatus = useSelector( + updateCalendarQueryLoadingStatusSelector, + ); + const entriesLoadingStatus = combineLoadingStatuses( + fetchEntriesLoadingStatus, + updateCalendarQueryLoadingStatus, + ); - const baseLoggedIn = useSelector(isLoggedIn); - const restorationHasFinished = useIsUserDataReady(); - const loggedIn = baseLoggedIn && restorationHasFinished; + const baseLoggedIn = useSelector(isLoggedIn); + const restorationHasFinished = useIsUserDataReady(); + const loggedIn = baseLoggedIn && restorationHasFinished; - const activeThreadCurrentlyUnread = useSelector( - state => - !activeChatThreadID || - !!state.threadStore.threadInfos[activeChatThreadID]?.currentUser.unread, - ); + const activeThreadCurrentlyUnread = useSelector( + state => + !activeChatThreadID || + !!state.threadStore.threadInfos[activeChatThreadID]?.currentUser.unread, + ); - const dispatch = useDispatch(); - const modalContext = useModalContext(); - const modals = React.useMemo( - () => - modalContext.modals.map(([modal, key]) => ( - {modal} - )), - [modalContext.modals], - ); + const dispatch = useDispatch(); + const modalContext = useModalContext(); + const modals = React.useMemo( + () => + modalContext.modals.map(([modal, key]) => ( + {modal} + )), + [modalContext.modals], + ); - const { lockStatus, releaseLockOrAbortRequest } = useWebLock( - TUNNELBROKER_LOCK_NAME, - ); + const { lockStatus, releaseLockOrAbortRequest } = useWebLock( + TUNNELBROKER_LOCK_NAME, + ); - const secondaryTunnelbrokerConnection: SecondaryTunnelbrokerConnection = - useOtherTabsTunnelbrokerConnection(); + const secondaryTunnelbrokerConnection: SecondaryTunnelbrokerConnection = + useOtherTabsTunnelbrokerConnection(); - const handleSecondaryDeviceLogInError = - useHandleSecondaryDeviceLogInError(); + const handleSecondaryDeviceLogInError = useHandleSecondaryDeviceLogInError(); - return ( - - - - - - - - - - - - - - - - - - - ); - }, -); + return ( + + + + + + + + + + + + + + + + + + + ); +}); function AppWithProvider(props: BaseProps): React.Node { return ( diff --git a/web/calendar/calendar.react.js b/web/calendar/calendar.react.js --- a/web/calendar/calendar.react.js +++ b/web/calendar/calendar.react.js @@ -262,31 +262,32 @@ }; } -const ConnectedCalendar: React.ComponentType = React.memo( - function ConnectedCalendar(props) { - const year = useSelector(yearAssertingSelector); - const month = useSelector(monthAssertingSelector); - const daysToEntries = useSelector(currentDaysToEntries); - const navInfo = useSelector(state => state.navInfo); - const currentCalendarQuery = useSelector(webCalendarQuery); - const loggedIn = useSelector(isLoggedIn); - const callUpdateCalendarQuery = useUpdateCalendarQuery(); - const dispatchActionPromise = useDispatchActionPromise(); +const ConnectedCalendar: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedCalendar(props) { + const year = useSelector(yearAssertingSelector); + const month = useSelector(monthAssertingSelector); + const daysToEntries = useSelector(currentDaysToEntries); + const navInfo = useSelector(state => state.navInfo); + const currentCalendarQuery = useSelector(webCalendarQuery); + const loggedIn = useSelector(isLoggedIn); + const callUpdateCalendarQuery = useUpdateCalendarQuery(); + const dispatchActionPromise = useDispatchActionPromise(); - return ( - - ); - }, -); + return ( + + ); +}); export default ConnectedCalendar; diff --git a/web/calendar/day.react.js b/web/calendar/day.react.js --- a/web/calendar/day.react.js +++ b/web/calendar/day.react.js @@ -222,29 +222,30 @@ }; } -const ConnectedDay: React.ComponentType = React.memo( - function ConnectedDay(props) { - const onScreenThreadInfos = useSelector(onScreenThreadInfosSelector); - const viewerID = useSelector(state => state.currentUserInfo?.id); - const loggedIn = useSelector( - state => - !!(state.currentUserInfo && !state.currentUserInfo.anonymous && true), - ); - const dispatch = useDispatch(); - const { pushModal, popModal } = useModalContext(); - - return ( - - ); - }, -); +const ConnectedDay: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedDay(props) { + const onScreenThreadInfos = useSelector(onScreenThreadInfosSelector); + const viewerID = useSelector(state => state.currentUserInfo?.id); + const loggedIn = useSelector( + state => + !!(state.currentUserInfo && !state.currentUserInfo.anonymous && true), + ); + const dispatch = useDispatch(); + const { pushModal, popModal } = useModalContext(); + + return ( + + ); +}); export default ConnectedDay; diff --git a/web/calendar/entry.react.js b/web/calendar/entry.react.js --- a/web/calendar/entry.react.js +++ b/web/calendar/entry.react.js @@ -484,62 +484,63 @@ export type InnerEntry = Entry; -const ConnectedEntry: React.ComponentType = React.memo( - function ConnectedEntry(props) { - const { threadID } = props.entryInfo; - const unresolvedThreadInfo = useSelector( - state => threadInfoSelector(state)[threadID], - ); - const threadInfo = useResolvedThreadInfo(unresolvedThreadInfo); - const loggedIn = useSelector( - state => - !!(state.currentUserInfo && !state.currentUserInfo.anonymous && true), - ); - const currentUserCanEditEntry = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_ENTRIES, - ); - const calendarQuery = useSelector(nonThreadCalendarQuery); - const { socketState } = useTunnelbroker(); - - const keyserverID = extractKeyserverIDFromIDOptional(threadID); - const isKeyserverConnected = useSelector(state => { - if (!keyserverID) { - return true; - } - return connectionSelector(keyserverID)(state)?.status === 'connected'; - }); - - const online = threadSpecs[threadInfo.type] - .protocol() - .calendarIsOnline(socketState, isKeyserverConnected); - - const callCreateEntry = useCreateEntry(); - const callSaveEntry = useSaveEntry(); - const callDeleteEntry = useDeleteEntry(); - const dispatchActionPromise = useDispatchActionPromise(); - const dispatch = useDispatch(); - - const modalContext = useModalContext(); - - return ( - - ); - }, -); +const ConnectedEntry: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedEntry(props) { + const { threadID } = props.entryInfo; + const unresolvedThreadInfo = useSelector( + state => threadInfoSelector(state)[threadID], + ); + const threadInfo = useResolvedThreadInfo(unresolvedThreadInfo); + const loggedIn = useSelector( + state => + !!(state.currentUserInfo && !state.currentUserInfo.anonymous && true), + ); + const currentUserCanEditEntry = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_ENTRIES, + ); + const calendarQuery = useSelector(nonThreadCalendarQuery); + const { socketState } = useTunnelbroker(); + + const keyserverID = extractKeyserverIDFromIDOptional(threadID); + const isKeyserverConnected = useSelector(state => { + if (!keyserverID) { + return true; + } + return connectionSelector(keyserverID)(state)?.status === 'connected'; + }); + + const online = threadSpecs[threadInfo.type] + .protocol() + .calendarIsOnline(socketState, isKeyserverConnected); + + const callCreateEntry = useCreateEntry(); + const callSaveEntry = useSaveEntry(); + const callDeleteEntry = useDeleteEntry(); + const dispatchActionPromise = useDispatchActionPromise(); + const dispatch = useDispatch(); + + const modalContext = useModalContext(); + + return ( + + ); +}); export default ConnectedEntry; diff --git a/web/calendar/filter-panel.react.js b/web/calendar/filter-panel.react.js --- a/web/calendar/filter-panel.react.js +++ b/web/calendar/filter-panel.react.js @@ -395,7 +395,7 @@ }; const ConnectedFilterPanel: React.ComponentType = - React.memo(function ConnectedFilterPanel( + React.memo(function ConnectedFilterPanel( props: ConnectedFilterPanelProps, ): React.Node { const filteredThreadIDs = useSelector(filteredThreadIDsSelector); diff --git a/web/chat/chat-input-bar.react.js b/web/chat/chat-input-bar.react.js --- a/web/chat/chat-input-bar.react.js +++ b/web/chat/chat-input-bar.react.js @@ -556,150 +556,150 @@ const createThreadLoadingStatusSelector = createLoadingStatusSelector(newThreadActionTypes); -const ConnectedChatInputBar: React.ComponentType = - React.memo(function ConnectedChatInputBar(props) { - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); - const isThreadActive = useSelector( - state => props.threadInfo.id === state.navInfo.activeChatThreadID, - ); - const userInfos = useSelector(state => state.userStore.userInfos); - const joinThreadLoadingStatus = useSelector( - joinThreadLoadingStatusSelector, - ); - const createThreadLoadingStatus = useSelector( - createThreadLoadingStatusSelector, - ); - const threadCreationInProgress = createThreadLoadingStatus === 'loading'; - const calendarQuery = useSelector(nonThreadCalendarQuery); - const dispatchActionPromise = useDispatchActionPromise(); - const rawThreadInfo = useSelector( - state => state.threadStore.threadInfos[props.threadInfo.id], - ); - const callJoinThread = useJoinThread(); - const { getChatMentionSearchIndex } = useChatMentionContext(); - const chatMentionSearchIndex = getChatMentionSearchIndex(props.threadInfo); - - const { parentThreadID, community } = props.threadInfo; - const parentThreadInfo = useSelector(state => - parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, - ); - - const communityThreadInfo = useSelector(state => - community ? threadInfoSelector(state)[community] : null, - ); - - const threadFrozen = useThreadFrozenDueToViewerBlock( - props.threadInfo, - communityThreadInfo, - viewerID, - userInfos, - ); - - const currentUserIsVoiced = useThreadHasPermission( - props.threadInfo, - threadPermissions.VOICED, - ); - - const currentUserCanJoinThread = useThreadHasPermission( - props.threadInfo, - threadPermissions.JOIN_THREAD, - ); - - const userMentionsCandidates = useUserMentionsCandidates( - props.threadInfo, - parentThreadInfo, - ); - - const chatMentionCandidates = useThreadChatMentionCandidates( - props.threadInfo, - ); - - const typeaheadRegexMatches = React.useMemo( - () => - getTypeaheadRegexMatches( - props.inputState.draft, - { - start: props.inputState.textCursorPosition, - end: props.inputState.textCursorPosition, - }, - webMentionTypeaheadRegex, - ), - [props.inputState.textCursorPosition, props.inputState.draft], - ); - - const typeaheadMatchedStrings: ?TypeaheadMatchedStrings = - React.useMemo(() => { - if (typeaheadRegexMatches === null) { - return null; - } - return { - textBeforeAtSymbol: typeaheadRegexMatches.groups?.textPrefix ?? '', - query: typeaheadRegexMatches.groups?.mentionText ?? '', - }; - }, [typeaheadRegexMatches]); - - React.useEffect(() => { - if (props.inputState.typeaheadState.keepUpdatingThreadMembers) { - const setter = props.inputState.setTypeaheadState; - setter({ - frozenUserMentionsCandidates: userMentionsCandidates, - frozenChatMentionsCandidates: chatMentionCandidates, - }); +const ConnectedChatInputBar: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedChatInputBar(props) { + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const isThreadActive = useSelector( + state => props.threadInfo.id === state.navInfo.activeChatThreadID, + ); + const userInfos = useSelector(state => state.userStore.userInfos); + const joinThreadLoadingStatus = useSelector(joinThreadLoadingStatusSelector); + const createThreadLoadingStatus = useSelector( + createThreadLoadingStatusSelector, + ); + const threadCreationInProgress = createThreadLoadingStatus === 'loading'; + const calendarQuery = useSelector(nonThreadCalendarQuery); + const dispatchActionPromise = useDispatchActionPromise(); + const rawThreadInfo = useSelector( + state => state.threadStore.threadInfos[props.threadInfo.id], + ); + const callJoinThread = useJoinThread(); + const { getChatMentionSearchIndex } = useChatMentionContext(); + const chatMentionSearchIndex = getChatMentionSearchIndex(props.threadInfo); + + const { parentThreadID, community } = props.threadInfo; + const parentThreadInfo = useSelector(state => + parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, + ); + + const communityThreadInfo = useSelector(state => + community ? threadInfoSelector(state)[community] : null, + ); + + const threadFrozen = useThreadFrozenDueToViewerBlock( + props.threadInfo, + communityThreadInfo, + viewerID, + userInfos, + ); + + const currentUserIsVoiced = useThreadHasPermission( + props.threadInfo, + threadPermissions.VOICED, + ); + + const currentUserCanJoinThread = useThreadHasPermission( + props.threadInfo, + threadPermissions.JOIN_THREAD, + ); + + const userMentionsCandidates = useUserMentionsCandidates( + props.threadInfo, + parentThreadInfo, + ); + + const chatMentionCandidates = useThreadChatMentionCandidates( + props.threadInfo, + ); + + const typeaheadRegexMatches = React.useMemo( + () => + getTypeaheadRegexMatches( + props.inputState.draft, + { + start: props.inputState.textCursorPosition, + end: props.inputState.textCursorPosition, + }, + webMentionTypeaheadRegex, + ), + [props.inputState.textCursorPosition, props.inputState.draft], + ); + + const typeaheadMatchedStrings: ?TypeaheadMatchedStrings = + React.useMemo(() => { + if (typeaheadRegexMatches === null) { + return null; } - }, [ - userMentionsCandidates, - props.inputState.setTypeaheadState, - props.inputState.typeaheadState.keepUpdatingThreadMembers, - chatMentionCandidates, - ]); - - const suggestedUsers = useMentionTypeaheadUserSuggestions( - props.inputState.typeaheadState.frozenUserMentionsCandidates, - typeaheadMatchedStrings, - ); - - const suggestedChats = useMentionTypeaheadChatSuggestions( - chatMentionSearchIndex, - props.inputState.typeaheadState.frozenChatMentionsCandidates, - typeaheadMatchedStrings, - ); - - const suggestions: $ReadOnlyArray = - React.useMemo( - () => [...suggestedUsers, ...suggestedChats], - [suggestedUsers, suggestedChats], - ); - - const baseCancelPendingUpload = props.inputState.cancelPendingUpload; - const cancelPendingUpload = React.useCallback( - (uploadID: string) => { - baseCancelPendingUpload(props.threadInfo, uploadID); - }, - [baseCancelPendingUpload, props.threadInfo], + return { + textBeforeAtSymbol: typeaheadRegexMatches.groups?.textPrefix ?? '', + query: typeaheadRegexMatches.groups?.mentionText ?? '', + }; + }, [typeaheadRegexMatches]); + + React.useEffect(() => { + if (props.inputState.typeaheadState.keepUpdatingThreadMembers) { + const setter = props.inputState.setTypeaheadState; + setter({ + frozenUserMentionsCandidates: userMentionsCandidates, + frozenChatMentionsCandidates: chatMentionCandidates, + }); + } + }, [ + userMentionsCandidates, + props.inputState.setTypeaheadState, + props.inputState.typeaheadState.keepUpdatingThreadMembers, + chatMentionCandidates, + ]); + + const suggestedUsers = useMentionTypeaheadUserSuggestions( + props.inputState.typeaheadState.frozenUserMentionsCandidates, + typeaheadMatchedStrings, + ); + + const suggestedChats = useMentionTypeaheadChatSuggestions( + chatMentionSearchIndex, + props.inputState.typeaheadState.frozenChatMentionsCandidates, + typeaheadMatchedStrings, + ); + + const suggestions: $ReadOnlyArray = + React.useMemo( + () => [...suggestedUsers, ...suggestedChats], + [suggestedUsers, suggestedChats], ); - return ( - - ); - }); + const baseCancelPendingUpload = props.inputState.cancelPendingUpload; + const cancelPendingUpload = React.useCallback( + (uploadID: string) => { + baseCancelPendingUpload(props.threadInfo, uploadID); + }, + [baseCancelPendingUpload, props.threadInfo], + ); + + return ( + + ); +}); export default ConnectedChatInputBar; diff --git a/web/chat/chat-input-text-area.react.js b/web/chat/chat-input-text-area.react.js --- a/web/chat/chat-input-text-area.react.js +++ b/web/chat/chat-input-text-area.react.js @@ -16,7 +16,7 @@ +maxHeight?: number, }; -const ChatInputTextArea: React.ComponentType = React.memo( +const ChatInputTextArea: React.ComponentType = React.memo( function ChatInputTextArea(props: Props) { const { currentText, diff --git a/web/chat/chat-message-list.react.js b/web/chat/chat-message-list.react.js --- a/web/chat/chat-message-list.react.js +++ b/web/chat/chat-message-list.react.js @@ -384,78 +384,78 @@ } } -const ConnectedChatMessageList: React.ComponentType = - React.memo(function ConnectedChatMessageList( - props: BaseProps, - ): React.Node { - const { threadInfo } = props; - const messageListData = useMessageListData({ - threadInfo, - searching: false, - userInfoInputArray: [], - }); - - const startReached = !!useSelector(state => { - const activeID = threadInfo.id; - if (!activeID) { - return null; - } - - if (threadIsPending(activeID)) { - return true; - } - - const threadMessageInfo = state.messageStore.threads[activeID]; - if (!threadMessageInfo) { - return null; - } - return threadMessageInfo.startReached; - }); +const ConnectedChatMessageList: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedChatMessageList(props: BaseProps): React.Node { + const { threadInfo } = props; + const messageListData = useMessageListData({ + threadInfo, + searching: false, + userInfoInputArray: [], + }); - const fetchMessages = useFetchMessages(threadInfo); + const startReached = !!useSelector(state => { + const activeID = threadInfo.id; + if (!activeID) { + return null; + } - const inputState = React.useContext(InputStateContext); + if (threadIsPending(activeID)) { + return true; + } - const { clearTooltip } = useTooltipContext(); + const threadMessageInfo = state.messageStore.threads[activeID]; + if (!threadMessageInfo) { + return null; + } + return threadMessageInfo.startReached; + }); - const chatMentionCandidates = useThreadChatMentionCandidates(threadInfo); - const getTextMessageMarkdownRules = useTextMessageRulesFunc( - threadInfo, - chatMentionCandidates, - ); - const messageListContext = React.useMemo(() => { - if (!getTextMessageMarkdownRules) { - return undefined; - } - return { getTextMessageMarkdownRules }; - }, [getTextMessageMarkdownRules]); + const fetchMessages = useFetchMessages(threadInfo); - const { - editState, - addScrollToMessageListener, - removeScrollToMessageListener, - } = useEditModalContext(); - const isEditState = editState !== null; + const inputState = React.useContext(InputStateContext); - const viewerID = useSelector(state => state.currentUserInfo?.id); + const { clearTooltip } = useTooltipContext(); - return ( - - - - ); - }); + const chatMentionCandidates = useThreadChatMentionCandidates(threadInfo); + const getTextMessageMarkdownRules = useTextMessageRulesFunc( + threadInfo, + chatMentionCandidates, + ); + const messageListContext = React.useMemo(() => { + if (!getTextMessageMarkdownRules) { + return undefined; + } + return { getTextMessageMarkdownRules }; + }, [getTextMessageMarkdownRules]); + + const { + editState, + addScrollToMessageListener, + removeScrollToMessageListener, + } = useEditModalContext(); + const isEditState = editState !== null; + + const viewerID = useSelector(state => state.currentUserInfo?.id); + + return ( + + + + ); +}); export default ConnectedChatMessageList; diff --git a/web/chat/chat.react.js b/web/chat/chat.react.js --- a/web/chat/chat.react.js +++ b/web/chat/chat.react.js @@ -52,6 +52,6 @@ ); } -const MemoizedChat: React.ComponentType<{}> = React.memo<{}>(Chat); +const MemoizedChat: React.ComponentType<{}> = React.memo<{}, void>(Chat); export default MemoizedChat; diff --git a/web/chat/composed-message.react.js b/web/chat/composed-message.react.js --- a/web/chat/composed-message.react.js +++ b/web/chat/composed-message.react.js @@ -57,7 +57,7 @@ +fixedWidth?: boolean, +borderRadius?: number, }; -const ComposedMessage: React.ComponentType = React.memo( +const ComposedMessage: React.ComponentType = React.memo( function ComposedMessage(props) { const { item, threadInfo } = props; const { messageInfo } = item; diff --git a/web/chat/edit-text-message.react.js b/web/chat/edit-text-message.react.js --- a/web/chat/edit-text-message.react.js +++ b/web/chat/edit-text-message.react.js @@ -183,19 +183,20 @@ ); } -const ComposedEditTextMessage: React.ComponentType = React.memo( - function ComposedEditTextMessage(props) { - const { background, ...restProps } = props; - return ( - - - - ); - }, -); +const ComposedEditTextMessage: React.ComponentType = React.memo< + Props, + void, +>(function ComposedEditTextMessage(props) { + const { background, ...restProps } = props; + return ( + + + + ); +}); export { EditTextMessage, ComposedEditTextMessage }; diff --git a/web/chat/failed-send.react.js b/web/chat/failed-send.react.js --- a/web/chat/failed-send.react.js +++ b/web/chat/failed-send.react.js @@ -129,35 +129,35 @@ }; } -const ConnectedFailedSend: React.ComponentType = - React.memo(function ConnectedFailedSend(props) { - const { messageInfo } = props.item; - assertComposableMessageType(messageInfo.type); - const id = messageID(messageInfo); - const rawMessageInfo = useSelector( - state => state.messageStore.messages[id], - ); - assertComposableMessageType(rawMessageInfo.type); - invariant( - rawMessageInfo.type === messageTypes.TEXT || - rawMessageInfo.type === messageTypes.IMAGES || - rawMessageInfo.type === messageTypes.MULTIMEDIA, - 'FailedSend should only be used for composable message types', - ); - const inputState = React.useContext(InputStateContext); - const { parentThreadID } = props.threadInfo; - const parentThreadInfo = useSelector(state => - parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, - ); +const ConnectedFailedSend: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedFailedSend(props) { + const { messageInfo } = props.item; + assertComposableMessageType(messageInfo.type); + const id = messageID(messageInfo); + const rawMessageInfo = useSelector(state => state.messageStore.messages[id]); + assertComposableMessageType(rawMessageInfo.type); + invariant( + rawMessageInfo.type === messageTypes.TEXT || + rawMessageInfo.type === messageTypes.IMAGES || + rawMessageInfo.type === messageTypes.MULTIMEDIA, + 'FailedSend should only be used for composable message types', + ); + const inputState = React.useContext(InputStateContext); + const { parentThreadID } = props.threadInfo; + const parentThreadInfo = useSelector(state => + parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, + ); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedFailedSend; diff --git a/web/chat/multimedia-message.react.js b/web/chat/multimedia-message.react.js --- a/web/chat/multimedia-message.react.js +++ b/web/chat/multimedia-message.react.js @@ -102,10 +102,12 @@ } } -const ConnectedMultimediaMessage: React.ComponentType = - React.memo(function ConnectedMultimediaMessage(props) { - const inputState = React.useContext(InputStateContext); - return ; - }); +const ConnectedMultimediaMessage: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedMultimediaMessage(props) { + const inputState = React.useContext(InputStateContext); + return ; +}); export default ConnectedMultimediaMessage; diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js --- a/web/chat/robotext-message.react.js +++ b/web/chat/robotext-message.react.js @@ -129,7 +129,7 @@ }); }; } -const ThreadEntity = React.memo( +const ThreadEntity = React.memo( function ConnectedInnerThreadEntity(props: BaseInnerThreadEntityProps) { const { id } = props; const threadInfo = useSelector(state => threadInfoSelector(state)[id]); @@ -163,7 +163,9 @@ return {props.color}; } -const MemoizedRobotextMessage: React.ComponentType = - React.memo(RobotextMessage); +const MemoizedRobotextMessage: React.ComponentType = React.memo< + Props, + void, +>(RobotextMessage); export default MemoizedRobotextMessage; diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js --- a/web/input/input-state-container.react.js +++ b/web/input/input-state-container.react.js @@ -1648,75 +1648,74 @@ } } -const ConnectedInputStateContainer: React.ComponentType = - React.memo(function ConnectedInputStateContainer(props) { - const activeChatThreadID = useSelector( - state => state.navInfo.activeChatThreadID, - ); - const drafts = useSelector(state => state.draftStore.drafts); - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); - const messageStoreMessages = useSelector( - state => state.messageStore.messages, - ); - const pendingToRealizedThreadIDs = useSelector(state => - pendingToRealizedThreadIDsSelector(state.threadStore.threadInfos), - ); - const calendarQuery = useSelector(nonThreadCalendarQuery); - const callBlobServiceUpload = useBlobServiceUpload(); - const callDeleteUpload = useDeleteUpload(); - const callSendMultimediaMessage = - useInputStateContainerSendMultimediaMessage(); - const callSendTextMessage = useInputStateContainerSendTextMessage(); - const callNewThinThread = useNewThinThread(); - const callNewThickThread = useNewThickThread(); - const dispatch = useDispatch(); - const dispatchActionPromise = useDispatchActionPromise(); - const modalContext = useModalContext(); - const identityContext = React.useContext(IdentityClientContext); - const callInvalidTokenLogOut = useInvalidCSATLogOut(); - - const [sendCallbacks, setSendCallbacks] = React.useState< - $ReadOnlyArray<() => mixed>, - >([]); - const registerSendCallback = React.useCallback((callback: () => mixed) => { - setSendCallbacks(prevCallbacks => [...prevCallbacks, callback]); - }, []); - const unregisterSendCallback = React.useCallback( - (callback: () => mixed) => { - setSendCallbacks(prevCallbacks => - prevCallbacks.filter(candidate => candidate !== callback), - ); - }, - [], - ); - - return ( - +const ConnectedInputStateContainer: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedInputStateContainer(props) { + const activeChatThreadID = useSelector( + state => state.navInfo.activeChatThreadID, + ); + const drafts = useSelector(state => state.draftStore.drafts); + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const messageStoreMessages = useSelector( + state => state.messageStore.messages, + ); + const pendingToRealizedThreadIDs = useSelector(state => + pendingToRealizedThreadIDsSelector(state.threadStore.threadInfos), + ); + const calendarQuery = useSelector(nonThreadCalendarQuery); + const callBlobServiceUpload = useBlobServiceUpload(); + const callDeleteUpload = useDeleteUpload(); + const callSendMultimediaMessage = + useInputStateContainerSendMultimediaMessage(); + const callSendTextMessage = useInputStateContainerSendTextMessage(); + const callNewThinThread = useNewThinThread(); + const callNewThickThread = useNewThickThread(); + const dispatch = useDispatch(); + const dispatchActionPromise = useDispatchActionPromise(); + const modalContext = useModalContext(); + const identityContext = React.useContext(IdentityClientContext); + const callInvalidTokenLogOut = useInvalidCSATLogOut(); + + const [sendCallbacks, setSendCallbacks] = React.useState< + $ReadOnlyArray<() => mixed>, + >([]); + const registerSendCallback = React.useCallback((callback: () => mixed) => { + setSendCallbacks(prevCallbacks => [...prevCallbacks, callback]); + }, []); + const unregisterSendCallback = React.useCallback((callback: () => mixed) => { + setSendCallbacks(prevCallbacks => + prevCallbacks.filter(candidate => candidate !== callback), ); - }); + }, []); + + return ( + + ); +}); export default ConnectedInputStateContainer; diff --git a/web/markdown/markdown-spoiler.react.js b/web/markdown/markdown-spoiler.react.js --- a/web/markdown/markdown-spoiler.react.js +++ b/web/markdown/markdown-spoiler.react.js @@ -34,6 +34,6 @@ } const MemoizedMarkdownSpoiler: React.ComponentType = - React.memo(MarkdownSpoiler); + React.memo(MarkdownSpoiler); export default MemoizedMarkdownSpoiler; diff --git a/web/media/loadable-video.react.js b/web/media/loadable-video.react.js --- a/web/media/loadable-video.react.js +++ b/web/media/loadable-video.react.js @@ -107,7 +107,7 @@ const MemoizedLoadableVideo: MemoizedLoadableVideoComponentType = React.memo< Props, - HTMLVideoElement, + React.RefSetter, >(React.forwardRef(LoadableVideo)); export default MemoizedLoadableVideo; diff --git a/web/media/multimedia.react.js b/web/media/multimedia.react.js --- a/web/media/multimedia.react.js +++ b/web/media/multimedia.react.js @@ -226,7 +226,8 @@ ); } -const MemoizedMultimedia: React.ComponentType = - React.memo(Multimedia); +const MemoizedMultimedia: React.ComponentType = React.memo( + Multimedia, +); export default MemoizedMultimedia; diff --git a/web/modals/history/history-entry.react.js b/web/modals/history/history-entry.react.js --- a/web/modals/history/history-entry.react.js +++ b/web/modals/history/history-entry.react.js @@ -146,43 +146,45 @@ } } -const ConnectedHistoryEntry: React.ComponentType = - React.memo(function ConnectedHistoryEntry(props) { - const entryID = props.entryInfo.id; - invariant(entryID, 'entryInfo.id (serverID) should be set'); - const unresolvedThreadInfo = useSelector( - state => threadInfoSelector(state)[props.entryInfo.threadID], - ); - const threadInfo = useResolvedThreadInfo(unresolvedThreadInfo); - const loggedIn = useSelector( - state => - !!(state.currentUserInfo && !state.currentUserInfo.anonymous && true), - ); - const restoreLoadingStatus = useSelector( - createLoadingStatusSelector( - restoreEntryActionTypes, - `${restoreEntryActionTypes.started}:${entryID}`, - ), - ); - const calenderQuery = useSelector(nonThreadCalendarQuery); - const callRestoreEntry = useRestoreEntry(); - const dispatchActionPromise = useDispatchActionPromise(); +const ConnectedHistoryEntry: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedHistoryEntry(props) { + const entryID = props.entryInfo.id; + invariant(entryID, 'entryInfo.id (serverID) should be set'); + const unresolvedThreadInfo = useSelector( + state => threadInfoSelector(state)[props.entryInfo.threadID], + ); + const threadInfo = useResolvedThreadInfo(unresolvedThreadInfo); + const loggedIn = useSelector( + state => + !!(state.currentUserInfo && !state.currentUserInfo.anonymous && true), + ); + const restoreLoadingStatus = useSelector( + createLoadingStatusSelector( + restoreEntryActionTypes, + `${restoreEntryActionTypes.started}:${entryID}`, + ), + ); + const calenderQuery = useSelector(nonThreadCalendarQuery); + const callRestoreEntry = useRestoreEntry(); + const dispatchActionPromise = useDispatchActionPromise(); - const { creator } = props.entryInfo; - const [creatorWithENSName] = useENSNames([creator]); + const { creator } = props.entryInfo; + const [creatorWithENSName] = useENSNames([creator]); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedHistoryEntry; diff --git a/web/modals/history/history-modal.react.js b/web/modals/history/history-modal.react.js --- a/web/modals/history/history-modal.react.js +++ b/web/modals/history/history-modal.react.js @@ -264,36 +264,36 @@ fetchRevisionsForEntryActionTypes, ); -const ConnectedHistoryModal: React.ComponentType = - React.memo(function ConnectedHistoryModal(props) { - const entryInfos = useSelector( - state => allDaysToEntries(state)[props.dayString], - ); - const threadInfos = useSelector(state => state.threadStore.threadInfos); - const dayLoadingStatus = useSelector(dayLoadingStatusSelector); - const entryLoadingStatus = useSelector(entryLoadingStatusSelector); - const calendarFilters = useSelector( - nonExcludeDeletedCalendarFiltersSelector, - ); - const callFetchEntries = useFetchEntries(); - const callFetchRevisionsForEntry = useFetchRevisionsForEntry(); - const dispatchActionPromise = useDispatchActionPromise(); - const modalContext = useModalContext(); +const ConnectedHistoryModal: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedHistoryModal(props) { + const entryInfos = useSelector( + state => allDaysToEntries(state)[props.dayString], + ); + const threadInfos = useSelector(state => state.threadStore.threadInfos); + const dayLoadingStatus = useSelector(dayLoadingStatusSelector); + const entryLoadingStatus = useSelector(entryLoadingStatusSelector); + const calendarFilters = useSelector(nonExcludeDeletedCalendarFiltersSelector); + const callFetchEntries = useFetchEntries(); + const callFetchRevisionsForEntry = useFetchRevisionsForEntry(); + const dispatchActionPromise = useDispatchActionPromise(); + const modalContext = useModalContext(); - return ( - - ); - }); + return ( + + ); +}); export default ConnectedHistoryModal; diff --git a/web/modals/threads/gallery/thread-settings-media-gallery-item.react.js b/web/modals/threads/gallery/thread-settings-media-gallery-item.react.js --- a/web/modals/threads/gallery/thread-settings-media-gallery-item.react.js +++ b/web/modals/threads/gallery/thread-settings-media-gallery-item.react.js @@ -65,7 +65,8 @@ ); } -const MemoizedItem: React.ComponentType = - React.memo(MediaGalleryItem); +const MemoizedItem: React.ComponentType = React.memo( + MediaGalleryItem, +); export default MemoizedItem; diff --git a/web/modals/threads/settings/thread-settings-modal.react.js b/web/modals/threads/settings/thread-settings-modal.react.js --- a/web/modals/threads/settings/thread-settings-modal.react.js +++ b/web/modals/threads/settings/thread-settings-modal.react.js @@ -47,269 +47,268 @@ changeThreadSettingsActionTypes, ); -const ConnectedThreadSettingsModal: React.ComponentType = - React.memo(function ConnectedThreadSettingsModal(props) { - const changeInProgress = useSelector( - state => - deleteThreadLoadingStatusSelector(state) === 'loading' || - changeThreadSettingsLoadingStatusSelector(state) === 'loading', - ); - const threadInfo: ?ThreadInfo = useSelector( - state => threadInfoSelector(state)[props.threadID], - ); - const modalContext = useModalContext(); - const [errorMessage, setErrorMessage] = React.useState(''); - const [currentTabType, setCurrentTabType] = - React.useState('general'); - const [queuedChanges, setQueuedChanges] = React.useState( - Object.freeze({}), - ); - - const threadInfoWithNoName = React.useMemo(() => { - invariant(threadInfo, 'threadInfo should exist in threadInfoWithNoName'); - if (threadInfo.name === null || threadInfo.name === undefined) { - return threadInfo; - } - const withNoName = { ...threadInfo, name: undefined }; - return { - ...withNoName, - uiName: threadUIName(withNoName), - }; - }, [threadInfo]); - const resolvedThreadInfo = useResolvedThreadInfo(threadInfoWithNoName); - const namePlaceholder = resolvedThreadInfo.uiName; - - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); - const userInfos = useSelector(state => state.userStore.userInfos); - const otherMemberID = React.useMemo(() => { - if (!threadInfo) { - return null; - } - return getSingleOtherUser(threadInfo, viewerID); - }, [threadInfo, viewerID]); - const otherUserInfo = otherMemberID ? userInfos[otherMemberID] : null; - - const availableRelationshipActions = React.useMemo(() => { - if ( - !otherUserInfo || - !threadInfo?.type || - !threadTypeIsPersonal(threadInfo.type) - ) { - return ([]: RelationshipButton[]); - } - return getAvailableRelationshipButtons(otherUserInfo); - }, [otherUserInfo, threadInfo?.type]); +const ConnectedThreadSettingsModal: React.ComponentType = React.memo< + BaseProps, + void, +>(function ConnectedThreadSettingsModal(props) { + const changeInProgress = useSelector( + state => + deleteThreadLoadingStatusSelector(state) === 'loading' || + changeThreadSettingsLoadingStatusSelector(state) === 'loading', + ); + const threadInfo: ?ThreadInfo = useSelector( + state => threadInfoSelector(state)[props.threadID], + ); + const modalContext = useModalContext(); + const [errorMessage, setErrorMessage] = React.useState(''); + const [currentTabType, setCurrentTabType] = + React.useState('general'); + const [queuedChanges, setQueuedChanges] = React.useState( + Object.freeze({}), + ); - const canEditThreadName = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_NAME, - ); - const canEditThreadColor = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_COLOR, - ); - const canEditThreadDescription = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_DESCRIPTION, - ); - const canEditThreadPermissions = useThreadHasPermission( - threadInfo, - threadPermissions.EDIT_PERMISSIONS, - ); - const canDeleteThread = useThreadHasPermission( - threadInfo, - threadPermissions.DELETE_THREAD, - ); + const threadInfoWithNoName = React.useMemo(() => { + invariant(threadInfo, 'threadInfo should exist in threadInfoWithNoName'); + if (threadInfo.name === null || threadInfo.name === undefined) { + return threadInfo; + } + const withNoName = { ...threadInfo, name: undefined }; + return { + ...withNoName, + uiName: threadUIName(withNoName), + }; + }, [threadInfo]); + const resolvedThreadInfo = useResolvedThreadInfo(threadInfoWithNoName); + const namePlaceholder = resolvedThreadInfo.uiName; - const hasPermissionForTab = React.useCallback( - // ESLint doesn't recognize that invariant always throws - // eslint-disable-next-line consistent-return - (tab: TabType) => { - if (tab === 'general') { - return ( - canEditThreadName || canEditThreadColor || canEditThreadDescription - ); - } else if (tab === 'privacy') { - return canEditThreadPermissions; - } else if (tab === 'delete') { - return canDeleteThread; - } else if (tab === 'relationship') { - return true; - } - invariant(false, `invalid tab: ${tab}`); - }, - [ - canEditThreadName, - canEditThreadColor, - canEditThreadDescription, - canEditThreadPermissions, - canDeleteThread, - ], - ); + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const userInfos = useSelector(state => state.userStore.userInfos); + const otherMemberID = React.useMemo(() => { + if (!threadInfo) { + return null; + } + return getSingleOtherUser(threadInfo, viewerID); + }, [threadInfo, viewerID]); + const otherUserInfo = otherMemberID ? userInfos[otherMemberID] : null; - React.useEffect(() => { - if ( - currentTabType !== 'general' && - !hasPermissionForTab(currentTabType) - ) { - setCurrentTabType('general'); - } - }, [currentTabType, hasPermissionForTab]); + const availableRelationshipActions = React.useMemo(() => { + if ( + !otherUserInfo || + !threadInfo?.type || + !threadTypeIsPersonal(threadInfo.type) + ) { + return ([]: RelationshipButton[]); + } + return getAvailableRelationshipButtons(otherUserInfo); + }, [otherUserInfo, threadInfo?.type]); - React.useEffect(() => () => setErrorMessage(''), [currentTabType]); + const canEditThreadName = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_NAME, + ); + const canEditThreadColor = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_COLOR, + ); + const canEditThreadDescription = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_DESCRIPTION, + ); + const canEditThreadPermissions = useThreadHasPermission( + threadInfo, + threadPermissions.EDIT_PERMISSIONS, + ); + const canDeleteThread = useThreadHasPermission( + threadInfo, + threadPermissions.DELETE_THREAD, + ); - const tabsData: $ReadOnlyArray> = React.useMemo(() => { - if (!threadInfo) { - return []; + const hasPermissionForTab = React.useCallback( + // ESLint doesn't recognize that invariant always throws + // eslint-disable-next-line consistent-return + (tab: TabType) => { + if (tab === 'general') { + return ( + canEditThreadName || canEditThreadColor || canEditThreadDescription + ); + } else if (tab === 'privacy') { + return canEditThreadPermissions; + } else if (tab === 'delete') { + return canDeleteThread; + } else if (tab === 'relationship') { + return true; } + invariant(false, `invalid tab: ${tab}`); + }, + [ + canEditThreadName, + canEditThreadColor, + canEditThreadDescription, + canEditThreadPermissions, + canDeleteThread, + ], + ); - const result = [{ id: 'general', header: 'General' }]; - - // This UI needs to be updated to handle sidebars but we haven't gotten - // there yet. We'll probably end up ripping it out anyways, so for now we - // are just hiding the privacy tab for any thread that was created as a - // sidebar - const canSeePrivacyTab = - (queuedChanges['parentThreadID'] ?? threadInfo['parentThreadID']) && - !threadInfo.sourceMessageID && - (threadInfo.type === threadTypes.COMMUNITY_OPEN_SUBTHREAD || - threadInfo.type === threadTypes.COMMUNITY_SECRET_SUBTHREAD); + React.useEffect(() => { + if (currentTabType !== 'general' && !hasPermissionForTab(currentTabType)) { + setCurrentTabType('general'); + } + }, [currentTabType, hasPermissionForTab]); - if (canSeePrivacyTab) { - result.push({ id: 'privacy', header: 'Privacy' }); - } + React.useEffect(() => () => setErrorMessage(''), [currentTabType]); - if (availableRelationshipActions.length > 0 && otherUserInfo) { - result.push({ id: 'relationship', header: 'Relationship' }); - } + const tabsData: $ReadOnlyArray> = React.useMemo(() => { + if (!threadInfo) { + return []; + } - if (hasPermissionForTab('delete')) { - result.push({ id: 'delete', header: 'Delete' }); - } + const result = [{ id: 'general', header: 'General' }]; - return result; - }, [ - availableRelationshipActions.length, - hasPermissionForTab, - otherUserInfo, - queuedChanges, - threadInfo, - ]); + // This UI needs to be updated to handle sidebars but we haven't gotten + // there yet. We'll probably end up ripping it out anyways, so for now we + // are just hiding the privacy tab for any thread that was created as a + // sidebar + const canSeePrivacyTab = + (queuedChanges['parentThreadID'] ?? threadInfo['parentThreadID']) && + !threadInfo.sourceMessageID && + (threadInfo.type === threadTypes.COMMUNITY_OPEN_SUBTHREAD || + threadInfo.type === threadTypes.COMMUNITY_SECRET_SUBTHREAD); - const tabs = React.useMemo( - () => ( - - ), - [currentTabType, tabsData], - ); + if (canSeePrivacyTab) { + result.push({ id: 'privacy', header: 'Privacy' }); + } - const tabContent = React.useMemo(() => { - if (!threadInfo) { - return null; - } - if (currentTabType === 'general') { - return ( - - ); - } - if (currentTabType === 'privacy') { - return ( - - ); - } - if (currentTabType === 'relationship') { - invariant(otherUserInfo, 'otherUserInfo should be set'); - return ( - - ); - } - return ; - }, [ - availableRelationshipActions, - changeInProgress, - currentTabType, - namePlaceholder, - otherUserInfo, - queuedChanges, - threadInfo, - ]); + if (availableRelationshipActions.length > 0 && otherUserInfo) { + result.push({ id: 'relationship', header: 'Relationship' }); + } - const primaryButton = React.useMemo(() => { - if (!threadInfo) { - return null; - } + if (hasPermissionForTab('delete')) { + result.push({ id: 'delete', header: 'Delete' }); + } - if (currentTabType === 'general' || currentTabType === 'privacy') { - return ( - - ); - } + return result; + }, [ + availableRelationshipActions.length, + hasPermissionForTab, + otherUserInfo, + queuedChanges, + threadInfo, + ]); - if (currentTabType === 'relationship') { - return
; - } + const tabs = React.useMemo( + () => ( + + ), + [currentTabType, tabsData], + ); + const tabContent = React.useMemo(() => { + if (!threadInfo) { + return null; + } + if (currentTabType === 'general') { return ( - + ); + } + if (currentTabType === 'privacy') { + return ( + + ); + } + if (currentTabType === 'relationship') { + invariant(otherUserInfo, 'otherUserInfo should be set'); + return ( + ); - }, [changeInProgress, currentTabType, queuedChanges, threadInfo]); + } + return ; + }, [ + availableRelationshipActions, + changeInProgress, + currentTabType, + namePlaceholder, + otherUserInfo, + queuedChanges, + threadInfo, + ]); + const primaryButton = React.useMemo(() => { if (!threadInfo) { + return null; + } + + if (currentTabType === 'general' || currentTabType === 'privacy') { return ( - -
-

You no longer have permission to view this chat

-
-
+ ); } + if (currentTabType === 'relationship') { + return
; + } + return ( - + + ); + }, [changeInProgress, currentTabType, queuedChanges, threadInfo]); + + if (!threadInfo) { + return ( +
-
{tabContent}
-
{errorMessage}
+

You no longer have permission to view this chat

); - }); + } + + return ( + +
+
{tabContent}
+
{errorMessage}
+
+
+ ); +}); export default ConnectedThreadSettingsModal; diff --git a/web/navigation-panels/topbar.react.js b/web/navigation-panels/topbar.react.js --- a/web/navigation-panels/topbar.react.js +++ b/web/navigation-panels/topbar.react.js @@ -46,6 +46,6 @@ ); } -const MemoizedTopbar: React.ComponentType<{}> = React.memo<{}>(Topbar); +const MemoizedTopbar: React.ComponentType<{}> = React.memo<{}, void>(Topbar); export default MemoizedTopbar; diff --git a/web/socket.react.js b/web/socket.react.js --- a/web/socket.react.js +++ b/web/socket.react.js @@ -29,84 +29,86 @@ } from './selectors/socket-selectors.js'; import { decompressMessage } from './utils/decompress.js'; -const WebSocket: React.ComponentType = - React.memo(function WebSocket(props) { - const { keyserverID } = props; +const WebSocket: React.ComponentType = React.memo< + BaseSocketProps, + void, +>(function WebSocket(props) { + const { keyserverID } = props; - const cookie = useSelector(cookieSelector(keyserverID)); - const connection = useSelector(connectionSelector(keyserverID)); - invariant(connection, 'keyserver missing from keyserverStore'); - const active = useSelector( - state => - !!state.currentUserInfo && - !state.currentUserInfo.anonymous && - state.lifecycleState !== 'background', - ); + const cookie = useSelector(cookieSelector(keyserverID)); + const connection = useSelector(connectionSelector(keyserverID)); + invariant(connection, 'keyserver missing from keyserverStore'); + const active = useSelector( + state => + !!state.currentUserInfo && + !state.currentUserInfo.anonymous && + state.lifecycleState !== 'background', + ); - const openSocket = useSelector(openSocketSelector(keyserverID)); - invariant(openSocket, 'openSocket failed to be created'); - const sessionIdentification = useSelector( - sessionIdentificationSelector(keyserverID), - ); - const preRequestUserState = useSelector( - preRequestUserStateForSingleKeyserverSelector(keyserverID), - ); - const getInitialNotificationsEncryptedMessage = - useInitialNotificationsEncryptedMessage(keyserverID); - const getClientResponses = useSelector(state => - webGetClientResponsesSelector({ - state, - getInitialNotificationsEncryptedMessage, - keyserverID, - }), - ); - const sessionStateFunc = useSelector( - webSessionStateFuncSelector(keyserverID), - ); - const currentCalendarQuery = useSelector(webCalendarQuery); + const openSocket = useSelector(openSocketSelector(keyserverID)); + invariant(openSocket, 'openSocket failed to be created'); + const sessionIdentification = useSelector( + sessionIdentificationSelector(keyserverID), + ); + const preRequestUserState = useSelector( + preRequestUserStateForSingleKeyserverSelector(keyserverID), + ); + const getInitialNotificationsEncryptedMessage = + useInitialNotificationsEncryptedMessage(keyserverID); + const getClientResponses = useSelector(state => + webGetClientResponsesSelector({ + state, + getInitialNotificationsEncryptedMessage, + keyserverID, + }), + ); + const sessionStateFunc = useSelector( + webSessionStateFuncSelector(keyserverID), + ); + const currentCalendarQuery = useSelector(webCalendarQuery); - const activeThread = useSelector(foregroundActiveThreadSelector); + const activeThread = useSelector(foregroundActiveThreadSelector); - const dispatch = useDispatch(); - const dispatchActionPromise = useDispatchActionPromise(); + const dispatch = useDispatch(); + const dispatchActionPromise = useDispatchActionPromise(); - const lastCommunicatedPlatformDetails = useSelector( - lastCommunicatedPlatformDetailsSelector(keyserverID), - ); + const lastCommunicatedPlatformDetails = useSelector( + lastCommunicatedPlatformDetailsSelector(keyserverID), + ); - const activeSessionRecovery = useSelector( - state => - state.keyserverStore.keyserverInfos[keyserverID]?.connection - .activeSessionRecovery, - ); + const activeSessionRecovery = useSelector( + state => + state.keyserverStore.keyserverInfos[keyserverID]?.connection + .activeSessionRecovery, + ); - const fetchPendingUpdates = useFetchPendingUpdates(); + const fetchPendingUpdates = useFetchPendingUpdates(); - const isConnectedToInternet = useNetworkConnected(); + const isConnectedToInternet = useNetworkConnected(); - return ( - - ); - }); + return ( + + ); +}); export default WebSocket;