message: Fix right panel soft crashes chat rooms
diff: @@ -230,11 +230,14 @@ export default class RightPanelStore extends ReadyWatchingStore {
  }
  private emitAndUpdateSettings() {
  + this.filterValidCards(this.global);
  const storePanelGlobal = convertToStorePanel(this.global);
  SettingsStore.setValue("RightPanel.phasesGlobal", null, SettingLevel.DEVICE, storePanelGlobal);
  if (!!this.viewedRoomId) {
  - const storePanelThisRoom = convertToStorePanel(this.byRoom[this.viewedRoomId]);
  + const panelThisRoom = this.byRoom[this.viewedRoomId];
  + this.filterValidCards(panelThisRoom);
  + const storePanelThisRoom = convertToStorePanel(panelThisRoom);
  SettingsStore.setValue(
  "RightPanel.phases",
  this.viewedRoomId,
  @@ -245,6 +248,57 @@ export default class RightPanelStore extends ReadyWatchingStore {
  this.emit(UPDATE_EVENT, null);
  }
  + private filterValidCards(rightPanelForRoom?: IRightPanelForRoom) {
  + if (!rightPanelForRoom?.history) return;
  + rightPanelForRoom.history = rightPanelForRoom.history.filter((card) => this.isCardStateValid(card));
  + }
  +
  + private isCardStateValid(card: IRightPanelCard) {
  + // this function does a sanity check on the card. this is required because
  + // some phases require specific state properties that might not be available.
  + // This can be caused on if element is reloaded and the tries to reload right panel data from id's stored in the local storage.
  + // we store id's of users and matrix events. If are not yet fetched on reload the right panel cannot display them.
  + // or potentially other errors.
  + // (A nicer fix could be to indicate, that the right panel is loading if there is missing state data and re-emit if the data is available)
  + switch (card.phase) {
  + case RightPanelPhases.ThreadView:
  + if (!card.state.threadHeadEvent) {
  + console.warn("removed card from right panel because of missing threadHeadEvent in card state");
  + }
  + return !!card.state.threadHeadEvent;
  + case RightPanelPhases.RoomMemberInfo:
  + case RightPanelPhases.SpaceMemberInfo:
  + case RightPanelPhases.EncryptionPanel:
  + case RightPanelPhases.GroupMemberInfo:
  + if (!card.state.member) {
  + console.warn("removed card from right panel because of missing member in card state");
  + }
  + return !!card.state.member;
  + case RightPanelPhases.SpaceMemberList:
  + if (!card.state.spaceId) {
  + console.warn("removed card from right panel because of missing spaceId in card state");
  + }
  + return !!card.state.spaceId;
  + case RightPanelPhases.Room3pidMemberInfo:
  + case RightPanelPhases.Space3pidMemberInfo:
  + if (!card.state.memberInfoEvent) {
  + console.warn("removed card from right panel because of missing memberInfoEvent in card state");
  + }
  + return !!card.state.memberInfoEvent;
  + case RightPanelPhases.GroupRoomInfo:
  + if (!card.state.groupRoomId) {
  + console.warn("removed card from right panel because of missing groupRoomId in card state");
  + }
  + return !!card.state.groupRoomId;
  + case RightPanelPhases.Widget:
  + if (!card.state.widgetId) {
  + console.warn("removed card from right panel because of missing widgetId in card state");
  + }
  + return !!card.state.widgetId;
  + }
  + return true;
  + }
  +
  private setRightPanelCache(card: IRightPanelCard, roomId?: string) {
  const history = [{ phase: card.phase, state: card.state ?? {} }];
  this.byRoom[roomId ?? this.viewedRoomId] = { history, isOpen: true };
  