mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 03:36:07 +03:00
History based navigation with new right panel store (#7398)
Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
This commit is contained in:
parent
6f89267a31
commit
4ab3470184
25 changed files with 248 additions and 252 deletions
|
@ -27,7 +27,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { MatrixClientPeg } from '../../MatrixClientPeg';
|
import { MatrixClientPeg } from '../../MatrixClientPeg';
|
||||||
import EventIndexPeg from "../../indexing/EventIndexPeg";
|
import EventIndexPeg from "../../indexing/EventIndexPeg";
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
|
|
||||||
import DesktopBuildsNotice, { WarningKind } from "../views/elements/DesktopBuildsNotice";
|
import DesktopBuildsNotice, { WarningKind } from "../views/elements/DesktopBuildsNotice";
|
||||||
import BaseCard from "../views/right_panel/BaseCard";
|
import BaseCard from "../views/right_panel/BaseCard";
|
||||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||||
|
@ -221,7 +220,6 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
return <BaseCard
|
return <BaseCard
|
||||||
className="mx_FilePanel mx_RoomView_messageListWrapper"
|
className="mx_FilePanel mx_RoomView_messageListWrapper"
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
previousPhase={RightPanelPhases.RoomSummary}
|
|
||||||
>
|
>
|
||||||
<div className="mx_RoomView_empty">
|
<div className="mx_RoomView_empty">
|
||||||
{ _t("You must <a>register</a> to use this functionality",
|
{ _t("You must <a>register</a> to use this functionality",
|
||||||
|
@ -234,7 +232,6 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
return <BaseCard
|
return <BaseCard
|
||||||
className="mx_FilePanel mx_RoomView_messageListWrapper"
|
className="mx_FilePanel mx_RoomView_messageListWrapper"
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
previousPhase={RightPanelPhases.RoomSummary}
|
|
||||||
>
|
>
|
||||||
<div className="mx_RoomView_empty">{ _t("You must join the room to see its files") }</div>
|
<div className="mx_RoomView_empty">{ _t("You must join the room to see its files") }</div>
|
||||||
</BaseCard>;
|
</BaseCard>;
|
||||||
|
@ -258,7 +255,6 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
<BaseCard
|
<BaseCard
|
||||||
className="mx_FilePanel"
|
className="mx_FilePanel"
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
previousPhase={RightPanelPhases.RoomSummary}
|
|
||||||
withoutScrollContainer
|
withoutScrollContainer
|
||||||
>
|
>
|
||||||
<DesktopBuildsNotice isRoomEncrypted={isRoomEncrypted} kind={WarningKind.Files} />
|
<DesktopBuildsNotice isRoomEncrypted={isRoomEncrypted} kind={WarningKind.Files} />
|
||||||
|
@ -285,7 +281,6 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
<BaseCard
|
<BaseCard
|
||||||
className="mx_FilePanel"
|
className="mx_FilePanel"
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
previousPhase={RightPanelPhases.RoomSummary}
|
|
||||||
>
|
>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
|
|
|
@ -27,12 +27,10 @@ import GroupStore from '../../stores/GroupStore';
|
||||||
import { RightPanelPhases } from '../../stores/right-panel/RightPanelStorePhases';
|
import { RightPanelPhases } from '../../stores/right-panel/RightPanelStorePhases';
|
||||||
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
import { Action } from "../../dispatcher/actions";
|
|
||||||
import RoomSummaryCard from "../views/right_panel/RoomSummaryCard";
|
import RoomSummaryCard from "../views/right_panel/RoomSummaryCard";
|
||||||
import WidgetCard from "../views/right_panel/WidgetCard";
|
import WidgetCard from "../views/right_panel/WidgetCard";
|
||||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import { ActionPayload } from "../../dispatcher/payloads";
|
|
||||||
import MemberList from "../views/rooms/MemberList";
|
import MemberList from "../views/rooms/MemberList";
|
||||||
import GroupMemberList from "../views/groups/GroupMemberList";
|
import GroupMemberList from "../views/groups/GroupMemberList";
|
||||||
import GroupRoomList from "../views/groups/GroupRoomList";
|
import GroupRoomList from "../views/groups/GroupRoomList";
|
||||||
|
@ -47,7 +45,6 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||||
import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard";
|
import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard";
|
||||||
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
|
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
|
||||||
import { E2EStatus } from '../../utils/ShieldUtils';
|
import { E2EStatus } from '../../utils/ShieldUtils';
|
||||||
import { dispatchShowThreadsPanelEvent } from '../../dispatcher/dispatch-actions/threads';
|
|
||||||
import TimelineCard from '../views/right_panel/TimelineCard';
|
import TimelineCard from '../views/right_panel/TimelineCard';
|
||||||
import { UPDATE_EVENT } from '../../stores/AsyncStore';
|
import { UPDATE_EVENT } from '../../stores/AsyncStore';
|
||||||
import { IRightPanelCard, IRightPanelCardState } from '../../stores/right-panel/RightPanelStoreIPanelState';
|
import { IRightPanelCard, IRightPanelCardState } from '../../stores/right-panel/RightPanelStoreIPanelState';
|
||||||
|
@ -72,8 +69,6 @@ interface IState {
|
||||||
export default class RightPanel extends React.Component<IProps, IState> {
|
export default class RightPanel extends React.Component<IProps, IState> {
|
||||||
static contextType = MatrixClientContext;
|
static contextType = MatrixClientContext;
|
||||||
|
|
||||||
private dispatcherRef: string;
|
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
|
@ -90,7 +85,6 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
||||||
}, 500, { leading: true, trailing: true });
|
}, 500, { leading: true, trailing: true });
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
|
||||||
const cli = this.context;
|
const cli = this.context;
|
||||||
cli.on("RoomState.members", this.onRoomStateMember);
|
cli.on("RoomState.members", this.onRoomStateMember);
|
||||||
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||||
|
@ -98,7 +92,6 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
dis.unregister(this.dispatcherRef);
|
|
||||||
if (this.context) {
|
if (this.context) {
|
||||||
this.context.removeListener("RoomState.members", this.onRoomStateMember);
|
this.context.removeListener("RoomState.members", this.onRoomStateMember);
|
||||||
}
|
}
|
||||||
|
@ -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 = () => {
|
private onClose = () => {
|
||||||
// XXX: There are three different ways of 'closing' this panel depending on what state
|
// 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
|
// things are in... this knows far more than it should do about the state of the rest
|
||||||
|
|
|
@ -94,7 +94,7 @@ import MessageComposer from '../views/rooms/MessageComposer';
|
||||||
import JumpToBottomButton from "../views/rooms/JumpToBottomButton";
|
import JumpToBottomButton from "../views/rooms/JumpToBottomButton";
|
||||||
import TopUnreadMessagesBar from "../views/rooms/TopUnreadMessagesBar";
|
import TopUnreadMessagesBar from "../views/rooms/TopUnreadMessagesBar";
|
||||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
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 { fetchInitialEvent } from "../../utils/EventUtils";
|
||||||
import { ComposerType } from "../../dispatcher/payloads/ComposerInsertPayload";
|
import { ComposerType } from "../../dispatcher/payloads/ComposerInsertPayload";
|
||||||
import AppsDrawer from '../views/rooms/AppsDrawer';
|
import AppsDrawer from '../views/rooms/AppsDrawer';
|
||||||
|
@ -338,6 +338,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
if (WidgetLayoutStore.instance.hasMaximisedWidget(this.state.room)) {
|
if (WidgetLayoutStore.instance.hasMaximisedWidget(this.state.room)) {
|
||||||
// Show chat in right panel when a widget is maximised
|
// Show chat in right panel when a widget is maximised
|
||||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.Timeline });
|
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);
|
this.checkWidgets(this.state.room);
|
||||||
};
|
};
|
||||||
|
@ -424,21 +431,21 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
const thread = initialEvent?.getThread();
|
const thread = initialEvent?.getThread();
|
||||||
if (thread && !initialEvent?.isThreadRoot) {
|
if (thread && !initialEvent?.isThreadRoot) {
|
||||||
dispatchShowThreadEvent(
|
showThread({
|
||||||
thread.rootEvent,
|
rootEvent: thread.rootEvent,
|
||||||
initialEvent,
|
initialEvent,
|
||||||
RoomViewStore.isInitialEventHighlighted(),
|
highlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||||
);
|
});
|
||||||
} else {
|
} else {
|
||||||
newState.initialEventId = initialEventId;
|
newState.initialEventId = initialEventId;
|
||||||
newState.isInitialEventHighlighted = RoomViewStore.isInitialEventHighlighted();
|
newState.isInitialEventHighlighted = RoomViewStore.isInitialEventHighlighted();
|
||||||
|
|
||||||
if (thread && initialEvent?.isThreadRoot) {
|
if (thread && initialEvent?.isThreadRoot) {
|
||||||
dispatchShowThreadEvent(
|
showThread({
|
||||||
thread.rootEvent,
|
rootEvent: thread.rootEvent,
|
||||||
initialEvent,
|
initialEvent,
|
||||||
RoomViewStore.isInitialEventHighlighted(),
|
highlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,6 @@ import UploadBar from './UploadBar';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import ThreadListContextMenu from '../views/context_menus/ThreadListContextMenu';
|
import ThreadListContextMenu from '../views/context_menus/ThreadListContextMenu';
|
||||||
import RightPanelStore from '../../stores/right-panel/RightPanelStore';
|
import RightPanelStore from '../../stores/right-panel/RightPanelStore';
|
||||||
import SettingsStore from '../../settings/SettingsStore';
|
|
||||||
import { WidgetLayoutStore } from '../../stores/widgets/WidgetLayoutStore';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -194,24 +192,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
event_id: this.state.thread?.id,
|
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 (
|
return (
|
||||||
<RoomContext.Provider value={{
|
<RoomContext.Provider value={{
|
||||||
...this.context,
|
...this.context,
|
||||||
|
@ -222,8 +202,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
<BaseCard
|
<BaseCard
|
||||||
className="mx_ThreadView mx_ThreadPanel"
|
className="mx_ThreadView mx_ThreadPanel"
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
previousPhase={previousPhase}
|
|
||||||
previousPhaseLabel={previousPhaseLabels[previousPhase]}
|
|
||||||
withoutScrollContainer={true}
|
withoutScrollContainer={true}
|
||||||
header={this.renderThreadViewHeader()}
|
header={this.renderThreadViewHeader()}
|
||||||
>
|
>
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { Action } from "../../../dispatcher/actions";
|
||||||
import BaseAvatar from "./BaseAvatar";
|
import BaseAvatar from "./BaseAvatar";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { mediaFromMxc } from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
|
import { CardContext } from '../right_panel/BaseCard';
|
||||||
|
|
||||||
interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" | "idName" | "url"> {
|
interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" | "idName" | "url"> {
|
||||||
member: RoomMember;
|
member: RoomMember;
|
||||||
|
@ -36,6 +37,7 @@ interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" |
|
||||||
onClick?: React.MouseEventHandler;
|
onClick?: React.MouseEventHandler;
|
||||||
// Whether the onClick of the avatar should be overridden to dispatch `Action.ViewUser`
|
// Whether the onClick of the avatar should be overridden to dispatch `Action.ViewUser`
|
||||||
viewUserOnClick?: boolean;
|
viewUserOnClick?: boolean;
|
||||||
|
pushUserOnClick?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
style?: any;
|
style?: any;
|
||||||
}
|
}
|
||||||
|
@ -99,6 +101,7 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.ViewUser,
|
action: Action.ViewUser,
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
|
push: this.context.isCard,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -109,7 +112,10 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
|
||||||
title={this.state.title}
|
title={this.state.title}
|
||||||
idName={userId}
|
idName={userId}
|
||||||
url={this.state.imageUrl}
|
url={this.state.imageUrl}
|
||||||
onClick={onClick} />
|
onClick={onClick}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemberAvatar.contextType = CardContext;
|
||||||
|
|
|
@ -51,10 +51,11 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
private openRequest = () => {
|
private openRequest = () => {
|
||||||
const { verificationRequest } = this.props.mxEvent;
|
const { verificationRequest } = this.props.mxEvent;
|
||||||
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
|
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
|
||||||
RightPanelStore.instance.setCard({
|
RightPanelStore.instance.setCards([
|
||||||
phase: RightPanelPhases.EncryptionPanel,
|
{ phase: RightPanelPhases.RoomSummary },
|
||||||
state: { verificationRequest, member },
|
{ phase: RightPanelPhases.RoomMemberInfo, state: { member } },
|
||||||
});
|
{ phase: RightPanelPhases.EncryptionPanel, state: { verificationRequest, member } },
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRequestChanged = () => {
|
private onRequestChanged = () => {
|
||||||
|
|
|
@ -39,8 +39,9 @@ import DownloadActionButton from "./DownloadActionButton";
|
||||||
import SettingsStore from '../../../settings/SettingsStore';
|
import SettingsStore from '../../../settings/SettingsStore';
|
||||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||||
import ReplyChain from '../elements/ReplyChain';
|
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 ReactionPicker from "../emojipicker/ReactionPicker";
|
||||||
|
import { CardContext } from '../right_panel/BaseCard';
|
||||||
|
|
||||||
interface IOptionsButtonProps {
|
interface IOptionsButtonProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -219,8 +220,8 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onThreadClick = (): void => {
|
private onThreadClick = (isCard: boolean): void => {
|
||||||
dispatchShowThreadEvent(this.props.mxEvent);
|
showThread({ rootEvent: this.props.mxEvent, push: isCard });
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.FocusSendMessageComposer,
|
action: Action.FocusSendMessageComposer,
|
||||||
context: TimelineRenderingType.Thread,
|
context: TimelineRenderingType.Thread,
|
||||||
|
@ -303,6 +304,17 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
key="cancel"
|
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.
|
// We show a different toolbar for failed events, so detect that first.
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
const editStatus = mxEvent.replacingEvent() && mxEvent.replacingEvent().status;
|
const editStatus = mxEvent.replacingEvent() && mxEvent.replacingEvent().status;
|
||||||
|
@ -335,12 +347,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
key="reply"
|
key="reply"
|
||||||
/>
|
/>
|
||||||
{ (this.showReplyInThreadAction) && (
|
{ (this.showReplyInThreadAction) && (
|
||||||
<RovingAccessibleTooltipButton
|
threadTooltipButton
|
||||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
|
|
||||||
title={_t("Reply in thread")}
|
|
||||||
onClick={this.onThreadClick}
|
|
||||||
key="thread"
|
|
||||||
/>
|
|
||||||
) }
|
) }
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
@ -368,12 +375,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
this.props.mxEvent.getThread() &&
|
this.props.mxEvent.getThread() &&
|
||||||
!isContentActionable(this.props.mxEvent)
|
!isContentActionable(this.props.mxEvent)
|
||||||
) {
|
) {
|
||||||
toolbarOpts.unshift(<RovingAccessibleTooltipButton
|
toolbarOpts.unshift(threadTooltipButton);
|
||||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
|
|
||||||
title={_t("Reply in thread")}
|
|
||||||
onClick={this.onThreadClick}
|
|
||||||
key="thread"
|
|
||||||
/>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowCancel) {
|
if (allowCancel) {
|
||||||
|
|
|
@ -20,16 +20,15 @@ import classNames from 'classnames';
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
|
|
||||||
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
|
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
|
||||||
|
import { backLabelForPhase } from '../../../stores/right-panel/RightPanelStorePhases';
|
||||||
|
|
||||||
|
export const CardContext = React.createContext({ isCard: false });
|
||||||
interface IProps {
|
interface IProps {
|
||||||
header?: ReactNode;
|
header?: ReactNode;
|
||||||
footer?: ReactNode;
|
footer?: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
withoutScrollContainer?: boolean;
|
withoutScrollContainer?: boolean;
|
||||||
previousPhase?: RightPanelPhases;
|
|
||||||
previousPhaseLabel?: string;
|
|
||||||
closeLabel?: string;
|
closeLabel?: string;
|
||||||
onClose?(): void;
|
onClose?(): void;
|
||||||
cardState?;
|
cardState?;
|
||||||
|
@ -54,20 +53,16 @@ const BaseCard: React.FC<IProps> = ({
|
||||||
header,
|
header,
|
||||||
footer,
|
footer,
|
||||||
withoutScrollContainer,
|
withoutScrollContainer,
|
||||||
previousPhase,
|
|
||||||
previousPhaseLabel,
|
|
||||||
children,
|
children,
|
||||||
cardState,
|
|
||||||
}) => {
|
}) => {
|
||||||
let backButton;
|
let backButton;
|
||||||
if (previousPhase) {
|
const cardHistory = RightPanelStore.instance.roomPhaseHistory;
|
||||||
|
if (cardHistory.length > 1) {
|
||||||
|
const prevCard = cardHistory[cardHistory.length - 2];
|
||||||
const onBackClick = () => {
|
const onBackClick = () => {
|
||||||
// TODO RightPanelStore (will be addressed in a follow up PR): this should ideally be:
|
RightPanelStore.instance.popCard();
|
||||||
// RightPanelStore.instance.popRightPanel();
|
|
||||||
|
|
||||||
RightPanelStore.instance.setCard({ phase: previousPhase, state: cardState });
|
|
||||||
};
|
};
|
||||||
const label = previousPhaseLabel ?? _t("Back");
|
const label = backLabelForPhase(prevCard.phase) ?? _t("Back");
|
||||||
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={label} />;
|
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={label} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,15 +82,17 @@ const BaseCard: React.FC<IProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames("mx_BaseCard", className)}>
|
<CardContext.Provider value={{ isCard: true }}>
|
||||||
<div className="mx_BaseCard_header">
|
<div className={classNames("mx_BaseCard", className)}>
|
||||||
{ backButton }
|
<div className="mx_BaseCard_header">
|
||||||
{ closeButton }
|
{ backButton }
|
||||||
{ header }
|
{ closeButton }
|
||||||
|
{ header }
|
||||||
|
</div>
|
||||||
|
{ children }
|
||||||
|
{ footer && <div className="mx_BaseCard_footer">{ footer }</div> }
|
||||||
</div>
|
</div>
|
||||||
{ children }
|
</CardContext.Provider>
|
||||||
{ footer && <div className="mx_BaseCard_footer">{ footer }</div> }
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -116,10 +116,13 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
||||||
setRequest(verificationRequest_);
|
setRequest(verificationRequest_);
|
||||||
setPhase(verificationRequest_.phase);
|
setPhase(verificationRequest_.phase);
|
||||||
// Notify the RightPanelStore about this
|
// Notify the RightPanelStore about this
|
||||||
RightPanelStore.instance.setCard({
|
if (RightPanelStore.instance.currentCard.phase != RightPanelPhases.EncryptionPanel) {
|
||||||
phase: RightPanelPhases.EncryptionPanel,
|
RightPanelStore.instance.pushCard({
|
||||||
state: { member, verificationRequest: verificationRequest_ },
|
phase: RightPanelPhases.EncryptionPanel,
|
||||||
});
|
state: { member, verificationRequest: verificationRequest_ },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!RightPanelStore.instance.isOpenForRoom) RightPanelStore.instance.togglePanel();
|
||||||
}, [member]);
|
}, [member]);
|
||||||
|
|
||||||
const requested =
|
const requested =
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { Action } from "../../../dispatcher/actions";
|
||||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||||
import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload";
|
import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
|
||||||
|
|
||||||
const GROUP_PHASES = [
|
const GROUP_PHASES = [
|
||||||
RightPanelPhases.GroupMemberInfo,
|
RightPanelPhases.GroupMemberInfo,
|
||||||
|
@ -49,7 +50,11 @@ export default class GroupHeaderButtons extends HeaderButtons {
|
||||||
protected onAction(payload: ActionPayload) {
|
protected onAction(payload: ActionPayload) {
|
||||||
if (payload.action === Action.ViewUser) {
|
if (payload.action === Action.ViewUser) {
|
||||||
if ((payload as ViewUserPayload).member) {
|
if ((payload as ViewUserPayload).member) {
|
||||||
this.setPhase(RightPanelPhases.RoomMemberInfo, { member: payload.member });
|
RightPanelStore.instance.setCards([
|
||||||
|
{ phase: RightPanelPhases.GroupRoomInfo },
|
||||||
|
{ phase: RightPanelPhases.GroupMemberList },
|
||||||
|
{ phase: RightPanelPhases.RoomMemberInfo, state: { member: payload.member } },
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,13 @@ export default abstract class HeaderButtons<P = {}> extends React.Component<IPro
|
||||||
protected abstract onAction(payload);
|
protected abstract onAction(payload);
|
||||||
|
|
||||||
public setPhase(phase: RightPanelPhases, cardState?: Partial<IRightPanelCardState>) {
|
public setPhase(phase: RightPanelPhases, cardState?: Partial<IRightPanelCardState>) {
|
||||||
RightPanelStore.instance.setCard({ phase, state: cardState });
|
const rps = RightPanelStore.instance;
|
||||||
|
if (rps.currentCard.phase == phase && !cardState && rps.isOpenForRoom) {
|
||||||
|
rps.togglePanel();
|
||||||
|
} else {
|
||||||
|
RightPanelStore.instance.setCard({ phase, state: cardState });
|
||||||
|
if (!rps.isOpenForRoom) rps.togglePanel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public isPhase(phases: string | string[]) {
|
public isPhase(phases: string | string[]) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { useSettingValue } from "../../../hooks/useSettings";
|
import { useSettingValue } from "../../../hooks/useSettings";
|
||||||
import { useReadPinnedEvents, usePinnedEvents } from './PinnedMessagesCard';
|
import { useReadPinnedEvents, usePinnedEvents } from './PinnedMessagesCard';
|
||||||
import { dispatchShowThreadsPanelEvent } from "../../../dispatcher/dispatch-actions/threads";
|
import { showThreadPanel } from "../../../dispatcher/dispatch-actions/threads";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
|
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
|
||||||
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
|
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
|
||||||
|
@ -154,7 +154,17 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
|
||||||
protected onAction(payload: ActionPayload) {
|
protected onAction(payload: ActionPayload) {
|
||||||
if (payload.action === Action.ViewUser) {
|
if (payload.action === Action.ViewUser) {
|
||||||
if (payload.member) {
|
if (payload.member) {
|
||||||
this.setPhase(RightPanelPhases.RoomMemberInfo, { member: payload.member });
|
if (payload.push) {
|
||||||
|
RightPanelStore.instance.pushCard(
|
||||||
|
{ phase: RightPanelPhases.RoomMemberInfo, state: { member: payload.member } },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
RightPanelStore.instance.setCards([
|
||||||
|
{ phase: RightPanelPhases.RoomSummary },
|
||||||
|
{ phase: RightPanelPhases.RoomMemberList },
|
||||||
|
{ phase: RightPanelPhases.RoomMemberInfo, state: { member: payload.member } },
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setPhase(RightPanelPhases.RoomMemberList);
|
this.setPhase(RightPanelPhases.RoomMemberList);
|
||||||
}
|
}
|
||||||
|
@ -199,7 +209,7 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
|
||||||
if (RoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) {
|
if (RoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) {
|
||||||
RightPanelStore.instance.togglePanel();
|
RightPanelStore.instance.togglePanel();
|
||||||
} else {
|
} else {
|
||||||
dispatchShowThreadsPanelEvent();
|
showThreadPanel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,7 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
||||||
}, [room.roomId]);
|
}, [room.roomId]);
|
||||||
|
|
||||||
const onOpenWidgetClick = () => {
|
const onOpenWidgetClick = () => {
|
||||||
// TODO RightPanelStore (will be addressed in a follow up PR): should push the widget
|
RightPanelStore.instance.pushCard({
|
||||||
RightPanelStore.instance.setCard({
|
|
||||||
phase: RightPanelPhases.Widget,
|
phase: RightPanelPhases.Widget,
|
||||||
state: { widgetId: app.id },
|
state: { widgetId: app.id },
|
||||||
});
|
});
|
||||||
|
@ -234,13 +233,11 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onRoomMembersClick = (allowClose = true) => {
|
export const onRoomMembersClick = (allowClose = true) => {
|
||||||
// TODO RightPanelStore (will be addressed in a follow up PR): should push the phase
|
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, allowClose);
|
||||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomMemberList }, allowClose);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onRoomFilesClick = (allowClose = true) => {
|
export const onRoomFilesClick = (allowClose = true) => {
|
||||||
// TODO RightPanelStore (will be addressed in a follow up PR): should push the phase
|
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, allowClose);
|
||||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.FilePanel }, allowClose);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRoomSettingsClick = () => {
|
const onRoomSettingsClick = () => {
|
||||||
|
|
|
@ -1651,21 +1651,15 @@ const UserInfo: React.FC<IProps> = ({
|
||||||
const classes = ["mx_UserInfo"];
|
const classes = ["mx_UserInfo"];
|
||||||
|
|
||||||
let cardState: IRightPanelCardState;
|
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
|
// We have no previousPhase for when viewing a UserInfo from a Group or without a Room at this time
|
||||||
if (room && phase === RightPanelPhases.EncryptionPanel) {
|
if (room && phase === RightPanelPhases.EncryptionPanel) {
|
||||||
previousPhase = RightPanelPhases.RoomMemberInfo;
|
|
||||||
cardState = { member };
|
cardState = { member };
|
||||||
} else if (room?.isSpaceRoom() && SpaceStore.spacesEnabled) {
|
} else if (room?.isSpaceRoom() && SpaceStore.spacesEnabled) {
|
||||||
previousPhase = RightPanelPhases.SpaceMemberList;
|
|
||||||
cardState = { spaceId: room.roomId };
|
cardState = { spaceId: room.roomId };
|
||||||
} else if (room) {
|
|
||||||
previousPhase = RightPanelPhases.RoomMemberList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onEncryptionPanelClose = () => {
|
const onEncryptionPanelClose = () => {
|
||||||
// TODO RightPanelStore (will be addressed in a follow up PR): here we want to pop the panel
|
RightPanelStore.instance.popCard();
|
||||||
RightPanelStore.instance.setCard({ phase: previousPhase, state: cardState });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
|
@ -1679,7 +1673,8 @@ const UserInfo: React.FC<IProps> = ({
|
||||||
member={member as User}
|
member={member as User}
|
||||||
groupId={groupId as string}
|
groupId={groupId as string}
|
||||||
devices={devices}
|
devices={devices}
|
||||||
isRoomEncrypted={isRoomEncrypted} />
|
isRoomEncrypted={isRoomEncrypted}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case RightPanelPhases.EncryptionPanel:
|
case RightPanelPhases.EncryptionPanel:
|
||||||
|
@ -1720,7 +1715,6 @@ const UserInfo: React.FC<IProps> = ({
|
||||||
header={header}
|
header={header}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
closeLabel={closeLabel}
|
closeLabel={closeLabel}
|
||||||
previousPhase={previousPhase}
|
|
||||||
cardState={cardState}
|
cardState={cardState}
|
||||||
>
|
>
|
||||||
{ content }
|
{ content }
|
||||||
|
|
|
@ -23,7 +23,6 @@ import WidgetUtils from "../../../utils/WidgetUtils";
|
||||||
import AppTile from "../elements/AppTile";
|
import AppTile from "../elements/AppTile";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import { useWidgets } from "./RoomSummaryCard";
|
import { useWidgets } from "./RoomSummaryCard";
|
||||||
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
|
|
||||||
import { ChevronFace, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
|
import { ChevronFace, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
|
||||||
import WidgetContextMenu from "../context_menus/WidgetContextMenu";
|
import WidgetContextMenu from "../context_menus/WidgetContextMenu";
|
||||||
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
||||||
|
@ -48,9 +47,7 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!app || isPinned) {
|
if (!app || isPinned) {
|
||||||
// stop showing this card
|
// stop showing this card
|
||||||
|
RightPanelStore.instance.popCard();
|
||||||
//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]);
|
}, [app, isPinned]);
|
||||||
|
|
||||||
|
@ -88,7 +85,6 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
||||||
header={header}
|
header={header}
|
||||||
className="mx_WidgetCard"
|
className="mx_WidgetCard"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
previousPhase={RightPanelPhases.RoomSummary}
|
|
||||||
withoutScrollContainer
|
withoutScrollContainer
|
||||||
>
|
>
|
||||||
<AppTile
|
<AppTile
|
||||||
|
|
|
@ -61,7 +61,7 @@ import ReactionsRow from '../messages/ReactionsRow';
|
||||||
import { getEventDisplayInfo } from '../../../utils/EventUtils';
|
import { getEventDisplayInfo } from '../../../utils/EventUtils';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import MKeyVerificationConclusion from "../messages/MKeyVerificationConclusion";
|
import MKeyVerificationConclusion from "../messages/MKeyVerificationConclusion";
|
||||||
import { dispatchShowThreadEvent } from '../../../dispatcher/dispatch-actions/threads';
|
import { showThread } from '../../../dispatcher/dispatch-actions/threads';
|
||||||
import { MessagePreviewStore } from '../../../stores/room-list/MessagePreviewStore';
|
import { MessagePreviewStore } from '../../../stores/room-list/MessagePreviewStore';
|
||||||
import { TimelineRenderingType } from "../../../contexts/RoomContext";
|
import { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||||
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||||
|
@ -72,6 +72,7 @@ import { ThreadNotificationState } from '../../../stores/notifications/ThreadNot
|
||||||
import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNotificationStateStore';
|
import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNotificationStateStore';
|
||||||
import { NotificationStateEvents } from '../../../stores/notifications/NotificationState';
|
import { NotificationStateEvents } from '../../../stores/notifications/NotificationState';
|
||||||
import { NotificationColor } from '../../../stores/notifications/NotificationColor';
|
import { NotificationColor } from '../../../stores/notifications/NotificationColor';
|
||||||
|
import { CardContext } from '../right_panel/BaseCard';
|
||||||
|
|
||||||
const eventTileTypes = {
|
const eventTileTypes = {
|
||||||
[EventType.RoomMessage]: 'messages.MessageEvent',
|
[EventType.RoomMessage]: 'messages.MessageEvent',
|
||||||
|
@ -670,21 +671,23 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<CardContext.Consumer>
|
||||||
className="mx_ThreadInfo"
|
{ context =>
|
||||||
onClick={() => {
|
<div
|
||||||
dispatchShowThreadEvent(
|
className="mx_ThreadInfo"
|
||||||
this.props.mxEvent,
|
onClick={() => {
|
||||||
);
|
showThread({ rootEvent: this.props.mxEvent, push: context.isCard });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="mx_ThreadInfo_threads-amount">
|
<span className="mx_ThreadInfo_threads-amount">
|
||||||
{ _t("%(count)s reply", {
|
{ _t("%(count)s reply", {
|
||||||
count: this.thread.length,
|
count: this.thread.length,
|
||||||
}) }
|
}) }
|
||||||
</span>
|
</span>
|
||||||
{ this.renderThreadLastMessagePreview() }
|
{ this.renderThreadLastMessagePreview() }
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
</CardContext.Consumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1411,7 +1414,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
"data-notification": this.state.threadNotification,
|
"data-notification": this.state.threadNotification,
|
||||||
"onMouseEnter": () => this.setState({ hover: true }),
|
"onMouseEnter": () => this.setState({ hover: true }),
|
||||||
"onMouseLeave": () => this.setState({ hover: false }),
|
"onMouseLeave": () => this.setState({ hover: false }),
|
||||||
"onClick": () => dispatchShowThreadEvent(this.props.mxEvent),
|
"onClick": () => showThread({ rootEvent: this.props.mxEvent, push: true }),
|
||||||
}, <>
|
}, <>
|
||||||
{ sender }
|
{ sender }
|
||||||
{ avatar }
|
{ avatar }
|
||||||
|
@ -1430,7 +1433,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
<RovingAccessibleTooltipButton
|
<RovingAccessibleTooltipButton
|
||||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
|
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
|
||||||
title={_t("Reply in thread")}
|
title={_t("Reply in thread")}
|
||||||
onClick={() => dispatchShowThreadEvent(this.props.mxEvent)}
|
onClick={() => showThread({ rootEvent: this.props.mxEvent, push: true })}
|
||||||
key="thread"
|
key="thread"
|
||||||
/>
|
/>
|
||||||
<RovingThreadListContextMenu
|
<RovingThreadListContextMenu
|
||||||
|
|
|
@ -33,7 +33,6 @@ import { isValid3pidInvite } from "../../../RoomInvite";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
||||||
import BaseCard from "../right_panel/BaseCard";
|
import BaseCard from "../right_panel/BaseCard";
|
||||||
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
|
|
||||||
import RoomAvatar from "../avatars/RoomAvatar";
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
import RoomName from "../elements/RoomName";
|
import RoomName from "../elements/RoomName";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
@ -513,7 +512,6 @@ export default class MemberList extends React.Component<IProps, IState> {
|
||||||
return <BaseCard
|
return <BaseCard
|
||||||
className="mx_MemberList"
|
className="mx_MemberList"
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
previousPhase={RightPanelPhases.RoomSummary}
|
|
||||||
>
|
>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</BaseCard>;
|
</BaseCard>;
|
||||||
|
@ -567,11 +565,8 @@ export default class MemberList extends React.Component<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
let previousPhase = RightPanelPhases.RoomSummary;
|
|
||||||
// We have no previousPhase for when viewing a MemberList from a Space
|
|
||||||
let scopeHeader;
|
let scopeHeader;
|
||||||
if (SpaceStore.spacesEnabled && room?.isSpaceRoom()) {
|
if (SpaceStore.spacesEnabled && room?.isSpaceRoom()) {
|
||||||
previousPhase = undefined;
|
|
||||||
scopeHeader = <div className="mx_RightPanel_scopeHeader">
|
scopeHeader = <div className="mx_RightPanel_scopeHeader">
|
||||||
<RoomAvatar room={room} height={32} width={32} />
|
<RoomAvatar room={room} height={32} width={32} />
|
||||||
<RoomName room={room} />
|
<RoomName room={room} />
|
||||||
|
@ -586,7 +581,6 @@ export default class MemberList extends React.Component<IProps, IState> {
|
||||||
</React.Fragment>}
|
</React.Fragment>}
|
||||||
footer={footer}
|
footer={footer}
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
previousPhase={previousPhase}
|
|
||||||
>
|
>
|
||||||
<div className="mx_MemberList_wrapper">
|
<div className="mx_MemberList_wrapper">
|
||||||
<TruncatedList
|
<TruncatedList
|
||||||
|
|
|
@ -199,6 +199,7 @@ export default class MemberTile extends React.Component<IProps, IState> {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.ViewUser,
|
action: Action.ViewUser,
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
|
push: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -115,11 +115,13 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
room_id: request.channel.roomId,
|
room_id: request.channel.roomId,
|
||||||
should_peek: false,
|
should_peek: false,
|
||||||
});
|
});
|
||||||
RightPanelStore.instance.setCard(
|
const member = cli.getUser(request.otherUserId);
|
||||||
{
|
RightPanelStore.instance.setCards(
|
||||||
phase: RightPanelPhases.EncryptionPanel,
|
[
|
||||||
state: { verificationRequest: request, member: cli.getUser(request.otherUserId) },
|
{ phase: RightPanelPhases.RoomSummary },
|
||||||
},
|
{ phase: RightPanelPhases.RoomMemberInfo, state: { member } },
|
||||||
|
{ phase: RightPanelPhases.EncryptionPanel, state: { verificationRequest: request, member } },
|
||||||
|
],
|
||||||
undefined,
|
undefined,
|
||||||
request.channel.roomId,
|
request.channel.roomId,
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,23 +18,32 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
||||||
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
|
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
|
||||||
|
|
||||||
export const dispatchShowThreadEvent = (
|
export const showThread = (props: {
|
||||||
rootEvent: MatrixEvent,
|
rootEvent: MatrixEvent;
|
||||||
initialEvent?: MatrixEvent,
|
initialEvent?: MatrixEvent;
|
||||||
highlighted?: boolean,
|
highlighted?: boolean;
|
||||||
) => {
|
push?: boolean;
|
||||||
// TODO RightPanelStore (will be addressed in a follow up PR): this should really be a push!
|
}) => {
|
||||||
RightPanelStore.instance.setCard({
|
const push = props.push ?? false;
|
||||||
|
const threadViewCard = {
|
||||||
phase: RightPanelPhases.ThreadView,
|
phase: RightPanelPhases.ThreadView,
|
||||||
state: {
|
state: {
|
||||||
threadHeadEvent: rootEvent,
|
threadHeadEvent: props.rootEvent,
|
||||||
initialEvent,
|
initialEvent: props.initialEvent,
|
||||||
isInitialEventHighlighted: highlighted,
|
isInitialEventHighlighted: props.highlighted,
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
if (push) {
|
||||||
|
RightPanelStore.instance.pushCard(threadViewCard);
|
||||||
|
} else {
|
||||||
|
RightPanelStore.instance.setCards([
|
||||||
|
{ phase: RightPanelPhases.ThreadPanel },
|
||||||
|
threadViewCard,
|
||||||
|
]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dispatchShowThreadsPanelEvent = () => {
|
export const showThreadPanel = () => {
|
||||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.ThreadPanel });
|
RightPanelStore.instance.setCard({ phase: RightPanelPhases.ThreadPanel });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -843,6 +843,11 @@
|
||||||
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
"%(senderName)s: %(message)s": "%(senderName)s: %(message)s",
|
||||||
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
|
||||||
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
|
"Threads": "Threads",
|
||||||
|
"Back to chat": "Back to chat",
|
||||||
|
"Room information": "Room information",
|
||||||
|
"Room members": "Room members",
|
||||||
|
"Back to thread": "Back to thread",
|
||||||
"Change notification settings": "Change notification settings",
|
"Change notification settings": "Change notification settings",
|
||||||
"Messaging": "Messaging",
|
"Messaging": "Messaging",
|
||||||
"Profile": "Profile",
|
"Profile": "Profile",
|
||||||
|
@ -1507,7 +1512,6 @@
|
||||||
"this room": "this room",
|
"this room": "this room",
|
||||||
"View older messages in %(roomName)s.": "View older messages in %(roomName)s.",
|
"View older messages in %(roomName)s.": "View older messages in %(roomName)s.",
|
||||||
"Space information": "Space information",
|
"Space information": "Space information",
|
||||||
"Room information": "Room information",
|
|
||||||
"Internal room ID:": "Internal room ID:",
|
"Internal room ID:": "Internal room ID:",
|
||||||
"Room version": "Room version",
|
"Room version": "Room version",
|
||||||
"Room version:": "Room version:",
|
"Room version:": "Room version:",
|
||||||
|
@ -1920,7 +1924,6 @@
|
||||||
"If you have permissions, open the menu on any message and select <b>Pin</b> to stick them here.": "If you have permissions, open the menu on any message and select <b>Pin</b> to stick them here.",
|
"If you have permissions, open the menu on any message and select <b>Pin</b> to stick them here.": "If you have permissions, open the menu on any message and select <b>Pin</b> to stick them here.",
|
||||||
"Pinned messages": "Pinned messages",
|
"Pinned messages": "Pinned messages",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Threads": "Threads",
|
|
||||||
"Room Info": "Room Info",
|
"Room Info": "Room Info",
|
||||||
"You can only pin up to %(count)s widgets|other": "You can only pin up to %(count)s widgets",
|
"You can only pin up to %(count)s widgets|other": "You can only pin up to %(count)s widgets",
|
||||||
"Maximise widget": "Maximise widget",
|
"Maximise widget": "Maximise widget",
|
||||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { EventSubscription } from 'fbemitter';
|
|
||||||
|
|
||||||
import defaultDispatcher from '../../dispatcher/dispatcher';
|
import defaultDispatcher from '../../dispatcher/dispatcher';
|
||||||
import { pendingVerificationRequestForUser } from '../../verification';
|
import { pendingVerificationRequestForUser } from '../../verification';
|
||||||
|
@ -31,7 +30,6 @@ import {
|
||||||
convertToStatePanel,
|
convertToStatePanel,
|
||||||
convertToStorePanel,
|
convertToStorePanel,
|
||||||
IRightPanelForRoom,
|
IRightPanelForRoom,
|
||||||
convertCardToStore,
|
|
||||||
} from './RightPanelStoreIPanelState';
|
} from './RightPanelStoreIPanelState';
|
||||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
// import RoomViewStore from '../RoomViewStore';
|
// import RoomViewStore from '../RoomViewStore';
|
||||||
|
@ -63,7 +61,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
private viewedRoomId: string;
|
private viewedRoomId: string;
|
||||||
private isViewingRoom?: boolean;
|
private isViewingRoom?: boolean;
|
||||||
private dispatcherRefRightPanelStore: string;
|
private dispatcherRefRightPanelStore: string;
|
||||||
private roomStoreToken: EventSubscription;
|
private isReady = false;
|
||||||
|
|
||||||
private global?: IRightPanelForRoom = null;
|
private global?: IRightPanelForRoom = null;
|
||||||
private byRoom: {
|
private byRoom: {
|
||||||
|
@ -76,6 +74,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onReady(): Promise<any> {
|
protected async onReady(): Promise<any> {
|
||||||
|
this.isReady = true;
|
||||||
// TODO RightPanelStore (will be addressed when dropping groups): This should be used instead of the onDispatch callback when groups are removed.
|
// TODO RightPanelStore (will be addressed when dropping groups): This should be used instead of the onDispatch callback when groups are removed.
|
||||||
// RoomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
// RoomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
||||||
MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequestUpdate);
|
MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequestUpdate);
|
||||||
|
@ -90,9 +89,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onNotReady(): Promise<any> {
|
protected async onNotReady(): Promise<any> {
|
||||||
if (this.roomStoreToken) {
|
this.isReady = false;
|
||||||
this.roomStoreToken.remove();
|
|
||||||
}
|
|
||||||
MatrixClientPeg.get().off("crypto.verification.request", this.onVerificationRequestUpdate);
|
MatrixClientPeg.get().off("crypto.verification.request", this.onVerificationRequestUpdate);
|
||||||
// TODO RightPanelStore (will be addressed when dropping groups): User this instead of the dispatcher.
|
// TODO RightPanelStore (will be addressed when dropping groups): User this instead of the dispatcher.
|
||||||
// RoomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
// RoomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
||||||
|
@ -140,8 +137,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
// Setters
|
// Setters
|
||||||
public setCard(card: IRightPanelCard, allowClose = true, roomId?: string) {
|
public setCard(card: IRightPanelCard, allowClose = true, roomId?: string) {
|
||||||
const rId = roomId ?? this.viewedRoomId;
|
const rId = roomId ?? this.viewedRoomId;
|
||||||
// this was previously a very multifunctional command:
|
// This function behaves as following:
|
||||||
// Toggle panel: if the same phase is send but without a state
|
|
||||||
// Update state: if the same phase is send but with a state
|
// Update state: if the same phase is send but with a state
|
||||||
// Set right panel and erase history: if a "different to the current" phase is send (with or without a state)
|
// Set right panel and erase history: if a "different to the current" phase is send (with or without a state)
|
||||||
const redirect = this.getVerificationRedirect(card);
|
const redirect = this.getVerificationRedirect(card);
|
||||||
|
@ -149,32 +145,29 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
const cardState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state);
|
const cardState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state);
|
||||||
|
|
||||||
// Checks for wrong SetRightPanelPhase requests
|
// Checks for wrong SetRightPanelPhase requests
|
||||||
if (!this.isPhaseActionIsValid(targetPhase)) return;
|
if (!this.isPhaseActionValid(targetPhase)) return;
|
||||||
|
|
||||||
if (targetPhase === this.currentCard?.phase &&
|
if ((targetPhase === this.currentCardForRoom(rId)?.phase && !!cardState)) {
|
||||||
allowClose &&
|
|
||||||
(this.compareCards({ phase: targetPhase, state: cardState }, this.currentCard) || !cardState)
|
|
||||||
) {
|
|
||||||
// Toggle panel: a toggle command needs to fullfil the following:
|
|
||||||
// - the same phase
|
|
||||||
// - the panel can be closed
|
|
||||||
// - does not contain any state information (state)
|
|
||||||
if (targetPhase != RightPanelPhases.EncryptionPanel) {
|
|
||||||
this.togglePanel(rId);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else if ((targetPhase === this.currentCardForRoom(rId)?.phase && !!cardState)) {
|
|
||||||
// Update state: set right panel with a new state but keep the phase (dont know it this is ever needed...)
|
// Update state: set right panel with a new state but keep the phase (dont know it this is ever needed...)
|
||||||
const hist = this.byRoom[rId]?.history ?? [];
|
const hist = this.byRoom[rId]?.history ?? [];
|
||||||
hist[hist.length - 1].state = cardState;
|
hist[hist.length - 1].state = cardState;
|
||||||
this.emitAndUpdateSettings();
|
this.emitAndUpdateSettings();
|
||||||
return;
|
return;
|
||||||
} else if (targetPhase !== this.currentCard?.phase) {
|
}
|
||||||
|
|
||||||
|
if (targetPhase !== this.currentCard?.phase) {
|
||||||
// Set right panel and erase history.
|
// Set right panel and erase history.
|
||||||
this.setRightPanelCache({ phase: targetPhase, state: cardState ?? {} }, rId);
|
this.setRightPanelCache({ phase: targetPhase, state: cardState ?? {} }, rId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setCards(cards: IRightPanelCard[], allowClose = true, roomId: string = null) {
|
||||||
|
const rId = roomId ?? this.viewedRoomId;
|
||||||
|
const history = cards.map(c => ({ phase: c.phase, state: c.state ?? {} }));
|
||||||
|
this.byRoom[rId] = { history, isOpen: true };
|
||||||
|
this.emitAndUpdateSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public pushCard(
|
public pushCard(
|
||||||
card: IRightPanelCard,
|
card: IRightPanelCard,
|
||||||
allowClose = true,
|
allowClose = true,
|
||||||
|
@ -186,7 +179,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
const pState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state);
|
const pState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state);
|
||||||
|
|
||||||
// Checks for wrong SetRightPanelPhase requests
|
// Checks for wrong SetRightPanelPhase requests
|
||||||
if (!this.isPhaseActionIsValid(targetPhase)) return;
|
if (!this.isPhaseActionValid(targetPhase)) return;
|
||||||
|
|
||||||
let roomCache = this.byRoom[rId];
|
let roomCache = this.byRoom[rId];
|
||||||
if (!!roomCache) {
|
if (!!roomCache) {
|
||||||
|
@ -236,10 +229,6 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private compareCards(a: IRightPanelCard, b: IRightPanelCard): boolean {
|
|
||||||
return JSON.stringify(convertCardToStore(a)) == JSON.stringify(convertCardToStore(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
private emitAndUpdateSettings() {
|
private emitAndUpdateSettings() {
|
||||||
const storePanelGlobal = convertToStorePanel(this.global);
|
const storePanelGlobal = convertToStorePanel(this.global);
|
||||||
SettingsStore.setValue("RightPanel.phasesGlobal", null, SettingLevel.DEVICE, storePanelGlobal);
|
SettingsStore.setValue("RightPanel.phasesGlobal", null, SettingLevel.DEVICE, storePanelGlobal);
|
||||||
|
@ -257,10 +246,8 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
private setRightPanelCache(card: IRightPanelCard, roomId?: string) {
|
private setRightPanelCache(card: IRightPanelCard, roomId?: string) {
|
||||||
this.byRoom[roomId ?? this.viewedRoomId] = {
|
const history = [{ phase: card.phase, state: card.state ?? {} }];
|
||||||
history: [{ phase: card.phase, state: card.state ?? {} }],
|
this.byRoom[roomId ?? this.viewedRoomId] = { history, isOpen: true };
|
||||||
isOpen: true,
|
|
||||||
};
|
|
||||||
this.emitAndUpdateSettings();
|
this.emitAndUpdateSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +269,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private isPhaseActionIsValid(targetPhase) {
|
private isPhaseActionValid(targetPhase) {
|
||||||
if (!RightPanelPhases[targetPhase]) {
|
if (!RightPanelPhases[targetPhase]) {
|
||||||
logger.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
|
logger.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
|
||||||
return false;
|
return false;
|
||||||
|
@ -305,6 +292,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
|
|
||||||
private onVerificationRequestUpdate = () => {
|
private onVerificationRequestUpdate = () => {
|
||||||
const { member } = this.currentCard.state;
|
const { member } = this.currentCard.state;
|
||||||
|
if (!member) return;
|
||||||
const pendingRequest = pendingVerificationRequestForUser(member);
|
const pendingRequest = pendingVerificationRequestForUser(member);
|
||||||
if (pendingRequest) {
|
if (pendingRequest) {
|
||||||
this.currentCard.state.verificationRequest = pendingRequest;
|
this.currentCard.state.verificationRequest = pendingRequest;
|
||||||
|
@ -312,13 +300,13 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onRoomViewStoreUpdate() {
|
onRoomViewStoreUpdate = () => {
|
||||||
// TODO: use this function instead of the onDispatch (the whole onDispatch can get removed!) as soon groups are removed
|
// TODO: use this function instead of the onDispatch (the whole onDispatch can get removed!) as soon groups are removed
|
||||||
// this.viewedRoomId = RoomViewStore.getRoomId();
|
// this.viewedRoomId = RoomViewStore.getRoomId();
|
||||||
// this.isViewingRoom = true; // Is viewing room will of course be removed when removing groups
|
// this.isViewingRoom = true; // Is viewing room will of course be removed when removing groups
|
||||||
// // load values from byRoomCache with the viewedRoomId.
|
// // load values from byRoomCache with the viewedRoomId.
|
||||||
// this.loadCacheFromSettings();
|
// this.loadCacheFromSettings();
|
||||||
}
|
};
|
||||||
|
|
||||||
onDispatch(payload: ActionPayload) {
|
onDispatch(payload: ActionPayload) {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
|
@ -347,9 +335,9 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
_this.viewedRoomId = payload.room_id;
|
_this.viewedRoomId = payload.room_id;
|
||||||
_this.isViewingRoom = payload.action == Action.ViewRoom;
|
_this.isViewingRoom = payload.action == Action.ViewRoom;
|
||||||
// load values from byRoomCache with the viewedRoomId.
|
// load values from byRoomCache with the viewedRoomId.
|
||||||
if (!!_this.roomStoreToken) {
|
if (_this.isReady) {
|
||||||
// skip loading here since we need the client to be ready to get the events form the ids of the settings
|
// we need the client to be ready to get the events form the ids of the settings
|
||||||
// this loading will be done in the onReady function
|
// the loading will be done in the onReady function (to catch up with the changes done here before it was ready)
|
||||||
// all the logic in this case is not necessary anymore as soon as groups are dropped and we use: onRoomViewStoreUpdate
|
// all the logic in this case is not necessary anymore as soon as groups are dropped and we use: onRoomViewStoreUpdate
|
||||||
_this.loadCacheFromSettings();
|
_this.loadCacheFromSettings();
|
||||||
_this.emitAndUpdateSettings();
|
_this.emitAndUpdateSettings();
|
||||||
|
|
|
@ -89,48 +89,43 @@ export function convertToStatePanel(storeRoom: IRightPanelForRoomStored, room: R
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertCardToStore(panelState: IRightPanelCard): IRightPanelCardStored {
|
export function convertCardToStore(panelState: IRightPanelCard): IRightPanelCardStored {
|
||||||
const panelStateThisRoomStored = { ...panelState.state } as any;
|
const state = panelState.state ?? {};
|
||||||
if (!!panelState?.state?.threadHeadEvent?.getId()) {
|
const stateStored: IRightPanelCardStateStored = {
|
||||||
panelStateThisRoomStored.threadHeadEventId = panelState.state.threadHeadEvent.getId();
|
groupId: state.groupId,
|
||||||
}
|
groupRoomId: state.groupRoomId,
|
||||||
if (!!panelState?.state?.memberInfoEvent?.getId()) {
|
widgetId: state.widgetId,
|
||||||
panelStateThisRoomStored.memberInfoEventId = panelState.state.memberInfoEvent.getId();
|
spaceId: state.spaceId,
|
||||||
}
|
isInitialEventHighlighted: state.isInitialEventHighlighted,
|
||||||
if (!!panelState?.state?.initialEvent?.getId()) {
|
threadHeadEventId: !!state?.threadHeadEvent?.getId() ?
|
||||||
panelStateThisRoomStored.initialEventId = panelState.state.initialEvent.getId();
|
panelState.state.threadHeadEvent.getId() : undefined,
|
||||||
}
|
memberInfoEventId: !!state?.memberInfoEvent?.getId() ?
|
||||||
if (!!panelState?.state?.member?.userId) {
|
panelState.state.memberInfoEvent.getId() : undefined,
|
||||||
panelStateThisRoomStored.memberId = panelState.state.member.userId;
|
initialEventId: !!state?.initialEvent?.getId() ?
|
||||||
}
|
panelState.state.initialEvent.getId() : undefined,
|
||||||
delete panelStateThisRoomStored.threadHeadEvent;
|
memberId: !!state?.member?.userId ?
|
||||||
delete panelStateThisRoomStored.initialEvent;
|
panelState.state.member.userId : undefined,
|
||||||
delete panelStateThisRoomStored.memberInfoEvent;
|
};
|
||||||
delete panelStateThisRoomStored.verificationRequest;
|
|
||||||
delete panelStateThisRoomStored.verificationRequestPromise;
|
|
||||||
delete panelStateThisRoomStored.member;
|
|
||||||
|
|
||||||
const storedCard = { state: panelStateThisRoomStored as IRightPanelCardStored, phase: panelState.phase };
|
return { state: stateStored, phase: panelState.phase };
|
||||||
return storedCard as IRightPanelCardStored;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertStoreToCard(panelStateStore: IRightPanelCardStored, room: Room): IRightPanelCard {
|
function convertStoreToCard(panelStateStore: IRightPanelCardStored, room: Room): IRightPanelCard {
|
||||||
const panelStateThisRoom = { ...panelStateStore?.state } as any;
|
const stateStored = panelStateStore.state ?? {};
|
||||||
if (!!panelStateThisRoom.threadHeadEventId) {
|
const state: IRightPanelCardState = {
|
||||||
panelStateThisRoom.threadHeadEvent = room.findEventById(panelStateThisRoom.threadHeadEventId);
|
groupId: stateStored.groupId,
|
||||||
}
|
groupRoomId: stateStored.groupRoomId,
|
||||||
if (!!panelStateThisRoom.memberInfoEventId) {
|
widgetId: stateStored.widgetId,
|
||||||
panelStateThisRoom.memberInfoEvent = room.findEventById(panelStateThisRoom.memberInfoEventId);
|
spaceId: stateStored.spaceId,
|
||||||
}
|
isInitialEventHighlighted: stateStored.isInitialEventHighlighted,
|
||||||
if (!!panelStateThisRoom.initialEventId) {
|
threadHeadEvent: !!stateStored?.threadHeadEventId ?
|
||||||
panelStateThisRoom.initialEvent = room.findEventById(panelStateThisRoom.initialEventId);
|
room.findEventById(stateStored.threadHeadEventId) : undefined,
|
||||||
}
|
memberInfoEvent: !!stateStored?.memberInfoEventId ?
|
||||||
if (!!panelStateThisRoom.memberId) {
|
room.findEventById(stateStored.memberInfoEventId) : undefined,
|
||||||
panelStateThisRoom.member = room.getMember(panelStateThisRoom.memberId);
|
initialEvent: !!stateStored?.initialEventId ?
|
||||||
}
|
room.findEventById(stateStored.initialEventId) : undefined,
|
||||||
delete panelStateThisRoom.threadHeadEventId;
|
member: !!stateStored?.memberId ?
|
||||||
delete panelStateThisRoom.initialEventId;
|
room.getMember(stateStored.memberId) : undefined,
|
||||||
delete panelStateThisRoom.memberInfoEventId;
|
};
|
||||||
delete panelStateThisRoom.memberId;
|
|
||||||
|
|
||||||
return { state: panelStateThisRoom as IRightPanelCardState, phase: panelStateStore.phase } as IRightPanelCard;
|
return { state: state, phase: panelStateStore.phase };
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { _t } from "../../languageHandler";
|
||||||
|
|
||||||
// These are in their own file because of circular imports being a problem.
|
// These are in their own file because of circular imports being a problem.
|
||||||
export enum RightPanelPhases {
|
export enum RightPanelPhases {
|
||||||
// Room stuff
|
// Room stuff
|
||||||
|
@ -44,6 +46,17 @@ export enum RightPanelPhases {
|
||||||
ThreadPanel = "ThreadPanel",
|
ThreadPanel = "ThreadPanel",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function backLabelForPhase(phase: RightPanelPhases) {
|
||||||
|
switch (phase) {
|
||||||
|
case RightPanelPhases.ThreadPanel: return _t("Threads");
|
||||||
|
case RightPanelPhases.Timeline: return _t("Back to chat");
|
||||||
|
case RightPanelPhases.RoomSummary: return _t("Room information");
|
||||||
|
case RightPanelPhases.RoomMemberList: return _t("Room members");
|
||||||
|
case RightPanelPhases.ThreadView: return _t("Back to thread");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// These are the phases that are safe to persist (the ones that don't require additional
|
// These are the phases that are safe to persist (the ones that don't require additional
|
||||||
// arguments).
|
// arguments).
|
||||||
export const RIGHT_PANEL_PHASES_NO_ARGS = [
|
export const RIGHT_PANEL_PHASES_NO_ARGS = [
|
||||||
|
|
|
@ -28,6 +28,7 @@ import UntrustedDeviceDialog from "./components/views/dialogs/UntrustedDeviceDia
|
||||||
import { GroupMember, IDevice } from "./components/views/right_panel/UserInfo";
|
import { GroupMember, IDevice } from "./components/views/right_panel/UserInfo";
|
||||||
import ManualDeviceKeyVerificationDialog from "./components/views/dialogs/ManualDeviceKeyVerificationDialog";
|
import ManualDeviceKeyVerificationDialog from "./components/views/dialogs/ManualDeviceKeyVerificationDialog";
|
||||||
import RightPanelStore from "./stores/right-panel/RightPanelStore";
|
import RightPanelStore from "./stores/right-panel/RightPanelStore";
|
||||||
|
import { IRightPanelCardState } from "./stores/right-panel/RightPanelStoreIPanelState";
|
||||||
|
|
||||||
async function enable4SIfNeeded() {
|
async function enable4SIfNeeded() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -66,10 +67,7 @@ export async function verifyDevice(user: User, device: IDevice) {
|
||||||
device.deviceId,
|
device.deviceId,
|
||||||
VerificationMethods.SAS,
|
VerificationMethods.SAS,
|
||||||
);
|
);
|
||||||
RightPanelStore.instance.setCard({
|
setRightPanel({ member: user, verificationRequestPromise });
|
||||||
phase: RightPanelPhases.EncryptionPanel,
|
|
||||||
state: { member: user, verificationRequestPromise },
|
|
||||||
});
|
|
||||||
} else if (action === "legacy") {
|
} else if (action === "legacy") {
|
||||||
Modal.createTrackedDialog("Legacy verify session", "legacy verify session",
|
Modal.createTrackedDialog("Legacy verify session", "legacy verify session",
|
||||||
ManualDeviceKeyVerificationDialog,
|
ManualDeviceKeyVerificationDialog,
|
||||||
|
@ -96,10 +94,7 @@ export async function legacyVerifyUser(user: User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const verificationRequestPromise = cli.requestVerification(user.userId);
|
const verificationRequestPromise = cli.requestVerification(user.userId);
|
||||||
RightPanelStore.instance.setCard({
|
setRightPanel({ member: user, verificationRequestPromise });
|
||||||
phase: RightPanelPhases.EncryptionPanel,
|
|
||||||
state: { member: user, verificationRequestPromise },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyUser(user: User) {
|
export async function verifyUser(user: User) {
|
||||||
|
@ -112,10 +107,21 @@ export async function verifyUser(user: User) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const existingRequest = pendingVerificationRequestForUser(user);
|
const existingRequest = pendingVerificationRequestForUser(user);
|
||||||
RightPanelStore.instance.setCard({
|
setRightPanel({ member: user, verificationRequest: existingRequest });
|
||||||
phase: RightPanelPhases.EncryptionPanel,
|
}
|
||||||
state: { member: user, verificationRequest: existingRequest },
|
|
||||||
});
|
function setRightPanel(state: IRightPanelCardState) {
|
||||||
|
if (RightPanelStore.instance.roomPhaseHistory.some((card) => (card.phase == RightPanelPhases.RoomSummary))) {
|
||||||
|
RightPanelStore.instance.pushCard(
|
||||||
|
{ phase: RightPanelPhases.EncryptionPanel, state },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
RightPanelStore.instance.setCards([
|
||||||
|
{ phase: RightPanelPhases.RoomSummary },
|
||||||
|
{ phase: RightPanelPhases.RoomMemberInfo, state: { member: state.member } },
|
||||||
|
{ phase: RightPanelPhases.EncryptionPanel, state },
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pendingVerificationRequestForUser(user: User | RoomMember | GroupMember ) {
|
export function pendingVerificationRequestForUser(user: User | RoomMember | GroupMember ) {
|
||||||
|
|
Loading…
Reference in a new issue