Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 4ab3470

Browse files
toger5jryans
andauthored
History based navigation with new right panel store (#7398)
Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
1 parent 6f89267 commit 4ab3470

25 files changed

+251
-255
lines changed

src/components/structures/FilePanel.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import { logger } from "matrix-js-sdk/src/logger";
2727
import { MatrixClientPeg } from '../../MatrixClientPeg';
2828
import EventIndexPeg from "../../indexing/EventIndexPeg";
2929
import { _t } from '../../languageHandler';
30-
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
3130
import DesktopBuildsNotice, { WarningKind } from "../views/elements/DesktopBuildsNotice";
3231
import BaseCard from "../views/right_panel/BaseCard";
3332
import { replaceableComponent } from "../../utils/replaceableComponent";
@@ -221,7 +220,6 @@ class FilePanel extends React.Component<IProps, IState> {
221220
return <BaseCard
222221
className="mx_FilePanel mx_RoomView_messageListWrapper"
223222
onClose={this.props.onClose}
224-
previousPhase={RightPanelPhases.RoomSummary}
225223
>
226224
<div className="mx_RoomView_empty">
227225
{ _t("You must <a>register</a> to use this functionality",
@@ -234,7 +232,6 @@ class FilePanel extends React.Component<IProps, IState> {
234232
return <BaseCard
235233
className="mx_FilePanel mx_RoomView_messageListWrapper"
236234
onClose={this.props.onClose}
237-
previousPhase={RightPanelPhases.RoomSummary}
238235
>
239236
<div className="mx_RoomView_empty">{ _t("You must join the room to see its files") }</div>
240237
</BaseCard>;
@@ -258,7 +255,6 @@ class FilePanel extends React.Component<IProps, IState> {
258255
<BaseCard
259256
className="mx_FilePanel"
260257
onClose={this.props.onClose}
261-
previousPhase={RightPanelPhases.RoomSummary}
262258
withoutScrollContainer
263259
>
264260
<DesktopBuildsNotice isRoomEncrypted={isRoomEncrypted} kind={WarningKind.Files} />
@@ -285,7 +281,6 @@ class FilePanel extends React.Component<IProps, IState> {
285281
<BaseCard
286282
className="mx_FilePanel"
287283
onClose={this.props.onClose}
288-
previousPhase={RightPanelPhases.RoomSummary}
289284
>
290285
<Spinner />
291286
</BaseCard>

src/components/structures/RightPanel.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@ import GroupStore from '../../stores/GroupStore';
2727
import { RightPanelPhases } from '../../stores/right-panel/RightPanelStorePhases';
2828
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
2929
import MatrixClientContext from "../../contexts/MatrixClientContext";
30-
import { Action } from "../../dispatcher/actions";
3130
import RoomSummaryCard from "../views/right_panel/RoomSummaryCard";
3231
import WidgetCard from "../views/right_panel/WidgetCard";
3332
import { replaceableComponent } from "../../utils/replaceableComponent";
3433
import SettingsStore from "../../settings/SettingsStore";
35-
import { ActionPayload } from "../../dispatcher/payloads";
3634
import MemberList from "../views/rooms/MemberList";
3735
import GroupMemberList from "../views/groups/GroupMemberList";
3836
import GroupRoomList from "../views/groups/GroupRoomList";
@@ -47,7 +45,6 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
4745
import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard";
4846
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
4947
import { E2EStatus } from '../../utils/ShieldUtils';
50-
import { dispatchShowThreadsPanelEvent } from '../../dispatcher/dispatch-actions/threads';
5148
import TimelineCard from '../views/right_panel/TimelineCard';
5249
import { UPDATE_EVENT } from '../../stores/AsyncStore';
5350
import { IRightPanelCard, IRightPanelCardState } from '../../stores/right-panel/RightPanelStoreIPanelState';
@@ -72,8 +69,6 @@ interface IState {
7269
export default class RightPanel extends React.Component<IProps, IState> {
7370
static contextType = MatrixClientContext;
7471

75-
private dispatcherRef: string;
76-
7772
constructor(props, context) {
7873
super(props, context);
7974

@@ -90,15 +85,13 @@ export default class RightPanel extends React.Component<IProps, IState> {
9085
}, 500, { leading: true, trailing: true });
9186

9287
public componentDidMount(): void {
93-
this.dispatcherRef = dis.register(this.onAction);
9488
const cli = this.context;
9589
cli.on("RoomState.members", this.onRoomStateMember);
9690
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
9791
this.initGroupStore(this.props.groupId);
9892
}
9993

10094
public componentWillUnmount(): void {
101-
dis.unregister(this.dispatcherRef);
10295
if (this.context) {
10396
this.context.removeListener("RoomState.members", this.onRoomStateMember);
10497
}
@@ -153,14 +146,6 @@ export default class RightPanel extends React.Component<IProps, IState> {
153146
});
154147
};
155148

156-
private onAction = (payload: ActionPayload) => {
157-
const isChangingRoom = payload.action === Action.ViewRoom && payload.room_id !== this.props.room.roomId;
158-
const isViewingThread = this.state.phase === RightPanelPhases.ThreadView;
159-
if (isChangingRoom && isViewingThread) {
160-
dispatchShowThreadsPanelEvent();
161-
}
162-
};
163-
164149
private onClose = () => {
165150
// XXX: There are three different ways of 'closing' this panel depending on what state
166151
// things are in... this knows far more than it should do about the state of the rest

src/components/structures/RoomView.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ import MessageComposer from '../views/rooms/MessageComposer';
9494
import JumpToBottomButton from "../views/rooms/JumpToBottomButton";
9595
import TopUnreadMessagesBar from "../views/rooms/TopUnreadMessagesBar";
9696
import SpaceStore from "../../stores/spaces/SpaceStore";
97-
import { dispatchShowThreadEvent } from '../../dispatcher/dispatch-actions/threads';
97+
import { showThread } from '../../dispatcher/dispatch-actions/threads';
9898
import { fetchInitialEvent } from "../../utils/EventUtils";
9999
import { ComposerType } from "../../dispatcher/payloads/ComposerInsertPayload";
100100
import AppsDrawer from '../views/rooms/AppsDrawer';
@@ -338,6 +338,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
338338
if (WidgetLayoutStore.instance.hasMaximisedWidget(this.state.room)) {
339339
// Show chat in right panel when a widget is maximised
340340
RightPanelStore.instance.setCard({ phase: RightPanelPhases.Timeline });
341+
} else if (
342+
RightPanelStore.instance.isOpenForRoom &&
343+
RightPanelStore.instance.roomPhaseHistory.some(card => (card.phase === RightPanelPhases.Timeline))
344+
) {
345+
// hide chat in right panel when the widget is minimized
346+
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary });
347+
RightPanelStore.instance.togglePanel();
341348
}
342349
this.checkWidgets(this.state.room);
343350
};
@@ -424,21 +431,21 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
424431

425432
const thread = initialEvent?.getThread();
426433
if (thread && !initialEvent?.isThreadRoot) {
427-
dispatchShowThreadEvent(
428-
thread.rootEvent,
434+
showThread({
435+
rootEvent: thread.rootEvent,
429436
initialEvent,
430-
RoomViewStore.isInitialEventHighlighted(),
431-
);
437+
highlighted: RoomViewStore.isInitialEventHighlighted(),
438+
});
432439
} else {
433440
newState.initialEventId = initialEventId;
434441
newState.isInitialEventHighlighted = RoomViewStore.isInitialEventHighlighted();
435442

436443
if (thread && initialEvent?.isThreadRoot) {
437-
dispatchShowThreadEvent(
438-
thread.rootEvent,
444+
showThread({
445+
rootEvent: thread.rootEvent,
439446
initialEvent,
440-
RoomViewStore.isInitialEventHighlighted(),
441-
);
447+
highlighted: RoomViewStore.isInitialEventHighlighted(),
448+
});
442449
}
443450
}
444451
}

src/components/structures/ThreadView.tsx

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ import UploadBar from './UploadBar';
4040
import { _t } from '../../languageHandler';
4141
import ThreadListContextMenu from '../views/context_menus/ThreadListContextMenu';
4242
import RightPanelStore from '../../stores/right-panel/RightPanelStore';
43-
import SettingsStore from '../../settings/SettingsStore';
44-
import { WidgetLayoutStore } from '../../stores/widgets/WidgetLayoutStore';
4543

4644
interface IProps {
4745
room: Room;
@@ -194,24 +192,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
194192
event_id: this.state.thread?.id,
195193
};
196194

197-
let previousPhase = RightPanelStore.instance.previousCard.phase;
198-
if (!SettingsStore.getValue("feature_maximised_widgets")) {
199-
previousPhase = RightPanelPhases.ThreadPanel;
200-
}
201-
202-
// change the previous phase to the threadPanel in case there is no maximised widget anymore
203-
if (!WidgetLayoutStore.instance.hasMaximisedWidget(this.props.room)) {
204-
previousPhase = RightPanelPhases.ThreadPanel;
205-
}
206-
207-
// Make sure the previous Phase is always one of the two: Timeline or ThreadPanel
208-
if (![RightPanelPhases.ThreadPanel, RightPanelPhases.Timeline].includes(previousPhase)) {
209-
previousPhase = RightPanelPhases.ThreadPanel;
210-
}
211-
const previousPhaseLabels = {};
212-
previousPhaseLabels[RightPanelPhases.ThreadPanel] = _t("All threads");
213-
previousPhaseLabels[RightPanelPhases.Timeline] = _t("Chat");
214-
215195
return (
216196
<RoomContext.Provider value={{
217197
...this.context,
@@ -222,8 +202,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
222202
<BaseCard
223203
className="mx_ThreadView mx_ThreadPanel"
224204
onClose={this.props.onClose}
225-
previousPhase={previousPhase}
226-
previousPhaseLabel={previousPhaseLabels[previousPhase]}
227205
withoutScrollContainer={true}
228206
header={this.renderThreadViewHeader()}
229207
>

src/components/views/avatars/MemberAvatar.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { Action } from "../../../dispatcher/actions";
2525
import BaseAvatar from "./BaseAvatar";
2626
import { replaceableComponent } from "../../../utils/replaceableComponent";
2727
import { mediaFromMxc } from "../../../customisations/Media";
28+
import { CardContext } from '../right_panel/BaseCard';
2829

2930
interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" | "idName" | "url"> {
3031
member: RoomMember;
@@ -36,6 +37,7 @@ interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" |
3637
onClick?: React.MouseEventHandler;
3738
// Whether the onClick of the avatar should be overridden to dispatch `Action.ViewUser`
3839
viewUserOnClick?: boolean;
40+
pushUserOnClick?: boolean;
3941
title?: string;
4042
style?: any;
4143
}
@@ -99,6 +101,7 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
99101
dis.dispatch({
100102
action: Action.ViewUser,
101103
member: this.props.member,
104+
push: this.context.isCard,
102105
});
103106
};
104107
}
@@ -109,7 +112,10 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
109112
title={this.state.title}
110113
idName={userId}
111114
url={this.state.imageUrl}
112-
onClick={onClick} />
115+
onClick={onClick}
116+
/>
113117
);
114118
}
115119
}
120+
121+
MemberAvatar.contextType = CardContext;

src/components/views/messages/MKeyVerificationRequest.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
5151
private openRequest = () => {
5252
const { verificationRequest } = this.props.mxEvent;
5353
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
54-
RightPanelStore.instance.setCard({
55-
phase: RightPanelPhases.EncryptionPanel,
56-
state: { verificationRequest, member },
57-
});
54+
RightPanelStore.instance.setCards([
55+
{ phase: RightPanelPhases.RoomSummary },
56+
{ phase: RightPanelPhases.RoomMemberInfo, state: { member } },
57+
{ phase: RightPanelPhases.EncryptionPanel, state: { verificationRequest, member } },
58+
]);
5859
};
5960

6061
private onRequestChanged = () => {

src/components/views/messages/MessageActionBar.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ import DownloadActionButton from "./DownloadActionButton";
3939
import SettingsStore from '../../../settings/SettingsStore';
4040
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
4141
import ReplyChain from '../elements/ReplyChain';
42-
import { dispatchShowThreadEvent } from '../../../dispatcher/dispatch-actions/threads';
42+
import { showThread } from '../../../dispatcher/dispatch-actions/threads';
4343
import ReactionPicker from "../emojipicker/ReactionPicker";
44+
import { CardContext } from '../right_panel/BaseCard';
4445

4546
interface IOptionsButtonProps {
4647
mxEvent: MatrixEvent;
@@ -219,8 +220,8 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
219220
});
220221
};
221222

222-
private onThreadClick = (): void => {
223-
dispatchShowThreadEvent(this.props.mxEvent);
223+
private onThreadClick = (isCard: boolean): void => {
224+
showThread({ rootEvent: this.props.mxEvent, push: isCard });
224225
dis.dispatch({
225226
action: Action.FocusSendMessageComposer,
226227
context: TimelineRenderingType.Thread,
@@ -303,6 +304,17 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
303304
key="cancel"
304305
/>;
305306

307+
const threadTooltipButton = <CardContext.Consumer>
308+
{ context =>
309+
<RovingAccessibleTooltipButton
310+
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
311+
title={_t("Reply in thread")}
312+
onClick={this.onThreadClick.bind(null, context.isCard)}
313+
key="thread"
314+
/>
315+
}
316+
</CardContext.Consumer>;
317+
306318
// We show a different toolbar for failed events, so detect that first.
307319
const mxEvent = this.props.mxEvent;
308320
const editStatus = mxEvent.replacingEvent() && mxEvent.replacingEvent().status;
@@ -335,12 +347,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
335347
key="reply"
336348
/>
337349
{ (this.showReplyInThreadAction) && (
338-
<RovingAccessibleTooltipButton
339-
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
340-
title={_t("Reply in thread")}
341-
onClick={this.onThreadClick}
342-
key="thread"
343-
/>
350+
threadTooltipButton
344351
) }
345352
</>);
346353
}
@@ -368,12 +375,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
368375
this.props.mxEvent.getThread() &&
369376
!isContentActionable(this.props.mxEvent)
370377
) {
371-
toolbarOpts.unshift(<RovingAccessibleTooltipButton
372-
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
373-
title={_t("Reply in thread")}
374-
onClick={this.onThreadClick}
375-
key="thread"
376-
/>);
378+
toolbarOpts.unshift(threadTooltipButton);
377379
}
378380

379381
if (allowCancel) {

src/components/views/right_panel/BaseCard.tsx

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,15 @@ import classNames from 'classnames';
2020
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
2121
import { _t } from "../../../languageHandler";
2222
import AccessibleButton from "../elements/AccessibleButton";
23-
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
2423
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
24+
import { backLabelForPhase } from '../../../stores/right-panel/RightPanelStorePhases';
2525

26+
export const CardContext = React.createContext({ isCard: false });
2627
interface IProps {
2728
header?: ReactNode;
2829
footer?: ReactNode;
2930
className?: string;
3031
withoutScrollContainer?: boolean;
31-
previousPhase?: RightPanelPhases;
32-
previousPhaseLabel?: string;
3332
closeLabel?: string;
3433
onClose?(): void;
3534
cardState?;
@@ -54,20 +53,16 @@ const BaseCard: React.FC<IProps> = ({
5453
header,
5554
footer,
5655
withoutScrollContainer,
57-
previousPhase,
58-
previousPhaseLabel,
5956
children,
60-
cardState,
6157
}) => {
6258
let backButton;
63-
if (previousPhase) {
59+
const cardHistory = RightPanelStore.instance.roomPhaseHistory;
60+
if (cardHistory.length > 1) {
61+
const prevCard = cardHistory[cardHistory.length - 2];
6462
const onBackClick = () => {
65-
// TODO RightPanelStore (will be addressed in a follow up PR): this should ideally be:
66-
// RightPanelStore.instance.popRightPanel();
67-
68-
RightPanelStore.instance.setCard({ phase: previousPhase, state: cardState });
63+
RightPanelStore.instance.popCard();
6964
};
70-
const label = previousPhaseLabel ?? _t("Back");
65+
const label = backLabelForPhase(prevCard.phase) ?? _t("Back");
7166
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={label} />;
7267
}
7368

@@ -87,15 +82,17 @@ const BaseCard: React.FC<IProps> = ({
8782
}
8883

8984
return (
90-
<div className={classNames("mx_BaseCard", className)}>
91-
<div className="mx_BaseCard_header">
92-
{ backButton }
93-
{ closeButton }
94-
{ header }
85+
<CardContext.Provider value={{ isCard: true }}>
86+
<div className={classNames("mx_BaseCard", className)}>
87+
<div className="mx_BaseCard_header">
88+
{ backButton }
89+
{ closeButton }
90+
{ header }
91+
</div>
92+
{ children }
93+
{ footer && <div className="mx_BaseCard_footer">{ footer }</div> }
9594
</div>
96-
{ children }
97-
{ footer && <div className="mx_BaseCard_footer">{ footer }</div> }
98-
</div>
95+
</CardContext.Provider>
9996
);
10097
};
10198

0 commit comments

Comments
 (0)