Fix conversation view from getting stale data

This commit is contained in:
Jamie Kyle 2025-05-02 11:14:43 -07:00 committed by GitHub
commit 4cc088c79e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 41 additions and 40 deletions

View file

@ -6,6 +6,7 @@ import type { LocalizerType } from '../types/I18N';
import type { NavTabPanelProps } from './NavTabs'; import type { NavTabPanelProps } from './NavTabs';
import { WhatsNewLink } from './WhatsNewLink'; import { WhatsNewLink } from './WhatsNewLink';
import type { UnreadStats } from '../util/countUnreadStats'; import type { UnreadStats } from '../util/countUnreadStats';
import type { SmartConversationViewProps } from '../state/smart/ConversationView';
export type ChatsTabProps = Readonly<{ export type ChatsTabProps = Readonly<{
otherTabsUnreadStats: UnreadStats; otherTabsUnreadStats: UnreadStats;
@ -15,7 +16,7 @@ export type ChatsTabProps = Readonly<{
hasFailedStorySends: boolean; hasFailedStorySends: boolean;
navTabsCollapsed: boolean; navTabsCollapsed: boolean;
onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void; onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void;
renderConversationView: () => JSX.Element; renderConversationView: (props: SmartConversationViewProps) => JSX.Element;
renderLeftPane: (props: NavTabPanelProps) => JSX.Element; renderLeftPane: (props: NavTabPanelProps) => JSX.Element;
renderMiniPlayer: (options: { shouldFlow: boolean }) => JSX.Element; renderMiniPlayer: (options: { shouldFlow: boolean }) => JSX.Element;
selectedConversationId: string | undefined; selectedConversationId: string | undefined;
@ -51,10 +52,12 @@ export function ChatsTab({
<div id="toast" /> <div id="toast" />
{selectedConversationId ? ( {selectedConversationId ? (
<div <div
// Use `key` to force the tree to fully re-mount
key={selectedConversationId}
className="Inbox__conversation" className="Inbox__conversation"
id={`conversation-${selectedConversationId}`} id={`conversation-${selectedConversationId}`}
> >
{renderConversationView()} {renderConversationView({ selectedConversationId })}
</div> </div>
) : ( ) : (
<div className="Inbox__no-conversation-open"> <div className="Inbox__no-conversation-open">

View file

@ -3,6 +3,7 @@
import React, { memo, useEffect, useRef } from 'react'; import React, { memo, useEffect, useRef } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { ChatsTab } from '../../components/ChatsTab'; import { ChatsTab } from '../../components/ChatsTab';
import type { SmartConversationViewProps } from './ConversationView';
import { SmartConversationView } from './ConversationView'; import { SmartConversationView } from './ConversationView';
import { SmartMiniPlayer } from './MiniPlayer'; import { SmartMiniPlayer } from './MiniPlayer';
import { SmartLeftPane } from './LeftPane'; import { SmartLeftPane } from './LeftPane';
@ -27,8 +28,8 @@ import {
getTargetedMessageSource, getTargetedMessageSource,
} from '../selectors/conversations'; } from '../selectors/conversations';
function renderConversationView() { function renderConversationView(props: SmartConversationViewProps) {
return <SmartConversationView />; return <SmartConversationView {...props} />;
} }
function renderLeftPane(props: NavTabPanelProps) { function renderLeftPane(props: NavTabPanelProps) {

View file

@ -11,7 +11,6 @@ import { SmartTimeline } from './Timeline';
import { import {
getActivePanel, getActivePanel,
getIsPanelAnimating, getIsPanelAnimating,
getSelectedConversationId,
getSelectedMessageIds, getSelectedMessageIds,
} from '../selectors/conversations'; } from '../selectors/conversations';
import { useComposerActions } from '../ducks/composer'; import { useComposerActions } from '../ducks/composer';
@ -34,43 +33,41 @@ function renderPanel(conversationId: string) {
return <ConversationPanel conversationId={conversationId} />; return <ConversationPanel conversationId={conversationId} />;
} }
export const SmartConversationView = memo( export type SmartConversationViewProps = Readonly<{
function SmartConversationView(): JSX.Element { selectedConversationId: string;
const conversationId = useSelector(getSelectedConversationId); }>;
if (!conversationId) { export const SmartConversationView = memo(function SmartConversationView(
throw new Error('SmartConversationView: No selected conversation'); props: SmartConversationViewProps
} ): JSX.Element {
const { toggleSelectMode } = useConversationsActions();
const selectedMessageIds = useSelector(getSelectedMessageIds);
const isSelectMode = selectedMessageIds != null;
const { toggleSelectMode } = useConversationsActions(); const { processAttachments } = useComposerActions();
const selectedMessageIds = useSelector(getSelectedMessageIds);
const isSelectMode = selectedMessageIds != null;
const { processAttachments } = useComposerActions(); const hasOpenModal = useSelector(isShowingAnyModal);
const activePanel = useSelector(getActivePanel);
const isPanelAnimating = useSelector(getIsPanelAnimating);
const shouldHideConversationView = activePanel && !isPanelAnimating;
const hasOpenModal = useSelector(isShowingAnyModal); const onExitSelectMode = useCallback(() => {
const activePanel = useSelector(getActivePanel); toggleSelectMode(false);
const isPanelAnimating = useSelector(getIsPanelAnimating); }, [toggleSelectMode]);
const shouldHideConversationView = activePanel && !isPanelAnimating;
const onExitSelectMode = useCallback(() => { return (
toggleSelectMode(false); <ConversationView
}, [toggleSelectMode]); conversationId={props.selectedConversationId}
hasOpenModal={hasOpenModal}
return ( hasOpenPanel={activePanel != null}
<ConversationView isSelectMode={isSelectMode}
conversationId={conversationId} onExitSelectMode={onExitSelectMode}
hasOpenModal={hasOpenModal} processAttachments={processAttachments}
hasOpenPanel={activePanel != null} renderCompositionArea={renderCompositionArea}
isSelectMode={isSelectMode} renderConversationHeader={renderConversationHeader}
onExitSelectMode={onExitSelectMode} renderTimeline={renderTimeline}
processAttachments={processAttachments} renderPanel={renderPanel}
renderCompositionArea={renderCompositionArea} shouldHideConversationView={shouldHideConversationView}
renderConversationHeader={renderConversationHeader} />
renderTimeline={renderTimeline} );
renderPanel={renderPanel} });
shouldHideConversationView={shouldHideConversationView}
/>
);
}
);