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

Commit

Permalink
History based navigation with new right panel store (#7398)
Browse files Browse the repository at this point in the history
Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
  • Loading branch information
toger5 and jryans authored Jan 5, 2022
1 parent 6f89267 commit 4ab3470
Show file tree
Hide file tree
Showing 25 changed files with 251 additions and 255 deletions.
5 changes: 0 additions & 5 deletions src/components/structures/FilePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from '../../MatrixClientPeg';
import EventIndexPeg from "../../indexing/EventIndexPeg";
import { _t } from '../../languageHandler';
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";
Expand Down Expand Up @@ -221,7 +220,6 @@ class FilePanel extends React.Component<IProps, IState> {
return <BaseCard
className="mx_FilePanel mx_RoomView_messageListWrapper"
onClose={this.props.onClose}
previousPhase={RightPanelPhases.RoomSummary}
>
<div className="mx_RoomView_empty">
{ _t("You must <a>register</a> to use this functionality",
Expand All @@ -234,7 +232,6 @@ class FilePanel extends React.Component<IProps, IState> {
return <BaseCard
className="mx_FilePanel mx_RoomView_messageListWrapper"
onClose={this.props.onClose}
previousPhase={RightPanelPhases.RoomSummary}
>
<div className="mx_RoomView_empty">{ _t("You must join the room to see its files") }</div>
</BaseCard>;
Expand All @@ -258,7 +255,6 @@ class FilePanel extends React.Component<IProps, IState> {
<BaseCard
className="mx_FilePanel"
onClose={this.props.onClose}
previousPhase={RightPanelPhases.RoomSummary}
withoutScrollContainer
>
<DesktopBuildsNotice isRoomEncrypted={isRoomEncrypted} kind={WarningKind.Files} />
Expand All @@ -285,7 +281,6 @@ class FilePanel extends React.Component<IProps, IState> {
<BaseCard
className="mx_FilePanel"
onClose={this.props.onClose}
previousPhase={RightPanelPhases.RoomSummary}
>
<Spinner />
</BaseCard>
Expand Down
15 changes: 0 additions & 15 deletions src/components/structures/RightPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ import GroupStore from '../../stores/GroupStore';
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";
import WidgetCard from "../views/right_panel/WidgetCard";
import { replaceableComponent } from "../../utils/replaceableComponent";
import SettingsStore from "../../settings/SettingsStore";
import { ActionPayload } from "../../dispatcher/payloads";
import MemberList from "../views/rooms/MemberList";
import GroupMemberList from "../views/groups/GroupMemberList";
import GroupRoomList from "../views/groups/GroupRoomList";
Expand All @@ -47,7 +45,6 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard";
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';
Expand All @@ -72,8 +69,6 @@ interface IState {
export default class RightPanel extends React.Component<IProps, IState> {
static contextType = MatrixClientContext;

private dispatcherRef: string;

constructor(props, context) {
super(props, context);

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

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);
}

public componentWillUnmount(): void {
dis.unregister(this.dispatcherRef);
if (this.context) {
this.context.removeListener("RoomState.members", this.onRoomStateMember);
}
Expand Down Expand Up @@ -153,14 +146,6 @@ export default class RightPanel extends React.Component<IProps, IState> {
});
};

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();
}
};

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
Expand Down
25 changes: 16 additions & 9 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ import MessageComposer from '../views/rooms/MessageComposer';
import JumpToBottomButton from "../views/rooms/JumpToBottomButton";
import TopUnreadMessagesBar from "../views/rooms/TopUnreadMessagesBar";
import SpaceStore from "../../stores/spaces/SpaceStore";
import { dispatchShowThreadEvent } from '../../dispatcher/dispatch-actions/threads';
import { showThread } from '../../dispatcher/dispatch-actions/threads';
import { fetchInitialEvent } from "../../utils/EventUtils";
import { ComposerType } from "../../dispatcher/payloads/ComposerInsertPayload";
import AppsDrawer from '../views/rooms/AppsDrawer';
Expand Down Expand Up @@ -338,6 +338,13 @@ 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
RightPanelStore.instance.setCard({ phase: RightPanelPhases.Timeline });
} else if (
RightPanelStore.instance.isOpenForRoom &&
RightPanelStore.instance.roomPhaseHistory.some(card => (card.phase === RightPanelPhases.Timeline))
) {
// hide chat in right panel when the widget is minimized
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary });
RightPanelStore.instance.togglePanel();
}
this.checkWidgets(this.state.room);
};
Expand Down Expand Up @@ -424,21 +431,21 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {

const thread = initialEvent?.getThread();
if (thread && !initialEvent?.isThreadRoot) {
dispatchShowThreadEvent(
thread.rootEvent,
showThread({
rootEvent: thread.rootEvent,
initialEvent,
RoomViewStore.isInitialEventHighlighted(),
);
highlighted: RoomViewStore.isInitialEventHighlighted(),
});
} else {
newState.initialEventId = initialEventId;
newState.isInitialEventHighlighted = RoomViewStore.isInitialEventHighlighted();

if (thread && initialEvent?.isThreadRoot) {
dispatchShowThreadEvent(
thread.rootEvent,
showThread({
rootEvent: thread.rootEvent,
initialEvent,
RoomViewStore.isInitialEventHighlighted(),
);
highlighted: RoomViewStore.isInitialEventHighlighted(),
});
}
}
}
Expand Down
22 changes: 0 additions & 22 deletions src/components/structures/ThreadView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ import UploadBar from './UploadBar';
import { _t } from '../../languageHandler';
import ThreadListContextMenu from '../views/context_menus/ThreadListContextMenu';
import RightPanelStore from '../../stores/right-panel/RightPanelStore';
import SettingsStore from '../../settings/SettingsStore';
import { WidgetLayoutStore } from '../../stores/widgets/WidgetLayoutStore';

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

let previousPhase = RightPanelStore.instance.previousCard.phase;
if (!SettingsStore.getValue("feature_maximised_widgets")) {
previousPhase = RightPanelPhases.ThreadPanel;
}

// change the previous phase to the threadPanel in case there is no maximised widget anymore
if (!WidgetLayoutStore.instance.hasMaximisedWidget(this.props.room)) {
previousPhase = RightPanelPhases.ThreadPanel;
}

// Make sure the previous Phase is always one of the two: Timeline or ThreadPanel
if (![RightPanelPhases.ThreadPanel, RightPanelPhases.Timeline].includes(previousPhase)) {
previousPhase = RightPanelPhases.ThreadPanel;
}
const previousPhaseLabels = {};
previousPhaseLabels[RightPanelPhases.ThreadPanel] = _t("All threads");
previousPhaseLabels[RightPanelPhases.Timeline] = _t("Chat");

return (
<RoomContext.Provider value={{
...this.context,
Expand All @@ -222,8 +202,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
<BaseCard
className="mx_ThreadView mx_ThreadPanel"
onClose={this.props.onClose}
previousPhase={previousPhase}
previousPhaseLabel={previousPhaseLabels[previousPhase]}
withoutScrollContainer={true}
header={this.renderThreadViewHeader()}
>
Expand Down
8 changes: 7 additions & 1 deletion src/components/views/avatars/MemberAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Action } from "../../../dispatcher/actions";
import BaseAvatar from "./BaseAvatar";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromMxc } from "../../../customisations/Media";
import { CardContext } from '../right_panel/BaseCard';

interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" | "idName" | "url"> {
member: RoomMember;
Expand All @@ -36,6 +37,7 @@ interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" |
onClick?: React.MouseEventHandler;
// Whether the onClick of the avatar should be overridden to dispatch `Action.ViewUser`
viewUserOnClick?: boolean;
pushUserOnClick?: boolean;
title?: string;
style?: any;
}
Expand Down Expand Up @@ -99,6 +101,7 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
dis.dispatch({
action: Action.ViewUser,
member: this.props.member,
push: this.context.isCard,
});
};
}
Expand All @@ -109,7 +112,10 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
title={this.state.title}
idName={userId}
url={this.state.imageUrl}
onClick={onClick} />
onClick={onClick}
/>
);
}
}

MemberAvatar.contextType = CardContext;
9 changes: 5 additions & 4 deletions src/components/views/messages/MKeyVerificationRequest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
private openRequest = () => {
const { verificationRequest } = this.props.mxEvent;
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
RightPanelStore.instance.setCard({
phase: RightPanelPhases.EncryptionPanel,
state: { verificationRequest, member },
});
RightPanelStore.instance.setCards([
{ phase: RightPanelPhases.RoomSummary },
{ phase: RightPanelPhases.RoomMemberInfo, state: { member } },
{ phase: RightPanelPhases.EncryptionPanel, state: { verificationRequest, member } },
]);
};

private onRequestChanged = () => {
Expand Down
32 changes: 17 additions & 15 deletions src/components/views/messages/MessageActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ import DownloadActionButton from "./DownloadActionButton";
import SettingsStore from '../../../settings/SettingsStore';
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
import ReplyChain from '../elements/ReplyChain';
import { dispatchShowThreadEvent } from '../../../dispatcher/dispatch-actions/threads';
import { showThread } from '../../../dispatcher/dispatch-actions/threads';
import ReactionPicker from "../emojipicker/ReactionPicker";
import { CardContext } from '../right_panel/BaseCard';

interface IOptionsButtonProps {
mxEvent: MatrixEvent;
Expand Down Expand Up @@ -219,8 +220,8 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
});
};

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

const threadTooltipButton = <CardContext.Consumer>
{ context =>
<RovingAccessibleTooltipButton
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
title={_t("Reply in thread")}
onClick={this.onThreadClick.bind(null, context.isCard)}
key="thread"
/>
}
</CardContext.Consumer>;

// We show a different toolbar for failed events, so detect that first.
const mxEvent = this.props.mxEvent;
const editStatus = mxEvent.replacingEvent() && mxEvent.replacingEvent().status;
Expand Down Expand Up @@ -335,12 +347,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
key="reply"
/>
{ (this.showReplyInThreadAction) && (
<RovingAccessibleTooltipButton
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
title={_t("Reply in thread")}
onClick={this.onThreadClick}
key="thread"
/>
threadTooltipButton
) }
</>);
}
Expand Down Expand Up @@ -368,12 +375,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
this.props.mxEvent.getThread() &&
!isContentActionable(this.props.mxEvent)
) {
toolbarOpts.unshift(<RovingAccessibleTooltipButton
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
title={_t("Reply in thread")}
onClick={this.onThreadClick}
key="thread"
/>);
toolbarOpts.unshift(threadTooltipButton);
}

if (allowCancel) {
Expand Down
37 changes: 17 additions & 20 deletions src/components/views/right_panel/BaseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,15 @@ import classNames from 'classnames';
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { backLabelForPhase } from '../../../stores/right-panel/RightPanelStorePhases';

export const CardContext = React.createContext({ isCard: false });
interface IProps {
header?: ReactNode;
footer?: ReactNode;
className?: string;
withoutScrollContainer?: boolean;
previousPhase?: RightPanelPhases;
previousPhaseLabel?: string;
closeLabel?: string;
onClose?(): void;
cardState?;
Expand All @@ -54,20 +53,16 @@ const BaseCard: React.FC<IProps> = ({
header,
footer,
withoutScrollContainer,
previousPhase,
previousPhaseLabel,
children,
cardState,
}) => {
let backButton;
if (previousPhase) {
const cardHistory = RightPanelStore.instance.roomPhaseHistory;
if (cardHistory.length > 1) {
const prevCard = cardHistory[cardHistory.length - 2];
const onBackClick = () => {
// TODO RightPanelStore (will be addressed in a follow up PR): this should ideally be:
// RightPanelStore.instance.popRightPanel();

RightPanelStore.instance.setCard({ phase: previousPhase, state: cardState });
RightPanelStore.instance.popCard();
};
const label = previousPhaseLabel ?? _t("Back");
const label = backLabelForPhase(prevCard.phase) ?? _t("Back");
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={label} />;
}

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

return (
<div className={classNames("mx_BaseCard", className)}>
<div className="mx_BaseCard_header">
{ backButton }
{ closeButton }
{ header }
<CardContext.Provider value={{ isCard: true }}>
<div className={classNames("mx_BaseCard", className)}>
<div className="mx_BaseCard_header">
{ backButton }
{ closeButton }
{ header }
</div>
{ children }
{ footer && <div className="mx_BaseCard_footer">{ footer }</div> }
</div>
{ children }
{ footer && <div className="mx_BaseCard_footer">{ footer }</div> }
</div>
</CardContext.Provider>
);
};

Expand Down
Loading

0 comments on commit 4ab3470

Please sign in to comment.