Right panel store refactor (#7313)

Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
This commit is contained in:
Timo 2022-01-05 16:14:44 +01:00 committed by GitHub
parent 8e881336ab
commit 325e2ba99b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 765 additions and 811 deletions

View file

@ -27,9 +27,9 @@ import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from '../../MatrixClientPeg';
import EventIndexPeg from "../../indexing/EventIndexPeg";
import { _t } from '../../languageHandler';
import BaseCard from "../views/right_panel/BaseCard";
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
import DesktopBuildsNotice, { WarningKind } from "../views/elements/DesktopBuildsNotice";
import BaseCard from "../views/right_panel/BaseCard";
import { replaceableComponent } from "../../utils/replaceableComponent";
import ResizeNotifier from '../../utils/ResizeNotifier';
import TimelinePanel from "./TimelinePanel";

View file

@ -38,13 +38,14 @@ import GroupStore from '../../stores/GroupStore';
import FlairStore from '../../stores/FlairStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
import { makeGroupPermalink, makeUserPermalink } from "../../utils/permalinks/Permalinks";
import RightPanelStore from "../../stores/RightPanelStore";
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
import AutoHideScrollbar from "./AutoHideScrollbar";
import { mediaFromMxc } from "../../customisations/Media";
import { replaceableComponent } from "../../utils/replaceableComponent";
import { createSpaceFromCommunity } from "../../utils/space";
import { Action } from "../../dispatcher/actions";
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
const LONG_DESC_PLACEHOLDER = _td(
`<h1>HTML for your community's page</h1>
@ -427,7 +428,7 @@ export default class GroupView extends React.Component {
membershipBusy: false,
publicityBusy: false,
inviterProfile: null,
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
showRightPanel: RightPanelStore.instance.isOpenForGroup,
showUpgradeNotice: !localStorage.getItem(UPGRADE_NOTICE_LS_KEY),
};
@ -439,7 +440,7 @@ export default class GroupView extends React.Component {
this._initGroupStore(this.props.groupId, true);
this._dispatcherRef = dis.register(this._onAction);
this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate);
RightPanelStore.instance.on(UPDATE_EVENT, this._onRightPanelStoreUpdate);
}
componentWillUnmount() {
@ -447,10 +448,7 @@ export default class GroupView extends React.Component {
this._matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership);
dis.unregister(this._dispatcherRef);
// Remove RightPanelStore listener
if (this._rightPanelStoreToken) {
this._rightPanelStoreToken.remove();
}
RightPanelStore.instance.off(UPDATE_EVENT, this._onRightPanelStoreUpdate);
}
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
@ -468,7 +466,7 @@ export default class GroupView extends React.Component {
_onRightPanelStoreUpdate = () => {
this.setState({
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
showRightPanel: RightPanelStore.instance.isOpenForGroup,
});
};
@ -824,10 +822,7 @@ export default class GroupView extends React.Component {
};
_onAdminsLinkClick = () => {
dis.dispatch({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.GroupMemberList,
});
RightPanelStore.instance.setCard({ phase: RightPanelPhases.GroupMemberList });
};
_getGroupSection() {

View file

@ -44,7 +44,6 @@ import CallContainer from '../views/voip/CallContainer';
import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload";
import RoomListStore from "../../stores/room-list/RoomListStore";
import NonUrgentToastContainer from "./NonUrgentToastContainer";
import { ToggleRightPanelPayload } from "../../dispatcher/payloads/ToggleRightPanelPayload";
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
import Modal from "../../Modal";
import { ICollapseConfig } from "../../resizer/distributors/collapse";
@ -68,6 +67,7 @@ import GroupFilterPanel from './GroupFilterPanel';
import CustomRoomTagPanel from './CustomRoomTagPanel';
import { mediaFromMxc } from "../../customisations/Media";
import LegacyCommunityPreview from "./LegacyCommunityPreview";
import RightPanelStore from '../../stores/right-panel/RightPanelStore';
// We need to fetch each pinned message individually (if we don't already have it)
// so each pinned message may trigger a request. Limit the number per room for sanity.
@ -489,10 +489,7 @@ class LoggedInView extends React.Component<IProps, IState> {
break;
case NavigationAction.ToggleRoomSidePanel:
if (this.props.page_type === "room_view" || this.props.page_type === "group_view") {
dis.dispatch<ToggleRightPanelPayload>({
action: Action.ToggleRightPanel,
type: this.props.page_type === "room_view" ? "room" : "group",
});
RightPanelStore.instance.togglePanel();
handled = true;
}
break;

View file

@ -20,17 +20,12 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { RoomState } from "matrix-js-sdk/src/models/room-state";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { throttle } from 'lodash';
import dis from '../../dispatcher/dispatcher';
import GroupStore from '../../stores/GroupStore';
import {
RIGHT_PANEL_PHASES_NO_ARGS,
RIGHT_PANEL_SPACE_PHASES,
RightPanelPhases,
} from "../../stores/RightPanelStorePhases";
import RightPanelStore from "../../stores/RightPanelStore";
import { RightPanelPhases } from '../../stores/right-panel/RightPanelStorePhases';
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { Action } from "../../dispatcher/actions";
import RoomSummaryCard from "../views/right_panel/RoomSummaryCard";
@ -50,16 +45,17 @@ import ThreadPanel from "./ThreadPanel";
import NotificationPanel from "./NotificationPanel";
import ResizeNotifier from "../../utils/ResizeNotifier";
import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard";
import SpaceStore from "../../stores/spaces/SpaceStore";
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
import { E2EStatus } from '../../utils/ShieldUtils';
import { dispatchShowThreadsPanelEvent } from '../../dispatcher/dispatch-actions/threads';
import TimelineCard from '../views/right_panel/TimelineCard';
import { UPDATE_EVENT } from '../../stores/AsyncStore';
import { IRightPanelCard, IRightPanelCardState } from '../../stores/right-panel/RightPanelStoreIPanelState';
interface IProps {
room?: Room; // if showing panels for a given room, this is set
groupId?: string; // if showing panels for a given group, this is set
member?: RoomMember; // used if we know the room member ahead of opening the panel
overwriteCard?: IRightPanelCard; // used to display a custom card and ignoring the RightPanelStore (used for UserView)
resizeNotifier: ResizeNotifier;
permalinkCreator?: RoomPermalinkCreator;
e2eStatus?: E2EStatus;
@ -68,17 +64,8 @@ interface IProps {
interface IState {
phase: RightPanelPhases;
isUserPrivilegedInGroup?: boolean;
member?: RoomMember;
verificationRequest?: VerificationRequest;
verificationRequestPromise?: Promise<VerificationRequest>;
space?: Room;
widgetId?: string;
groupRoomId?: string;
groupId?: string;
event: MatrixEvent;
initialEvent?: MatrixEvent;
initialEventHighlighted?: boolean;
searchQuery: string;
cardState?: IRightPanelCardState;
}
@replaceableComponent("structures.RightPanel")
@ -89,11 +76,11 @@ export default class RightPanel extends React.Component<IProps, IState> {
constructor(props, context) {
super(props, context);
this.state = {
...RightPanelStore.getSharedInstance().roomPanelPhaseParams,
phase: this.getPhaseFromProps(),
cardState: RightPanelStore.instance.currentCard?.state,
phase: RightPanelStore.instance.currentCard?.phase,
isUserPrivilegedInGroup: null,
member: this.getUserForPanel(),
searchQuery: "",
};
}
@ -102,56 +89,11 @@ export default class RightPanel extends React.Component<IProps, IState> {
this.forceUpdate();
}, 500, { leading: true, trailing: true });
// Helper function to split out the logic for getPhaseFromProps() and the constructor
// as both are called at the same time in the constructor.
private getUserForPanel(): RoomMember {
if (this.state && this.state.member) return this.state.member;
const lastParams = RightPanelStore.getSharedInstance().roomPanelPhaseParams;
return this.props.member || lastParams['member'];
}
// gets the current phase from the props and also maybe the store
private getPhaseFromProps() {
const rps = RightPanelStore.getSharedInstance();
const userForPanel = this.getUserForPanel();
if (this.props.groupId) {
if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.groupPanelPhase)) {
dis.dispatch({ action: Action.SetRightPanelPhase, phase: RightPanelPhases.GroupMemberList });
return RightPanelPhases.GroupMemberList;
}
return rps.groupPanelPhase;
} else if (SpaceStore.spacesEnabled && this.props.room?.isSpaceRoom()
&& !RIGHT_PANEL_SPACE_PHASES.includes(rps.roomPanelPhase)
) {
return RightPanelPhases.SpaceMemberList;
} else if (userForPanel) {
// XXX FIXME AAAAAARGH: What is going on with this class!? It takes some of its state
// from its props and some from a store, except if the contents of the store changes
// while it's mounted in which case it replaces all of its state with that of the store,
// except it uses a dispatch instead of a normal store listener?
// Unfortunately rewriting this would almost certainly break showing the right panel
// in some of the many cases, and I don't have time to re-architect it and test all
// the flows now, so adding yet another special case so if the store thinks there is
// a verification going on for the member we're displaying, we show that, otherwise
// we race if a verification is started while the panel isn't displayed because we're
// not mounted in time to get the dispatch.
// Until then, let this code serve as a warning from history.
if (
rps.roomPanelPhaseParams.member &&
userForPanel.userId === rps.roomPanelPhaseParams.member.userId &&
rps.roomPanelPhaseParams.verificationRequest
) {
return rps.roomPanelPhase;
}
return RightPanelPhases.RoomMemberInfo;
}
return rps.roomPanelPhase;
}
public componentDidMount(): void {
this.dispatcherRef = dis.register(this.onAction);
const cli = this.context;
cli.on("RoomState.members", this.onRoomStateMember);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
this.initGroupStore(this.props.groupId);
}
@ -160,6 +102,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
if (this.context) {
this.context.removeListener("RoomState.members", this.onRoomStateMember);
}
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
this.unregisterGroupStore();
}
@ -193,42 +136,36 @@ export default class RightPanel extends React.Component<IProps, IState> {
// redraw the badge on the membership list
if (this.state.phase === RightPanelPhases.RoomMemberList && member.roomId === this.props.room.roomId) {
this.delayedUpdate();
} else if (this.state.phase === RightPanelPhases.RoomMemberInfo && member.roomId === this.props.room.roomId &&
member.userId === this.state.member.userId) {
} else if (
this.state.phase === RightPanelPhases.RoomMemberInfo && member.roomId === this.props.room.roomId &&
member.userId === this.state.cardState.member.userId
) {
// refresh the member info (e.g. new power level)
this.delayedUpdate();
}
};
private onRightPanelStoreUpdate = () => {
const currentPanel = RightPanelStore.instance.currentCard;
this.setState({
cardState: currentPanel.state,
phase: currentPanel.phase,
});
};
private onAction = (payload: ActionPayload) => {
const isChangingRoom = payload.action === Action.ViewRoom && payload.room_id !== this.props.room.roomId;
const isViewingThread = this.state.phase === RightPanelPhases.ThreadView;
if (isChangingRoom && isViewingThread) {
dispatchShowThreadsPanelEvent();
}
if (payload.action === Action.AfterRightPanelPhaseChange) {
this.setState({
phase: payload.phase,
groupRoomId: payload.groupRoomId,
groupId: payload.groupId,
member: payload.member,
event: payload.event,
initialEvent: payload.initialEvent,
initialEventHighlighted: payload.highlighted,
verificationRequest: payload.verificationRequest,
verificationRequestPromise: payload.verificationRequestPromise,
widgetId: payload.widgetId,
space: payload.space,
});
}
};
private onClose = () => {
// XXX: There are three different ways of 'closing' this panel depending on what state
// things are in... this knows far more than it should do about the state of the rest
// of the app and is generally a bit silly.
if (this.props.member) {
if (this.props.overwriteCard?.state?.member) {
// If we have a user prop then we're displaying a user from the 'user' page type
// in LoggedInView, so need to change the page type to close the panel (we switch
// to the home page which is not obviously the correct thing to do, but I'm not sure
@ -238,16 +175,12 @@ export default class RightPanel extends React.Component<IProps, IState> {
});
} else if (
this.state.phase === RightPanelPhases.EncryptionPanel &&
this.state.verificationRequest && this.state.verificationRequest.pending
this.state.cardState.verificationRequest && this.state.cardState.verificationRequest.pending
) {
// When the user clicks close on the encryption panel cancel the pending request first if any
this.state.verificationRequest.cancel();
this.state.cardState.verificationRequest.cancel();
} else {
// the RightPanelStore has no way of knowing which mode room/group it is in, so we handle closing here
dis.dispatch({
action: Action.ToggleRightPanel,
type: this.props.groupId ? "group" : "room",
});
RightPanelStore.instance.togglePanel();
}
};
@ -256,13 +189,14 @@ export default class RightPanel extends React.Component<IProps, IState> {
};
public render(): JSX.Element {
let panel = <div />;
let card = <div />;
const roomId = this.props.room ? this.props.room.roomId : undefined;
switch (this.state.phase) {
const phase = this.props.overwriteCard?.phase ?? this.state.phase;
const cardState = this.props.overwriteCard?.state ?? this.state.cardState;
switch (phase) {
case RightPanelPhases.RoomMemberList:
if (roomId) {
panel = <MemberList
card = <MemberList
roomId={roomId}
key={roomId}
onClose={this.onClose}
@ -272,9 +206,9 @@ export default class RightPanel extends React.Component<IProps, IState> {
}
break;
case RightPanelPhases.SpaceMemberList:
panel = <MemberList
roomId={this.state.space ? this.state.space.roomId : roomId}
key={this.state.space ? this.state.space.roomId : roomId}
card = <MemberList
roomId={cardState.spaceId ? cardState.spaceId : roomId}
key={cardState.spaceId ? cardState.spaceId : roomId}
onClose={this.onClose}
searchQuery={this.state.searchQuery}
onSearchQueryChanged={this.onSearchQueryChanged}
@ -283,61 +217,66 @@ export default class RightPanel extends React.Component<IProps, IState> {
case RightPanelPhases.GroupMemberList:
if (this.props.groupId) {
panel = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
card = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
}
break;
case RightPanelPhases.GroupRoomList:
panel = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
card = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
break;
case RightPanelPhases.RoomMemberInfo:
case RightPanelPhases.SpaceMemberInfo:
case RightPanelPhases.EncryptionPanel:
panel = <UserInfo
user={this.state.member}
room={this.context.getRoom(this.state.member.roomId) ?? this.props.room}
key={roomId || this.state.member.userId}
case RightPanelPhases.EncryptionPanel: {
const roomMember = cardState.member instanceof RoomMember
? cardState.member
: undefined;
card = <UserInfo
user={cardState.member}
room={this.context.getRoom(roomMember?.roomId) ?? this.props.room}
key={roomId || cardState.member.userId}
onClose={this.onClose}
phase={this.state.phase}
verificationRequest={this.state.verificationRequest}
verificationRequestPromise={this.state.verificationRequestPromise}
phase={phase}
verificationRequest={cardState.verificationRequest}
verificationRequestPromise={cardState.verificationRequestPromise}
/>;
break;
}
case RightPanelPhases.Room3pidMemberInfo:
case RightPanelPhases.Space3pidMemberInfo:
panel = <ThirdPartyMemberInfo event={this.state.event} key={roomId} />;
card = <ThirdPartyMemberInfo event={cardState.memberInfoEvent} key={roomId} />;
break;
case RightPanelPhases.GroupMemberInfo:
panel = <UserInfo
user={this.state.member}
card = <UserInfo
user={cardState.member}
groupId={this.props.groupId}
key={this.state.member.userId}
phase={this.state.phase}
onClose={this.onClose} />;
key={cardState.member.userId}
phase={phase}
onClose={this.onClose}
/>;
break;
case RightPanelPhases.GroupRoomInfo:
panel = <GroupRoomInfo
groupRoomId={this.state.groupRoomId}
card = <GroupRoomInfo
groupRoomId={cardState.groupRoomId}
groupId={this.props.groupId}
key={this.state.groupRoomId} />;
key={cardState.groupRoomId}
/>;
break;
case RightPanelPhases.NotificationPanel:
panel = <NotificationPanel onClose={this.onClose} />;
card = <NotificationPanel onClose={this.onClose} />;
break;
case RightPanelPhases.PinnedMessages:
if (SettingsStore.getValue("feature_pinning")) {
panel = <PinnedMessagesCard room={this.props.room} onClose={this.onClose} />;
card = <PinnedMessagesCard room={this.props.room} onClose={this.onClose} />;
}
break;
case RightPanelPhases.Timeline:
if (!SettingsStore.getValue("feature_maximised_widgets")) break;
panel = <TimelineCard
card = <TimelineCard
classNames="mx_ThreadPanel mx_TimelineCard"
room={this.props.room}
timelineSet={this.props.room.getUnfilteredTimelineSet()}
@ -348,23 +287,24 @@ export default class RightPanel extends React.Component<IProps, IState> {
/>;
break;
case RightPanelPhases.FilePanel:
panel = <FilePanel roomId={roomId} resizeNotifier={this.props.resizeNotifier} onClose={this.onClose} />;
card = <FilePanel roomId={roomId} resizeNotifier={this.props.resizeNotifier} onClose={this.onClose} />;
break;
case RightPanelPhases.ThreadView:
panel = <ThreadView
card = <ThreadView
room={this.props.room}
resizeNotifier={this.props.resizeNotifier}
onClose={this.onClose}
mxEvent={this.state.event}
initialEvent={this.state.initialEvent}
initialEventHighlighted={this.state.initialEventHighlighted}
mxEvent={cardState.threadHeadEvent}
initialEvent={cardState.initialEvent}
isInitialEventHighlighted={cardState.isInitialEventHighlighted}
permalinkCreator={this.props.permalinkCreator}
e2eStatus={this.props.e2eStatus} />;
e2eStatus={this.props.e2eStatus}
/>;
break;
case RightPanelPhases.ThreadPanel:
panel = <ThreadPanel
card = <ThreadPanel
roomId={roomId}
resizeNotifier={this.props.resizeNotifier}
onClose={this.onClose}
@ -373,17 +313,21 @@ export default class RightPanel extends React.Component<IProps, IState> {
break;
case RightPanelPhases.RoomSummary:
panel = <RoomSummaryCard room={this.props.room} onClose={this.onClose} />;
card = <RoomSummaryCard room={this.props.room} onClose={this.onClose} />;
break;
case RightPanelPhases.Widget:
panel = <WidgetCard room={this.props.room} widgetId={this.state.widgetId} onClose={this.onClose} />;
card = <WidgetCard
room={this.props.room}
widgetId={cardState.widgetId}
onClose={this.onClose}
/>;
break;
}
return (
<aside className="mx_RightPanel dark-panel" id="mx_RightPanel">
{ panel }
{ card }
</aside>
);
}

View file

@ -84,6 +84,7 @@ interface IState {
@replaceableComponent("structures.RoomStatusBar")
export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
private unmounted = false;
public static contextType = MatrixClientContext;
constructor(props: IProps, context: typeof MatrixClientContext) {
@ -110,6 +111,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
}
public componentWillUnmount(): void {
this.unmounted = true;
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
const client = this.context;
if (client) {
@ -122,6 +124,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
if (state === "SYNCING" && prevState === "SYNCING") {
return;
}
if (this.unmounted) return;
this.setState({
syncState: state,
syncStateData: data,

View file

@ -53,7 +53,7 @@ import WidgetEchoStore from '../../stores/WidgetEchoStore';
import SettingsStore from "../../settings/SettingsStore";
import { Layout } from "../../settings/enums/Layout";
import AccessibleButton from "../views/elements/AccessibleButton";
import RightPanelStore from "../../stores/RightPanelStore";
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
import { haveTileForEvent } from "../views/rooms/EventTile";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import MatrixClientContext, { withMatrixClientHOC, MatrixClientProps } from "../../contexts/MatrixClientContext";
@ -98,8 +98,7 @@ import { dispatchShowThreadEvent } from '../../dispatcher/dispatch-actions/threa
import { fetchInitialEvent } from "../../utils/EventUtils";
import { ComposerType } from "../../dispatcher/payloads/ComposerInsertPayload";
import AppsDrawer from '../views/rooms/AppsDrawer';
import { SetRightPanelPhasePayload } from '../../dispatcher/payloads/SetRightPanelPhasePayload';
import { RightPanelPhases } from '../../stores/RightPanelStorePhases';
import { RightPanelPhases } from '../../stores/right-panel/RightPanelStorePhases';
const DEBUG = false;
let debuglog = function(msg: string) {};
@ -214,7 +213,6 @@ export interface IRoomState {
export class RoomView extends React.Component<IRoomProps, IRoomState> {
private readonly dispatcherRef: string;
private readonly roomStoreToken: EventSubscription;
private readonly rightPanelStoreToken: EventSubscription;
private settingWatchers: string[];
private unmounted = false;
@ -246,7 +244,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
canPeek: false,
showApps: false,
isPeeking: false,
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
showRightPanel: RightPanelStore.instance.isOpenForRoom,
joining: false,
atEndOfLiveTimeline: true,
atEndOfLiveTimelineInit: false,
@ -289,7 +287,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
this.context.on("Event.decrypted", this.onEventDecrypted);
// Start listening for RoomViewStore updates
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
WidgetStore.instance.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
@ -337,13 +336,9 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
});
if (WidgetLayoutStore.instance.hasMaximisedWidget(this.state.room)) {
// Show chat in right panel when a widget is maximised
dis.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.Timeline,
});
RightPanelStore.instance.setCard({ phase: RightPanelPhases.Timeline });
}
this.checkWidgets(this.state.room);
this.checkRightPanel(this.state.room);
};
private checkWidgets = (room) => {
@ -361,22 +356,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
: MainSplitContentType.Timeline;
};
private checkRightPanel = (room) => {
// This is a hack to hide the chat. This should not be necessary once the right panel
// phase is stored per room. (need to be done after check widget so that mainSplitContentType is updated)
if (
RightPanelStore.getSharedInstance().roomPanelPhase === RightPanelPhases.Timeline &&
this.state.showRightPanel &&
!WidgetLayoutStore.instance.hasMaximisedWidget(this.state.room)
) {
// Two timelines are shown prevent this by hiding the right panel
dis.dispatch({
action: Action.ToggleRightPanel,
type: "room",
});
}
};
private onReadReceiptsChange = () => {
this.setState({
showReadReceipts: SettingsStore.getValue("showReadReceipts", this.state.roomId),
@ -754,11 +733,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (this.roomStoreToken) {
this.roomStoreToken.remove();
}
// Remove RightPanelStore listener
if (this.rightPanelStoreToken) {
this.rightPanelStoreToken.remove();
}
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
WidgetEchoStore.removeListener(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
WidgetStore.instance.removeListener(UPDATE_EVENT, this.onWidgetStoreUpdate);
@ -793,7 +769,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private onRightPanelStoreUpdate = () => {
this.setState({
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
showRightPanel: RightPanelStore.instance.isOpenForRoom,
});
};
@ -1039,7 +1015,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
this.updateE2EStatus(room);
this.updatePermissions(room);
this.checkWidgets(room);
this.checkRightPanel(room);
this.setState({
liveTimeline: room.getLiveTimeline(),

View file

@ -18,7 +18,6 @@ import React, { RefObject, useContext, useRef, useState } from "react";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { JoinRule, Preset } from "matrix-js-sdk/src/@types/partials";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventSubscription } from "fbemitter";
import { logger } from "matrix-js-sdk/src/logger";
import MatrixClientContext from "../../contexts/MatrixClientContext";
@ -43,9 +42,8 @@ import MainSplit from './MainSplit';
import ErrorBoundary from "../views/elements/ErrorBoundary";
import { ActionPayload } from "../../dispatcher/payloads";
import RightPanel from "./RightPanel";
import RightPanelStore from "../../stores/RightPanelStore";
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
import { SetRightPanelPhasePayload } from "../../dispatcher/payloads/SetRightPanelPhasePayload";
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
import { useStateArray } from "../../hooks/useStateArray";
import SpacePublicShare from "../views/spaces/SpacePublicShare";
import {
@ -85,6 +83,7 @@ import { useDispatcher } from "../../hooks/useDispatcher";
import { useRoomState } from "../../hooks/useRoomState";
import { shouldShowComponent } from "../../customisations/helpers/UIComponents";
import { UIComponent } from "../../settings/UIFeature";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
interface IProps {
space: Room;
@ -164,10 +163,9 @@ const SpaceInfo = ({ space }: { space: Room }) => {
kind="link"
className="mx_SpaceRoomView_info_memberCount"
onClick={() => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
RightPanelStore.instance.setCard({
phase: RightPanelPhases.RoomMemberList,
refireParams: { space },
state: { spaceId: space.roomId },
});
}}
>
@ -473,11 +471,7 @@ const SpaceLanding = ({ space }: { space: Room }) => {
}
const onMembersClick = () => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.RoomMemberList,
refireParams: { space },
});
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomMemberList, state: { spaceId: space.roomId } });
};
return <div className="mx_SpaceRoomView_landing">
@ -796,7 +790,6 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
private readonly creator: string;
private readonly dispatcherRef: string;
private readonly rightPanelStoreToken: EventSubscription;
constructor(props, context) {
super(props, context);
@ -813,18 +806,18 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
this.state = {
phase,
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
showRightPanel: RightPanelStore.instance.isOpenForRoom,
myMembership: this.props.space.getMyMembership(),
};
this.dispatcherRef = defaultDispatcher.register(this.onAction);
this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
this.context.on("Room.myMembership", this.onMyMembership);
}
componentWillUnmount() {
defaultDispatcher.unregister(this.dispatcherRef);
this.rightPanelStoreToken.remove();
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
this.context.off("Room.myMembership", this.onMyMembership);
}
@ -836,7 +829,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
private onRightPanelStoreUpdate = () => {
this.setState({
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
showRightPanel: RightPanelStore.instance.isOpenForRoom,
});
};
@ -849,28 +842,19 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
if (payload.action !== Action.ViewUser && payload.action !== "view_3pid_invite") return;
if (payload.action === Action.ViewUser && payload.member) {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
RightPanelStore.instance.setCard({
phase: RightPanelPhases.SpaceMemberInfo,
refireParams: {
space: this.props.space,
member: payload.member,
},
state: { spaceId: this.props.space.roomId, member: payload.member },
});
} else if (payload.action === "view_3pid_invite" && payload.event) {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
RightPanelStore.instance.setCard({
phase: RightPanelPhases.Space3pidMemberInfo,
refireParams: {
space: this.props.space,
event: payload.event,
},
state: { spaceId: this.props.space.roomId, member: payload.member },
});
} else {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
RightPanelStore.instance.setCard({
phase: RightPanelPhases.SpaceMemberList,
refireParams: { space: this.props.space },
state: { spaceId: this.props.space.roomId },
});
}
};

View file

@ -20,7 +20,7 @@ import { Thread, ThreadEvent } from 'matrix-js-sdk/src/models/thread';
import { RelationType } from 'matrix-js-sdk/src/@types/event';
import BaseCard from "../views/right_panel/BaseCard";
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
import { replaceableComponent } from "../../utils/replaceableComponent";
import ResizeNotifier from '../../utils/ResizeNotifier';
import { TileShape } from '../views/rooms/EventTile';
@ -30,7 +30,6 @@ import { Layout } from '../../settings/enums/Layout';
import TimelinePanel from './TimelinePanel';
import dis from "../../dispatcher/dispatcher";
import { ActionPayload } from '../../dispatcher/payloads';
import { SetRightPanelPhasePayload } from '../../dispatcher/payloads/SetRightPanelPhasePayload';
import { Action } from '../../dispatcher/actions';
import { MatrixClientPeg } from '../../MatrixClientPeg';
import { E2EStatus } from '../../utils/ShieldUtils';
@ -40,7 +39,7 @@ import ContentMessages from '../../ContentMessages';
import UploadBar from './UploadBar';
import { _t } from '../../languageHandler';
import ThreadListContextMenu from '../views/context_menus/ThreadListContextMenu';
import RightPanelStore from '../../stores/RightPanelStore';
import RightPanelStore from '../../stores/right-panel/RightPanelStore';
import SettingsStore from '../../settings/SettingsStore';
import { WidgetLayoutStore } from '../../stores/widgets/WidgetLayoutStore';
@ -52,7 +51,7 @@ interface IProps {
permalinkCreator?: RoomPermalinkCreator;
e2eStatus?: E2EStatus;
initialEvent?: MatrixEvent;
initialEventHighlighted?: boolean;
isInitialEventHighlighted?: boolean;
}
interface IState {
thread?: Thread;
@ -94,10 +93,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
}
if (prevProps.room !== this.props.room) {
dis.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.RoomSummary,
});
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary });
}
}
@ -168,7 +164,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
};
private onScroll = (): void => {
if (this.props.initialEvent && this.props.initialEventHighlighted) {
if (this.props.initialEvent && this.props.isInitialEventHighlighted) {
dis.dispatch({
action: Action.ViewRoom,
room_id: this.props.room.roomId,
@ -189,7 +185,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
};
public render(): JSX.Element {
const highlightedEventId = this.props.initialEventHighlighted
const highlightedEventId = this.props.isInitialEventHighlighted
? this.props.initialEvent?.getId()
: null;
@ -198,7 +194,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
event_id: this.state.thread?.id,
};
let previousPhase = RightPanelStore.getSharedInstance().previousPhase;
let previousPhase = RightPanelStore.instance.previousCard.phase;
if (!SettingsStore.getValue("feature_maximised_widgets")) {
previousPhase = RightPanelPhases.ThreadPanel;
}

View file

@ -680,6 +680,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
};
private onSync = (clientSyncState: SyncState, prevState: SyncState, data: object): void => {
if (this.unmounted) return;
this.setState({ clientSyncState });
};

View file

@ -29,6 +29,7 @@ import MainSplit from "./MainSplit";
import RightPanel from "./RightPanel";
import Spinner from "../views/elements/Spinner";
import ResizeNotifier from "../../utils/ResizeNotifier";
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
interface IProps {
userId?: string;
@ -88,7 +89,10 @@ export default class UserView extends React.Component<IProps, IState> {
if (this.state.loading) {
return <Spinner />;
} else if (this.state.member) {
const panel = <RightPanel member={this.state.member} resizeNotifier={this.props.resizeNotifier} />;
const panel = <RightPanel
overwriteCard={{ phase: RightPanelPhases.RoomMemberInfo, state: { member: this.state.member } }}
resizeNotifier={this.props.resizeNotifier}
/>;
return (<MainSplit panel={panel} resizeNotifier={this.props.resizeNotifier}>
<HomePage />
</MainSplit>);

View file

@ -38,12 +38,10 @@ import Modal from "../../../Modal";
import ExportDialog from "../dialogs/ExportDialog";
import { onRoomFilesClick, onRoomMembersClick } from "../right_panel/RoomSummaryCard";
import RoomViewStore from "../../../stores/RoomViewStore";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { ROOM_NOTIFICATIONS_TAB } from "../dialogs/RoomSettingsDialog";
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import DMRoomMap from "../../../utils/DMRoomMap";
interface IProps extends IContextMenuProps {
@ -272,11 +270,7 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
ev.stopPropagation();
ensureViewingRoom();
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.RoomSummary,
allowClose: false,
});
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary }, false);
onFinished();
}}
label={_t("Widgets")}

View file

@ -26,19 +26,13 @@ import { formatCommaSeparatedList } from '../../../utils/FormattingUtils';
import { isValid3pidInvite } from "../../../RoomInvite";
import EventListSummary from "./EventListSummary";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import defaultDispatcher from '../../../dispatcher/dispatcher';
import { RightPanelPhases } from '../../../stores/RightPanelStorePhases';
import { Action } from '../../../dispatcher/actions';
import { SetRightPanelPhasePayload } from '../../../dispatcher/payloads/SetRightPanelPhasePayload';
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { jsxJoin } from '../../../utils/ReactUtils';
import { Layout } from '../../../settings/enums/Layout';
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
const onPinnedMessagesClick = (): void => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.PinnedMessages,
allowClose: false,
});
RightPanelStore.instance.setCard({ phase: RightPanelPhases.PinnedMessages }, false);
};
const SENDER_AS_DISPLAY_NAME_EVENTS = [EventType.RoomServerAcl, EventType.RoomPinnedEvents];

View file

@ -20,14 +20,13 @@ import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import dis from '../../../dispatcher/dispatcher';
import GroupStore from '../../../stores/GroupStore';
import { showGroupInviteDialog } from '../../../GroupAddressPicker';
import AccessibleButton from '../elements/AccessibleButton';
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import { Action } from "../../../dispatcher/actions";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
const INITIAL_LOAD_NUM_MEMBERS = 30;
@ -170,10 +169,9 @@ export default class GroupMemberList extends React.Component {
onInviteToGroupButtonClick = () => {
showGroupInviteDialog(this.props.groupId).then(() => {
dis.dispatch({
action: Action.SetRightPanelPhase,
RightPanelStore.instance.setCard({
phase: RightPanelPhases.GroupMemberList,
refireParams: { groupId: this.props.groupId },
state: { groupId: this.props.groupId },
});
});
};

View file

@ -22,12 +22,11 @@ import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import { getNameForEventRoom, userLabelForEventRoom }
from '../../../utils/KeyVerificationStateObserver';
import dis from "../../../dispatcher/dispatcher";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import EventTileBubble from "./EventTileBubble";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import AccessibleButton from '../elements/AccessibleButton';
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
interface IProps {
mxEvent: MatrixEvent;
@ -52,10 +51,9 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
private openRequest = () => {
const { verificationRequest } = this.props.mxEvent;
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
dis.dispatch({
action: Action.SetRightPanelPhase,
RightPanelStore.instance.setCard({
phase: RightPanelPhases.EncryptionPanel,
refireParams: { verificationRequest, member },
state: { verificationRequest, member },
});
};

View file

@ -20,10 +20,8 @@ import classNames from 'classnames';
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
interface IProps {
header?: ReactNode;
@ -34,7 +32,7 @@ interface IProps {
previousPhaseLabel?: string;
closeLabel?: string;
onClose?(): void;
refireParams?;
cardState?;
}
interface IGroupProps {
@ -59,16 +57,15 @@ const BaseCard: React.FC<IProps> = ({
previousPhase,
previousPhaseLabel,
children,
refireParams,
cardState,
}) => {
let backButton;
if (previousPhase) {
const onBackClick = () => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: previousPhase,
refireParams: refireParams,
});
// TODO RightPanelStore (will be addressed in a follow up PR): this should ideally be:
// RightPanelStore.instance.popRightPanel();
RightPanelStore.instance.setCard({ phase: previousPhase, state: cardState });
};
const label = previousPhaseLabel ?? _t("Back");
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={label} />;

View file

@ -31,9 +31,8 @@ import { useEventEmitter } from "../../../hooks/useEventEmitter";
import Modal from "../../../Modal";
import * as sdk from "../../../index";
import { _t } from "../../../languageHandler";
import dis from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
// cancellation codes which constitute a key mismatch
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
@ -117,10 +116,9 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
setRequest(verificationRequest_);
setPhase(verificationRequest_.phase);
// Notify the RightPanelStore about this
dis.dispatch({
action: Action.SetRightPanelPhase,
RightPanelStore.instance.setCard({
phase: RightPanelPhases.EncryptionPanel,
refireParams: { member, verificationRequest: verificationRequest_ },
state: { member, verificationRequest: verificationRequest_ },
});
}, [member]);

View file

@ -23,7 +23,7 @@ import React from 'react';
import { _t } from '../../../languageHandler';
import HeaderButton from './HeaderButton';
import HeaderButtons, { HeaderKind } from './HeaderButtons';
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { Action } from "../../../dispatcher/actions";
import { ActionPayload } from "../../../dispatcher/payloads";
import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload";

View file

@ -21,15 +21,11 @@ limitations under the License.
import React from 'react';
import dis from '../../../dispatcher/dispatcher';
import RightPanelStore from "../../../stores/RightPanelStore";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { Action } from '../../../dispatcher/actions';
import {
SetRightPanelPhasePayload,
SetRightPanelPhaseRefireParams,
} from '../../../dispatcher/payloads/SetRightPanelPhasePayload';
import type { EventSubscription } from "fbemitter";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { IRightPanelCardState } from '../../../stores/right-panel/RightPanelStoreIPanelState';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
import { NotificationColor } from '../../../stores/notifications/NotificationColor';
export enum HeaderKind {
@ -47,38 +43,35 @@ interface IProps {}
@replaceableComponent("views.right_panel.HeaderButtons")
export default abstract class HeaderButtons<P = {}> extends React.Component<IProps & P, IState> {
private storeToken: EventSubscription;
private unmounted = false;
private dispatcherRef: string;
constructor(props: IProps & P, kind: HeaderKind) {
super(props);
const rps = RightPanelStore.getSharedInstance();
const rps = RightPanelStore.instance;
this.state = {
headerKind: kind,
phase: rps.currentCard.phase,
threadNotificationColor: NotificationColor.None,
phase: kind === HeaderKind.Room ? rps.visibleRoomPanelPhase : rps.visibleGroupPanelPhase,
};
}
public componentDidMount() {
this.storeToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelUpdate.bind(this));
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
this.dispatcherRef = dis.register(this.onAction.bind(this)); // used by subclasses
}
public componentWillUnmount() {
if (this.storeToken) this.storeToken.remove();
this.unmounted = true;
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
}
protected abstract onAction(payload);
public setPhase(phase: RightPanelPhases, extras?: Partial<SetRightPanelPhaseRefireParams>) {
dis.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: phase,
refireParams: extras,
});
public setPhase(phase: RightPanelPhases, cardState?: Partial<IRightPanelCardState>) {
RightPanelStore.instance.setCard({ phase, state: cardState });
}
public isPhase(phases: string | string[]) {
@ -89,14 +82,12 @@ export default abstract class HeaderButtons<P = {}> extends React.Component<IPro
}
}
private onRightPanelUpdate() {
const rps = RightPanelStore.getSharedInstance();
if (this.state.headerKind === HeaderKind.Room) {
this.setState({ phase: rps.visibleRoomPanelPhase });
} else if (this.state.headerKind === HeaderKind.Group) {
this.setState({ phase: rps.visibleGroupPanelPhase });
}
}
private onRightPanelStoreUpdate = () => {
if (this.unmounted) return;
let phase = RightPanelStore.instance.currentCard.phase;
if (!RightPanelStore.instance.isOpenForGroup) {phase = null;}
this.setState({ phase });
};
// XXX: Make renderButtons a prop
public abstract renderButtons(): JSX.Element;

View file

@ -25,16 +25,15 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { _t } from '../../../languageHandler';
import HeaderButton from './HeaderButton';
import HeaderButtons, { HeaderKind } from './HeaderButtons';
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { Action } from "../../../dispatcher/actions";
import { ActionPayload } from "../../../dispatcher/payloads";
import RightPanelStore from "../../../stores/RightPanelStore";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { useSettingValue } from "../../../hooks/useSettings";
import { useReadPinnedEvents, usePinnedEvents } from './PinnedMessagesCard';
import { dispatchShowThreadsPanelEvent } from "../../../dispatcher/dispatch-actions/threads";
import SettingsStore from "../../../settings/SettingsStore";
import dis from "../../../dispatcher/dispatcher";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
import { ThreadsRoomNotificationState } from "../../../stores/notifications/ThreadsRoomNotificationState";
@ -161,7 +160,7 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
}
} else if (payload.action === "view_3pid_invite") {
if (payload.event) {
this.setPhase(RightPanelPhases.Room3pidMemberInfo, { event: payload.event });
this.setPhase(RightPanelPhases.Room3pidMemberInfo, { memberInfoEvent: payload.event });
} else {
this.setPhase(RightPanelPhases.RoomMemberList);
}
@ -170,12 +169,12 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
private onRoomSummaryClicked = () => {
// use roomPanelPhase rather than this.state.phase as it remembers the latest one if we close
const lastPhase = RightPanelStore.getSharedInstance().roomPanelPhase;
if (ROOM_INFO_PHASES.includes(lastPhase)) {
if (this.state.phase === lastPhase) {
this.setPhase(lastPhase);
const currentPhase = RightPanelStore.instance.currentCard.phase;
if (ROOM_INFO_PHASES.includes(currentPhase)) {
if (this.state.phase === currentPhase) {
this.setPhase(currentPhase);
} else {
this.setPhase(lastPhase, RightPanelStore.getSharedInstance().roomPanelPhaseParams);
this.setPhase(currentPhase, RightPanelStore.instance.currentCard.state);
}
} else {
// This toggles for us, if needed
@ -198,10 +197,7 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
private onThreadsPanelClicked = () => {
if (RoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) {
dis.dispatch({
action: Action.ToggleRightPanel,
type: "room",
});
RightPanelStore.instance.togglePanel();
} else {
dispatchShowThreadsPanelEvent();
}
@ -227,6 +223,7 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
rightPanelPhaseButtons.set(RightPanelPhases.ThreadPanel,
SettingsStore.getValue("feature_thread")
? <HeaderButton
key={RightPanelPhases.ThreadPanel}
name="threadsButton"
title={_t("Threads")}
onClick={this.onThreadsPanelClicked}

View file

@ -25,9 +25,7 @@ import { _t } from '../../../languageHandler';
import RoomAvatar from "../avatars/RoomAvatar";
import AccessibleButton from "../elements/AccessibleButton";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import Modal from "../../../Modal";
import ShareDialog from '../dialogs/ShareDialog';
import { useEventEmitter } from "../../../hooks/useEventEmitter";
@ -48,6 +46,7 @@ import { Container, MAX_PINNED, WidgetLayoutStore } from "../../../stores/widget
import RoomName from "../elements/RoomName";
import UIStore from "../../../stores/UIStore";
import ExportDialog from "../dialogs/ExportDialog";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
interface IProps {
room: Room;
@ -103,12 +102,10 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
}, [room.roomId]);
const onOpenWidgetClick = () => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
// TODO RightPanelStore (will be addressed in a follow up PR): should push the widget
RightPanelStore.instance.setCard({
phase: RightPanelPhases.Widget,
refireParams: {
widgetId: app.id,
},
state: { widgetId: app.id },
});
};
@ -237,19 +234,13 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
};
export const onRoomMembersClick = (allowClose = true) => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.RoomMemberList,
allowClose,
});
// TODO RightPanelStore (will be addressed in a follow up PR): should push the phase
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomMemberList }, allowClose);
};
export const onRoomFilesClick = (allowClose = true) => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.FilePanel,
allowClose,
});
// TODO RightPanelStore (will be addressed in a follow up PR): should push the phase
RightPanelStore.instance.setCard({ phase: RightPanelPhases.FilePanel }, allowClose);
};
const onRoomSettingsClick = () => {

View file

@ -55,7 +55,7 @@ interface IState {
editState?: EditorStateTransfer;
replyToEvent?: MatrixEvent;
initialEventId?: string;
initialEventHighlighted?: boolean;
isInitialEventHighlighted?: boolean;
// settings:
showReadReceipts?: boolean;
@ -103,7 +103,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
// roomLoadError: RoomViewStore.getRoomLoadError(),
initialEventId: RoomViewStore.getInitialEventId(),
initialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
replyToEvent: RoomViewStore.getQuotingEvent(),
};
@ -127,7 +127,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
};
private onScroll = (): void => {
if (this.state.initialEventId && this.state.initialEventHighlighted) {
if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
dis.dispatch({
action: Action.ViewRoom,
room_id: this.props.room.roomId,
@ -145,7 +145,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
};
public render(): JSX.Element {
const highlightedEventId = this.state.initialEventHighlighted
const highlightedEventId = this.state.isInitialEventHighlighted
? this.state.initialEventId
: null;

View file

@ -44,7 +44,7 @@ import E2EIcon from "../rooms/E2EIcon";
import { useEventEmitter } from "../../../hooks/useEventEmitter";
import { textualPowerLevel } from '../../../Roles';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import EncryptionPanel from "./EncryptionPanel";
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
import { legacyVerifyUser, verifyDevice, verifyUser } from '../../../verification';
@ -63,7 +63,6 @@ import ErrorDialog from "../dialogs/ErrorDialog";
import QuestionDialog from "../dialogs/QuestionDialog";
import ConfirmUserActionDialog from "../dialogs/ConfirmUserActionDialog";
import InfoDialog from "../dialogs/InfoDialog";
import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
import RoomAvatar from "../avatars/RoomAvatar";
import RoomName from "../elements/RoomName";
import { mediaFromMxc } from "../../../customisations/Media";
@ -75,6 +74,8 @@ import { bulkSpaceBehaviour } from "../../../utils/space";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../settings/UIFeature";
import { TimelineRenderingType } from "../../../contexts/RoomContext";
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { IRightPanelCardState } from '../../../stores/right-panel/RightPanelStoreIPanelState';
import { useUserStatusMessage } from "../../../hooks/useUserStatusMessage";
export interface IDevice {
@ -1649,25 +1650,22 @@ const UserInfo: React.FC<IProps> = ({
const classes = ["mx_UserInfo"];
let refireParams;
let cardState: IRightPanelCardState;
let previousPhase: RightPanelPhases;
// We have no previousPhase for when viewing a UserInfo from a Group or without a Room at this time
if (room && phase === RightPanelPhases.EncryptionPanel) {
previousPhase = RightPanelPhases.RoomMemberInfo;
refireParams = { member };
cardState = { member };
} else if (room?.isSpaceRoom() && SpaceStore.spacesEnabled) {
previousPhase = previousPhase = RightPanelPhases.SpaceMemberList;
refireParams = { space: room };
previousPhase = RightPanelPhases.SpaceMemberList;
cardState = { spaceId: room.roomId };
} else if (room) {
previousPhase = RightPanelPhases.RoomMemberList;
}
const onEncryptionPanelClose = () => {
dis.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: previousPhase,
refireParams: refireParams,
});
// TODO RightPanelStore (will be addressed in a follow up PR): here we want to pop the panel
RightPanelStore.instance.setCard({ phase: previousPhase, state: cardState });
};
let content;
@ -1723,7 +1721,7 @@ const UserInfo: React.FC<IProps> = ({
onClose={onClose}
closeLabel={closeLabel}
previousPhase={previousPhase}
refireParams={refireParams}
cardState={cardState}
>
{ content }
</BaseCard>;

View file

@ -23,14 +23,12 @@ import WidgetUtils from "../../../utils/WidgetUtils";
import AppTile from "../elements/AppTile";
import { _t } from "../../../languageHandler";
import { useWidgets } from "./RoomSummaryCard";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { ChevronFace, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
import WidgetContextMenu from "../context_menus/WidgetContextMenu";
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import UIStore from "../../../stores/UIStore";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
interface IProps {
room: Room;
@ -50,10 +48,9 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
useEffect(() => {
if (!app || isPinned) {
// stop showing this card
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.RoomSummary,
});
//TODO RightPanelStore (will be addressed in a follow up PR): here we want to just pop the widget card.
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary });
}
}, [app, isPinned]);

View file

@ -56,6 +56,7 @@ interface IState {
@replaceableComponent("views.rooms.AppsDrawer")
export default class AppsDrawer extends React.Component<IProps, IState> {
private unmounted = false;
private resizeContainer: HTMLDivElement;
private resizer: Resizer;
private dispatcherRef: string;
@ -85,6 +86,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
}
public componentWillUnmount(): void {
this.unmounted = true;
ScalarMessaging.stopListening();
WidgetLayoutStore.instance.off(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
@ -213,6 +215,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
private centerApps = (): IApp[] => this.state.apps[Container.Center];
private updateApps = (): void => {
if (this.unmounted) return;
this.setState({
apps: this.getApps(),
});

View file

@ -33,7 +33,7 @@ import { isValid3pidInvite } from "../../../RoomInvite";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
import BaseCard from "../right_panel/BaseCard";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import RoomAvatar from "../avatars/RoomAvatar";
import RoomName from "../elements/RoomName";
import { replaceableComponent } from "../../../utils/replaceableComponent";

View file

@ -38,7 +38,7 @@ import { ContextMenuTooltipButton } from '../../structures/ContextMenu';
import RoomContextMenu from "../context_menus/RoomContextMenu";
import { contextMenuBelow } from './RoomTile';
import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNotificationStateStore';
import { RightPanelPhases } from '../../../stores/RightPanelStorePhases';
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { NotificationStateEvents } from '../../../stores/notifications/NotificationState';
export interface ISearchInfo {

View file

@ -36,7 +36,7 @@ import { ButtonEvent } from "../elements/AccessibleButton";
import Modal from "../../../Modal";
import EditCommunityPrototypeDialog from "../dialogs/EditCommunityPrototypeDialog";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
import ErrorDialog from "../dialogs/ErrorDialog";
import { showCommunityInviteDialog } from "../../../RoomInvite";
import { useDispatcher } from "../../../hooks/useDispatcher";
@ -50,6 +50,7 @@ import {
UPDATE_HOME_BEHAVIOUR,
UPDATE_SELECTED_SPACE,
} from "../../../stores/spaces";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import TooltipTarget from "../elements/TooltipTarget";
const contextMenuBelow = (elementRect: DOMRect) => {
@ -99,7 +100,7 @@ const PrototypeCommunityContextMenu = (props: ComponentProps<typeof SpaceContext
action: 'view_room',
room_id: chat.roomId,
}, true);
dis.dispatch({ action: Action.SetRightPanelPhase, phase: RightPanelPhases.RoomMemberList });
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomMemberList }, undefined, chat.roomId);
} else {
// "This should never happen" clauses go here for the prototype.
Modal.createTrackedDialog('Failed to find general chat', '', ErrorDialog, {

View file

@ -21,8 +21,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import { userLabelForEventRoom } from "../../../utils/KeyVerificationStateObserver";
import dis from "../../../dispatcher/dispatcher";
import ToastStore from "../../../stores/ToastStore";
@ -31,6 +30,7 @@ import GenericToast from "./GenericToast";
import { Action } from "../../../dispatcher/actions";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import VerificationRequestDialog from "../dialogs/VerificationRequestDialog";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
interface IProps {
toastKey: string;
@ -115,14 +115,14 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
room_id: request.channel.roomId,
should_peek: false,
});
dis.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.EncryptionPanel,
refireParams: {
verificationRequest: request,
member: cli.getUser(request.otherUserId),
RightPanelStore.instance.setCard(
{
phase: RightPanelPhases.EncryptionPanel,
state: { verificationRequest: request, member: cli.getUser(request.otherUserId) },
},
});
undefined,
request.channel.roomId,
);
} else {
Modal.createTrackedDialog('Incoming Verification', '', VerificationRequestDialog, {
verificationRequest: request,