From 7a32a68a0b9b81d40de905305e0349a474c5ece2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 1 Mar 2022 11:15:23 +0000 Subject: [PATCH] Fix right panel soft crashes due to missing room prop (#7923) --- res/css/structures/_GroupView.scss | 1 + src/components/structures/MatrixChat.tsx | 2 +- src/components/structures/RightPanel.tsx | 29 ++++++++++++------- src/components/structures/ThreadView.tsx | 2 +- src/stores/right-panel/RightPanelStore.ts | 14 ++++----- .../right-panel/RightPanelStorePhases.ts | 20 ------------- 6 files changed, 29 insertions(+), 39 deletions(-) diff --git a/res/css/structures/_GroupView.scss b/res/css/structures/_GroupView.scss index 8d05135a9a..9cbf632cdd 100644 --- a/res/css/structures/_GroupView.scss +++ b/res/css/structures/_GroupView.scss @@ -18,6 +18,7 @@ limitations under the License. display: flex; flex-direction: column; overflow: hidden; + flex-grow: 1; } .mx_GroupView_error { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 89ca0b2754..ebc90f51ad 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -983,7 +983,7 @@ export default class MatrixChat extends React.PureComponent { this.viewWelcome(); return; } - if (!this.state.currentGroupId && !this.state.currentRoomId) { + if (!this.state.currentGroupId && !this.state.currentRoomId && !this.state.currentUserId) { this.viewHome(); } } diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index 0ed9f4cee5..bb772faa80 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -92,10 +92,23 @@ export default class RightPanel extends React.Component { } public static getDerivedStateFromProps(props: IProps): Partial { - const currentCard = RightPanelStore.instance.currentCardForRoom(props.room.roomId); + let currentCard: IRightPanelCard; + if (props.room) { + currentCard = RightPanelStore.instance.currentCardForRoom(props.room.roomId); + } + if (props.groupId) { + currentCard = RightPanelStore.instance.currentGroup; + } + + if (currentCard?.phase && !RightPanelStore.instance.isPhaseValid(currentCard.phase, !!props.room)) { + // XXX: We can probably get rid of this workaround once GroupView is dead, it's unmounting happens weirdly + // late causing the app to soft-crash due to lack of a room object being passed to a RightPanel + return null; // skip this update, we're about to be unmounted and don't have the appropriate props + } + return { - cardState: currentCard.state, - phase: currentCard.phase, + cardState: currentCard?.state, + phase: currentCard?.phase, }; } @@ -116,11 +129,7 @@ export default class RightPanel extends React.Component { }; private onRightPanelStoreUpdate = () => { - const currentCard = RightPanelStore.instance.currentCardForRoom(this.props.room.roomId); - this.setState({ - cardState: currentCard.state, - phase: currentCard.phase, - }); + this.setState({ ...RightPanel.getDerivedStateFromProps(this.props) as IState }); }; private onClose = () => { @@ -137,7 +146,7 @@ export default class RightPanel extends React.Component { }); } else if ( this.state.phase === RightPanelPhases.EncryptionPanel && - this.state.cardState.verificationRequest && this.state.cardState.verificationRequest.pending + this.state.cardState.verificationRequest?.pending ) { // When the user clicks close on the encryption panel cancel the pending request first if any this.state.cardState.verificationRequest.cancel(); @@ -152,7 +161,7 @@ export default class RightPanel extends React.Component { public render(): JSX.Element { let card =
; - const roomId = this.props.room ? this.props.room.roomId : undefined; + const roomId = this.props.room?.roomId; const phase = this.props.overwriteCard?.phase ?? this.state.phase; const cardState = this.props.overwriteCard?.state ?? this.state.cardState; switch (phase) { diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index b3b2991853..bc8789b11a 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -106,7 +106,7 @@ export default class ThreadView extends React.Component { public componentWillUnmount(): void { this.teardownThread(); - dis.unregister(this.dispatcherRef); + if (this.dispatcherRef) dis.unregister(this.dispatcherRef); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); room.removeListener(ThreadEvent.New, this.onNewThread); SettingsStore.unwatchSetting(this.layoutWatcherRef); diff --git a/src/stores/right-panel/RightPanelStore.ts b/src/stores/right-panel/RightPanelStore.ts index e092b5cc7b..3ea50ec0bb 100644 --- a/src/stores/right-panel/RightPanelStore.ts +++ b/src/stores/right-panel/RightPanelStore.ts @@ -156,7 +156,7 @@ export default class RightPanelStore extends ReadyWatchingStore { const cardState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state); // Checks for wrong SetRightPanelPhase requests - if (!this.isPhaseActionValid(targetPhase)) return; + if (!this.isPhaseValid(targetPhase)) return; if ((targetPhase === this.currentCardForRoom(rId)?.phase && !!cardState)) { // Update state: set right panel with a new state but keep the phase (dont know it this is ever needed...) @@ -195,16 +195,16 @@ export default class RightPanelStore extends ReadyWatchingStore { const pState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state); // Checks for wrong SetRightPanelPhase requests - if (!this.isPhaseActionValid(targetPhase)) return; + if (!this.isPhaseValid(targetPhase)) return; - let roomCache = this.byRoom[rId]; + const roomCache = this.byRoom[rId]; if (!!roomCache) { // append new phase roomCache.history.push({ state: pState, phase: targetPhase }); roomCache.isOpen = allowClose ? roomCache.isOpen : true; } else { // setup room panel cache with the new card - roomCache = { + this.byRoom[rId] = { history: [{ phase: targetPhase, state: pState ?? {} }], // if there was no right panel store object the the panel was closed -> keep it closed, except if allowClose==false isOpen: !allowClose, @@ -345,18 +345,18 @@ export default class RightPanelStore extends ReadyWatchingStore { return null; } - private isPhaseActionValid(targetPhase) { + public isPhaseValid(targetPhase: RightPanelPhases, isViewingRoom = this.isViewingRoom): boolean { if (!RightPanelPhases[targetPhase]) { logger.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`); return false; } - if (GROUP_PHASES.includes(targetPhase) && this.isViewingRoom) { + if (GROUP_PHASES.includes(targetPhase) && isViewingRoom) { logger.warn( `Tried to switch right panel to a group phase: ${targetPhase}, ` + `but we are currently not viewing a group`, ); return false; - } else if (!GROUP_PHASES.includes(targetPhase) && !this.isViewingRoom) { + } else if (!GROUP_PHASES.includes(targetPhase) && !isViewingRoom) { logger.warn( `Tried to switch right panel to a room phase: ${targetPhase}, ` + `but we are currently not viewing a room`, diff --git a/src/stores/right-panel/RightPanelStorePhases.ts b/src/stores/right-panel/RightPanelStorePhases.ts index 10e7b64918..59d5a2f73a 100644 --- a/src/stores/right-panel/RightPanelStorePhases.ts +++ b/src/stores/right-panel/RightPanelStorePhases.ts @@ -56,23 +56,3 @@ export function backLabelForPhase(phase: RightPanelPhases) { } return null; } - -// These are the phases that are safe to persist (the ones that don't require additional -// arguments). -export const RIGHT_PANEL_PHASES_NO_ARGS = [ - RightPanelPhases.RoomSummary, - RightPanelPhases.NotificationPanel, - RightPanelPhases.PinnedMessages, - RightPanelPhases.FilePanel, - RightPanelPhases.RoomMemberList, - RightPanelPhases.GroupMemberList, - RightPanelPhases.GroupRoomList, - RightPanelPhases.Timeline, -]; - -// Subset of phases visible in the Space View -export const RIGHT_PANEL_SPACE_PHASES = [ - RightPanelPhases.SpaceMemberList, - RightPanelPhases.Space3pidMemberInfo, - RightPanelPhases.SpaceMemberInfo, -];