Merge pull request #5036 from swapnilraj/swapnilraj/right-panel-ts
Convert right_panel to TS
This commit is contained in:
commit
b1f8fe40d6
23 changed files with 568 additions and 387 deletions
|
@ -54,6 +54,7 @@ import LeftPanel from "./LeftPanel";
|
|||
import CallContainer from '../views/voip/CallContainer';
|
||||
import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload";
|
||||
import RoomListStore from "../../stores/room-list/RoomListStore";
|
||||
import { ToggleRightPanelPayload } from "../../dispatcher/payloads/ToggleRightPanelPayload";
|
||||
|
||||
// We need to fetch each pinned message individually (if we don't already have it)
|
||||
// so each pinned message may trigger a request. Limit the number per room for sanity.
|
||||
|
@ -472,8 +473,8 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
|
||||
case Key.PERIOD:
|
||||
if (ctrlCmdOnly && (this.props.page_type === "room_view" || this.props.page_type === "group_view")) {
|
||||
dis.dispatch({
|
||||
action: 'toggle_right_panel',
|
||||
dis.dispatch<ToggleRightPanelPayload>({
|
||||
action: Action.ToggleRightPanel,
|
||||
type: this.props.page_type === "room_view" ? "room" : "group",
|
||||
});
|
||||
handled = true;
|
||||
|
|
|
@ -26,7 +26,7 @@ import dis from '../../dispatcher/dispatcher';
|
|||
import RateLimitedFunc from '../../ratelimitedfunc';
|
||||
import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
||||
import GroupStore from '../../stores/GroupStore';
|
||||
import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases";
|
||||
import {RightPanelPhases, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases";
|
||||
import RightPanelStore from "../../stores/RightPanelStore";
|
||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
import {Action} from "../../dispatcher/actions";
|
||||
|
@ -75,8 +75,8 @@ export default class RightPanel extends React.Component {
|
|||
const userForPanel = this._getUserForPanel();
|
||||
if (this.props.groupId) {
|
||||
if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.groupPanelPhase)) {
|
||||
dis.dispatch({action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.GroupMemberList});
|
||||
return RIGHT_PANEL_PHASES.GroupMemberList;
|
||||
dis.dispatch({action: Action.SetRightPanelPhase, phase: RightPanelPhases.GroupMemberList});
|
||||
return RightPanelPhases.GroupMemberList;
|
||||
}
|
||||
return rps.groupPanelPhase;
|
||||
} else if (userForPanel) {
|
||||
|
@ -98,11 +98,11 @@ export default class RightPanel extends React.Component {
|
|||
) {
|
||||
return rps.roomPanelPhase;
|
||||
}
|
||||
return RIGHT_PANEL_PHASES.RoomMemberInfo;
|
||||
return RightPanelPhases.RoomMemberInfo;
|
||||
} else {
|
||||
if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.roomPanelPhase)) {
|
||||
dis.dispatch({action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.RoomMemberList});
|
||||
return RIGHT_PANEL_PHASES.RoomMemberList;
|
||||
dis.dispatch({action: Action.SetRightPanelPhase, phase: RightPanelPhases.RoomMemberList});
|
||||
return RightPanelPhases.RoomMemberList;
|
||||
}
|
||||
return rps.roomPanelPhase;
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ export default class RightPanel extends React.Component {
|
|||
onInviteToGroupButtonClick() {
|
||||
showGroupInviteDialog(this.props.groupId).then(() => {
|
||||
this.setState({
|
||||
phase: RIGHT_PANEL_PHASES.GroupMemberList,
|
||||
phase: RightPanelPhases.GroupMemberList,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -165,9 +165,9 @@ export default class RightPanel extends React.Component {
|
|||
return;
|
||||
}
|
||||
// redraw the badge on the membership list
|
||||
if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberList && member.roomId === this.props.roomId) {
|
||||
if (this.state.phase === RightPanelPhases.RoomMemberList && member.roomId === this.props.roomId) {
|
||||
this._delayedUpdate();
|
||||
} else if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberInfo && member.roomId === this.props.roomId &&
|
||||
} else if (this.state.phase === RightPanelPhases.RoomMemberInfo && member.roomId === this.props.roomId &&
|
||||
member.userId === this.state.member.userId) {
|
||||
// refresh the member info (e.g. new power level)
|
||||
this._delayedUpdate();
|
||||
|
@ -175,7 +175,7 @@ export default class RightPanel extends React.Component {
|
|||
}
|
||||
|
||||
onAction(payload) {
|
||||
if (payload.action === "after_right_panel_phase_change") {
|
||||
if (payload.action === Action.AfterRightPanelPhaseChange) {
|
||||
this.setState({
|
||||
phase: payload.phase,
|
||||
groupRoomId: payload.groupRoomId,
|
||||
|
@ -206,7 +206,7 @@ export default class RightPanel extends React.Component {
|
|||
// or the member list if we were in the member panel... phew.
|
||||
dis.dispatch({
|
||||
action: Action.ViewUser,
|
||||
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ? this.state.member : null,
|
||||
member: this.state.phase === RightPanelPhases.EncryptionPanel ? this.state.member : null,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -225,21 +225,21 @@ export default class RightPanel extends React.Component {
|
|||
let panel = <div />;
|
||||
|
||||
switch (this.state.phase) {
|
||||
case RIGHT_PANEL_PHASES.RoomMemberList:
|
||||
case RightPanelPhases.RoomMemberList:
|
||||
if (this.props.roomId) {
|
||||
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />;
|
||||
}
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.GroupMemberList:
|
||||
case RightPanelPhases.GroupMemberList:
|
||||
if (this.props.groupId) {
|
||||
panel = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||
}
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.GroupRoomList:
|
||||
case RightPanelPhases.GroupRoomList:
|
||||
panel = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.RoomMemberInfo:
|
||||
case RIGHT_PANEL_PHASES.EncryptionPanel:
|
||||
case RightPanelPhases.RoomMemberInfo:
|
||||
case RightPanelPhases.EncryptionPanel:
|
||||
panel = <UserInfo
|
||||
user={this.state.member}
|
||||
roomId={this.props.roomId}
|
||||
|
@ -250,26 +250,26 @@ export default class RightPanel extends React.Component {
|
|||
verificationRequestPromise={this.state.verificationRequestPromise}
|
||||
/>;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.Room3pidMemberInfo:
|
||||
case RightPanelPhases.Room3pidMemberInfo:
|
||||
panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.roomId} />;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.GroupMemberInfo:
|
||||
case RightPanelPhases.GroupMemberInfo:
|
||||
panel = <UserInfo
|
||||
user={this.state.member}
|
||||
groupId={this.props.groupId}
|
||||
key={this.state.member.userId}
|
||||
onClose={this.onCloseUserInfo} />;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.GroupRoomInfo:
|
||||
case RightPanelPhases.GroupRoomInfo:
|
||||
panel = <GroupRoomInfo
|
||||
groupRoomId={this.state.groupRoomId}
|
||||
groupId={this.props.groupId}
|
||||
key={this.state.groupRoomId} />;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.NotificationPanel:
|
||||
case RightPanelPhases.NotificationPanel:
|
||||
panel = <NotificationPanel />;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.FilePanel:
|
||||
case RightPanelPhases.FilePanel:
|
||||
panel = <FilePanel roomId={this.props.roomId} resizeNotifier={this.props.resizeNotifier} />;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,9 @@ import GroupStore from '../../../stores/GroupStore';
|
|||
import PropTypes from 'prop-types';
|
||||
import { showGroupInviteDialog } from '../../../GroupAddressPicker';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
||||
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
||||
|
||||
|
@ -164,9 +165,9 @@ export default createReactClass({
|
|||
onInviteToGroupButtonClick() {
|
||||
showGroupInviteDialog(this.props.groupId).then(() => {
|
||||
dis.dispatch({
|
||||
action: 'set_right_panel_phase',
|
||||
phase: RIGHT_PANEL_PHASES.GroupMemberList,
|
||||
groupId: this.props.groupId,
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.GroupMemberList,
|
||||
refireParams: { groupId: this.props.groupId },
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -22,7 +22,8 @@ import { _t } from '../../../languageHandler';
|
|||
import {getNameForEventRoom, userLabelForEventRoom}
|
||||
from '../../../utils/KeyVerificationStateObserver';
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
||||
export default class MKeyVerificationRequest extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -48,8 +49,8 @@ export default class MKeyVerificationRequest extends React.Component {
|
|||
const {verificationRequest} = this.props.mxEvent;
|
||||
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
|
||||
dis.dispatch({
|
||||
action: "set_right_panel_phase",
|
||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.EncryptionPanel,
|
||||
refireParams: {verificationRequest, member},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -15,10 +15,10 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import * as sdk from "../../../index";
|
||||
import {_t} from "../../../languageHandler";
|
||||
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
|
||||
|
||||
export const PendingActionSpinner = ({text}) => {
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
|
@ -28,7 +28,17 @@ export const PendingActionSpinner = ({text}) => {
|
|||
</div>;
|
||||
};
|
||||
|
||||
const EncryptionInfo = ({
|
||||
interface IProps {
|
||||
waitingForOtherParty: boolean;
|
||||
waitingForNetwork: boolean;
|
||||
member: RoomMember;
|
||||
onStartVerification: () => Promise<void>;
|
||||
isRoomEncrypted: boolean;
|
||||
inDialog: boolean;
|
||||
isSelfVerification: boolean;
|
||||
}
|
||||
|
||||
const EncryptionInfo: React.FC<IProps> = ({
|
||||
waitingForOtherParty,
|
||||
waitingForNetwork,
|
||||
member,
|
||||
|
@ -36,10 +46,10 @@ const EncryptionInfo = ({
|
|||
isRoomEncrypted,
|
||||
inDialog,
|
||||
isSelfVerification,
|
||||
}) => {
|
||||
let content;
|
||||
}: IProps) => {
|
||||
let content: JSX.Element;
|
||||
if (waitingForOtherParty || waitingForNetwork) {
|
||||
let text;
|
||||
let text: string;
|
||||
if (waitingForOtherParty) {
|
||||
if (isSelfVerification) {
|
||||
text = _t("Waiting for you to accept on your other session…");
|
||||
|
@ -61,7 +71,7 @@ const EncryptionInfo = ({
|
|||
);
|
||||
}
|
||||
|
||||
let description;
|
||||
let description: JSX.Element;
|
||||
if (isRoomEncrypted) {
|
||||
description = (
|
||||
<div>
|
||||
|
@ -97,10 +107,5 @@ const EncryptionInfo = ({
|
|||
</div>
|
||||
</React.Fragment>;
|
||||
};
|
||||
EncryptionInfo.propTypes = {
|
||||
member: PropTypes.object.isRequired,
|
||||
onStartVerification: PropTypes.func.isRequired,
|
||||
request: PropTypes.object,
|
||||
};
|
||||
|
||||
export default EncryptionInfo;
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, {useCallback, useEffect, useState} from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import EncryptionInfo from "./EncryptionInfo";
|
||||
import VerificationPanel from "./VerificationPanel";
|
||||
|
@ -26,11 +25,23 @@ import Modal from "../../../Modal";
|
|||
import {PHASE_REQUESTED, PHASE_UNSENT} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import * as sdk from "../../../index";
|
||||
import {_t} from "../../../languageHandler";
|
||||
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
|
||||
|
||||
// cancellation codes which constitute a key mismatch
|
||||
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
|
||||
|
||||
const EncryptionPanel = (props) => {
|
||||
interface IProps {
|
||||
member: RoomMember;
|
||||
onClose: () => void;
|
||||
verificationRequest: VerificationRequest;
|
||||
verificationRequestPromise: Promise<VerificationRequest>;
|
||||
layout: string;
|
||||
inDialog: boolean;
|
||||
isRoomEncrypted: boolean;
|
||||
}
|
||||
|
||||
const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
||||
const {verificationRequest, verificationRequestPromise, member, onClose, layout, isRoomEncrypted} = props;
|
||||
const [request, setRequest] = useState(verificationRequest);
|
||||
// state to show a spinner immediately after clicking "start verification",
|
||||
|
@ -48,10 +59,10 @@ const EncryptionPanel = (props) => {
|
|||
useEffect(() => {
|
||||
async function awaitPromise() {
|
||||
setRequesting(true);
|
||||
const request = await verificationRequestPromise;
|
||||
const requestFromPromise = await verificationRequestPromise;
|
||||
setRequesting(false);
|
||||
setRequest(request);
|
||||
setPhase(request.phase);
|
||||
setRequest(requestFromPromise);
|
||||
setPhase(requestFromPromise.phase);
|
||||
}
|
||||
if (verificationRequestPromise) {
|
||||
awaitPromise();
|
||||
|
@ -90,7 +101,7 @@ const EncryptionPanel = (props) => {
|
|||
}
|
||||
}, [request]);
|
||||
|
||||
let cancelButton;
|
||||
let cancelButton: JSX.Element;
|
||||
if (layout !== "dialog" && request && request.pending) {
|
||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||
cancelButton = (<AccessibleButton
|
||||
|
@ -104,9 +115,9 @@ const EncryptionPanel = (props) => {
|
|||
setRequesting(true);
|
||||
const cli = MatrixClientPeg.get();
|
||||
const roomId = await ensureDMExists(cli, member.userId);
|
||||
const verificationRequest = await cli.requestVerificationDM(member.userId, roomId);
|
||||
setRequest(verificationRequest);
|
||||
setPhase(verificationRequest.phase);
|
||||
const verificationRequest_ = await cli.requestVerificationDM(member.userId, roomId);
|
||||
setRequest(verificationRequest_);
|
||||
setPhase(verificationRequest_.phase);
|
||||
}, [member.userId]);
|
||||
|
||||
const requested =
|
||||
|
@ -144,12 +155,5 @@ const EncryptionPanel = (props) => {
|
|||
</React.Fragment>);
|
||||
}
|
||||
};
|
||||
EncryptionPanel.propTypes = {
|
||||
member: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
verificationRequest: PropTypes.object,
|
||||
layout: PropTypes.string,
|
||||
inDialog: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default EncryptionPanel;
|
|
@ -21,65 +21,68 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import HeaderButton from './HeaderButton';
|
||||
import HeaderButtons, {HEADER_KIND_GROUP} from './HeaderButtons';
|
||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||
import HeaderButtons, {HeaderKind} from './HeaderButtons';
|
||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||
import {ViewUserPayload} from "../../../dispatcher/payloads/ViewUserPayload";
|
||||
|
||||
const GROUP_PHASES = [
|
||||
RIGHT_PANEL_PHASES.GroupMemberInfo,
|
||||
RIGHT_PANEL_PHASES.GroupMemberList,
|
||||
RightPanelPhases.GroupMemberInfo,
|
||||
RightPanelPhases.GroupMemberList,
|
||||
];
|
||||
const ROOM_PHASES = [
|
||||
RIGHT_PANEL_PHASES.GroupRoomList,
|
||||
RIGHT_PANEL_PHASES.GroupRoomInfo,
|
||||
RightPanelPhases.GroupRoomList,
|
||||
RightPanelPhases.GroupRoomInfo,
|
||||
];
|
||||
|
||||
interface IProps {}
|
||||
|
||||
export default class GroupHeaderButtons extends HeaderButtons {
|
||||
constructor(props) {
|
||||
super(props, HEADER_KIND_GROUP);
|
||||
this._onMembersClicked = this._onMembersClicked.bind(this);
|
||||
this._onRoomsClicked = this._onRoomsClicked.bind(this);
|
||||
constructor(props: IProps) {
|
||||
super(props, HeaderKind.Group);
|
||||
this.onMembersClicked = this.onMembersClicked.bind(this);
|
||||
this.onRoomsClicked = this.onRoomsClicked.bind(this);
|
||||
}
|
||||
|
||||
onAction(payload: ActionPayload) {
|
||||
protected onAction(payload: ActionPayload) {
|
||||
super.onAction(payload);
|
||||
|
||||
if (payload.action === Action.ViewUser) {
|
||||
if (payload.member) {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
||||
if ((payload as ViewUserPayload).member) {
|
||||
this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member});
|
||||
} else {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList);
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
}
|
||||
} else if (payload.action === "view_group") {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList);
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
} else if (payload.action === "view_group_room") {
|
||||
this.setPhase(
|
||||
RIGHT_PANEL_PHASES.GroupRoomInfo,
|
||||
RightPanelPhases.GroupRoomInfo,
|
||||
{groupRoomId: payload.groupRoomId, groupId: payload.groupId},
|
||||
);
|
||||
} else if (payload.action === "view_group_room_list") {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupRoomList);
|
||||
this.setPhase(RightPanelPhases.GroupRoomList);
|
||||
} else if (payload.action === "view_group_member_list") {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList);
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
} else if (payload.action === "view_group_user") {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberInfo, {member: payload.member});
|
||||
this.setPhase(RightPanelPhases.GroupMemberInfo, {member: payload.member});
|
||||
}
|
||||
}
|
||||
|
||||
_onMembersClicked() {
|
||||
if (this.state.phase === RIGHT_PANEL_PHASES.GroupMemberInfo) {
|
||||
private onMembersClicked() {
|
||||
if (this.state.phase === RightPanelPhases.GroupMemberInfo) {
|
||||
// send the active phase to trigger a toggle
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberInfo);
|
||||
this.setPhase(RightPanelPhases.GroupMemberInfo);
|
||||
} else {
|
||||
// This toggles for us, if needed
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList);
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
}
|
||||
}
|
||||
|
||||
_onRoomsClicked() {
|
||||
private onRoomsClicked() {
|
||||
// This toggles for us, if needed
|
||||
this.setPhase(RIGHT_PANEL_PHASES.GroupRoomList);
|
||||
this.setPhase(RightPanelPhases.GroupRoomList);
|
||||
}
|
||||
|
||||
renderButtons() {
|
||||
|
@ -87,13 +90,13 @@ export default class GroupHeaderButtons extends HeaderButtons {
|
|||
<HeaderButton key="groupMembersButton" name="groupMembersButton"
|
||||
title={_t('Members')}
|
||||
isHighlighted={this.isPhase(GROUP_PHASES)}
|
||||
onClick={this._onMembersClicked}
|
||||
onClick={this.onMembersClicked}
|
||||
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
||||
/>,
|
||||
<HeaderButton key="roomsButton" name="roomsButton"
|
||||
title={_t('Rooms')}
|
||||
isHighlighted={this.isPhase(ROOM_PHASES)}
|
||||
onClick={this._onRoomsClicked}
|
||||
onClick={this.onRoomsClicked}
|
||||
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
||||
/>,
|
||||
];
|
|
@ -19,23 +19,38 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Analytics from '../../../Analytics';
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
||||
export default class HeaderButton extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
interface IProps {
|
||||
// Whether this button is highlighted
|
||||
isHighlighted: boolean;
|
||||
// click handler
|
||||
onClick: () => void;
|
||||
// The badge to display above the icon
|
||||
badge?: React.ReactNode;
|
||||
// The parameters to track the click event
|
||||
analytics: string[];
|
||||
|
||||
// Button name
|
||||
name: string;
|
||||
// Button title
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default class HeaderButton extends React.Component<IProps> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
onClick(ev) {
|
||||
private onClick() {
|
||||
Analytics.trackEvent(...this.props.analytics);
|
||||
this.props.onClick();
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const classes = classNames({
|
||||
mx_RightPanel_headerButton: true,
|
||||
mx_RightPanel_headerButton_highlight: this.props.isHighlighted,
|
||||
|
@ -51,19 +66,3 @@ export default class HeaderButton extends React.Component {
|
|||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
HeaderButton.propTypes = {
|
||||
// Whether this button is highlighted
|
||||
isHighlighted: PropTypes.bool.isRequired,
|
||||
// click handler
|
||||
onClick: PropTypes.func.isRequired,
|
||||
// The badge to display above the icon
|
||||
badge: PropTypes.node,
|
||||
// The parameters to track the click event
|
||||
analytics: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
|
||||
// Button name
|
||||
name: PropTypes.string.isRequired,
|
||||
// Button title
|
||||
title: PropTypes.string.isRequired,
|
||||
};
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import RightPanelStore from "../../../stores/RightPanelStore";
|
||||
|
||||
export const HEADER_KIND_ROOM = "room";
|
||||
export const HEADER_KIND_GROUP = "group";
|
||||
|
||||
const HEADER_KINDS = [HEADER_KIND_GROUP, HEADER_KIND_ROOM];
|
||||
|
||||
export default class HeaderButtons extends React.Component {
|
||||
constructor(props, kind) {
|
||||
super(props);
|
||||
|
||||
if (!HEADER_KINDS.includes(kind)) throw new Error(`Invalid header kind: ${kind}`);
|
||||
|
||||
const rps = RightPanelStore.getSharedInstance();
|
||||
this.state = {
|
||||
headerKind: kind,
|
||||
phase: kind === HEADER_KIND_ROOM ? rps.visibleRoomPanelPhase : rps.visibleGroupPanelPhase,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._storeToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelUpdate.bind(this));
|
||||
this._dispatcherRef = dis.register(this.onAction.bind(this)); // used by subclasses
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._storeToken) this._storeToken.remove();
|
||||
if (this._dispatcherRef) dis.unregister(this._dispatcherRef);
|
||||
}
|
||||
|
||||
onAction(payload) {
|
||||
// Ignore - intended to be overridden by subclasses
|
||||
}
|
||||
|
||||
setPhase(phase, extras) {
|
||||
dis.dispatch({
|
||||
action: 'set_right_panel_phase',
|
||||
phase: phase,
|
||||
refireParams: extras,
|
||||
});
|
||||
}
|
||||
|
||||
isPhase(phases: string | string[]) {
|
||||
if (Array.isArray(phases)) {
|
||||
return phases.includes(this.state.phase);
|
||||
} else {
|
||||
return phases === this.state.phase;
|
||||
}
|
||||
}
|
||||
|
||||
onRightPanelUpdate() {
|
||||
const rps = RightPanelStore.getSharedInstance();
|
||||
if (this.state.headerKind === HEADER_KIND_ROOM) {
|
||||
this.setState({phase: rps.visibleRoomPanelPhase});
|
||||
} else if (this.state.headerKind === HEADER_KIND_GROUP) {
|
||||
this.setState({phase: rps.visibleGroupPanelPhase});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// inline style as this will be swapped around in future commits
|
||||
return <div className="mx_HeaderButtons" role="tablist">
|
||||
{this.renderButtons()}
|
||||
</div>;
|
||||
}
|
||||
}
|
110
src/components/views/right_panel/HeaderButtons.tsx
Normal file
110
src/components/views/right_panel/HeaderButtons.tsx
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import RightPanelStore from "../../../stores/RightPanelStore";
|
||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||
import {Action} from '../../../dispatcher/actions';
|
||||
import {SetRightPanelPhasePayload, SetRightPanelPhaseRefireParams} from '../../../dispatcher/payloads/SetRightPanelPhasePayload';
|
||||
import {EventSubscription} from "fbemitter";
|
||||
|
||||
export enum HeaderKind {
|
||||
Room = "room",
|
||||
Group = "group",
|
||||
}
|
||||
|
||||
interface IState {
|
||||
headerKind: HeaderKind;
|
||||
phase: RightPanelPhases;
|
||||
}
|
||||
|
||||
interface IProps {}
|
||||
|
||||
export default class HeaderButtons extends React.Component<IProps, IState> {
|
||||
private storeToken: EventSubscription;
|
||||
private dispatcherRef: string;
|
||||
|
||||
constructor(props: IProps, kind: HeaderKind) {
|
||||
super(props);
|
||||
|
||||
const rps = RightPanelStore.getSharedInstance();
|
||||
this.state = {
|
||||
headerKind: kind,
|
||||
phase: kind === HeaderKind.Room ? rps.visibleRoomPanelPhase : rps.visibleGroupPanelPhase,
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.storeToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelUpdate.bind(this));
|
||||
this.dispatcherRef = dis.register(this.onAction.bind(this)); // used by subclasses
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
if (this.storeToken) this.storeToken.remove();
|
||||
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
|
||||
}
|
||||
|
||||
protected onAction(payload) {
|
||||
// Ignore - intended to be overridden by subclasses
|
||||
}
|
||||
|
||||
public setPhase(phase: RightPanelPhases, extras?: Partial<SetRightPanelPhaseRefireParams>) {
|
||||
dis.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: phase,
|
||||
refireParams: extras,
|
||||
});
|
||||
}
|
||||
|
||||
public isPhase(phases: string | string[]) {
|
||||
if (Array.isArray(phases)) {
|
||||
return phases.includes(this.state.phase);
|
||||
} else {
|
||||
return phases === this.state.phase;
|
||||
}
|
||||
}
|
||||
|
||||
private onRightPanelUpdate() {
|
||||
const rps = RightPanelStore.getSharedInstance();
|
||||
if (this.state.headerKind === HeaderKind.Room) {
|
||||
this.setState({phase: rps.visibleRoomPanelPhase});
|
||||
} else if (this.state.headerKind === HeaderKind.Group) {
|
||||
this.setState({phase: rps.visibleGroupPanelPhase});
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Make renderButtons a prop
|
||||
public renderButtons(): JSX.Element[] {
|
||||
// Ignore - intended to be overridden by subclasses
|
||||
// Return empty fragment to satisfy the type
|
||||
return [
|
||||
<React.Fragment>
|
||||
</React.Fragment>
|
||||
];
|
||||
}
|
||||
|
||||
public render() {
|
||||
// inline style as this will be swapped around in future commits
|
||||
return <div className="mx_HeaderButtons" role="tablist">
|
||||
{this.renderButtons()}
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -21,82 +21,82 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import HeaderButton from './HeaderButton';
|
||||
import HeaderButtons, {HEADER_KIND_ROOM} from './HeaderButtons';
|
||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||
import HeaderButtons, {HeaderKind} from './HeaderButtons';
|
||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||
|
||||
const MEMBER_PHASES = [
|
||||
RIGHT_PANEL_PHASES.RoomMemberList,
|
||||
RIGHT_PANEL_PHASES.RoomMemberInfo,
|
||||
RIGHT_PANEL_PHASES.EncryptionPanel,
|
||||
RIGHT_PANEL_PHASES.Room3pidMemberInfo,
|
||||
RightPanelPhases.RoomMemberList,
|
||||
RightPanelPhases.RoomMemberInfo,
|
||||
RightPanelPhases.EncryptionPanel,
|
||||
RightPanelPhases.Room3pidMemberInfo,
|
||||
];
|
||||
|
||||
export default class RoomHeaderButtons extends HeaderButtons {
|
||||
constructor(props) {
|
||||
super(props, HEADER_KIND_ROOM);
|
||||
this._onMembersClicked = this._onMembersClicked.bind(this);
|
||||
this._onFilesClicked = this._onFilesClicked.bind(this);
|
||||
this._onNotificationsClicked = this._onNotificationsClicked.bind(this);
|
||||
super(props, HeaderKind.Room);
|
||||
this.onMembersClicked = this.onMembersClicked.bind(this);
|
||||
this.onFilesClicked = this.onFilesClicked.bind(this);
|
||||
this.onNotificationsClicked = this.onNotificationsClicked.bind(this);
|
||||
}
|
||||
|
||||
onAction(payload: ActionPayload) {
|
||||
protected onAction(payload: ActionPayload) {
|
||||
super.onAction(payload);
|
||||
if (payload.action === Action.ViewUser) {
|
||||
if (payload.member) {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
||||
this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member});
|
||||
} else {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList);
|
||||
this.setPhase(RightPanelPhases.RoomMemberList);
|
||||
}
|
||||
} else if (payload.action === "view_3pid_invite") {
|
||||
if (payload.event) {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.Room3pidMemberInfo, {event: payload.event});
|
||||
this.setPhase(RightPanelPhases.Room3pidMemberInfo, {event: payload.event});
|
||||
} else {
|
||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList);
|
||||
this.setPhase(RightPanelPhases.RoomMemberList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onMembersClicked() {
|
||||
if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberInfo) {
|
||||
private onMembersClicked() {
|
||||
if (this.state.phase === RightPanelPhases.RoomMemberInfo) {
|
||||
// send the active phase to trigger a toggle
|
||||
// XXX: we should pass refireParams here but then it won't collapse as we desire it to
|
||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo);
|
||||
this.setPhase(RightPanelPhases.RoomMemberInfo);
|
||||
} else {
|
||||
// This toggles for us, if needed
|
||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList);
|
||||
this.setPhase(RightPanelPhases.RoomMemberList);
|
||||
}
|
||||
}
|
||||
|
||||
_onFilesClicked() {
|
||||
private onFilesClicked() {
|
||||
// This toggles for us, if needed
|
||||
this.setPhase(RIGHT_PANEL_PHASES.FilePanel);
|
||||
this.setPhase(RightPanelPhases.FilePanel);
|
||||
}
|
||||
|
||||
_onNotificationsClicked() {
|
||||
private onNotificationsClicked() {
|
||||
// This toggles for us, if needed
|
||||
this.setPhase(RIGHT_PANEL_PHASES.NotificationPanel);
|
||||
this.setPhase(RightPanelPhases.NotificationPanel);
|
||||
}
|
||||
|
||||
renderButtons() {
|
||||
public renderButtons() {
|
||||
return [
|
||||
<HeaderButton key="membersButton" name="membersButton"
|
||||
title={_t('Members')}
|
||||
isHighlighted={this.isPhase(MEMBER_PHASES)}
|
||||
onClick={this._onMembersClicked}
|
||||
onClick={this.onMembersClicked}
|
||||
analytics={['Right Panel', 'Member List Button', 'click']}
|
||||
/>,
|
||||
<HeaderButton key="filesButton" name="filesButton"
|
||||
title={_t('Files')}
|
||||
isHighlighted={this.isPhase(RIGHT_PANEL_PHASES.FilePanel)}
|
||||
onClick={this._onFilesClicked}
|
||||
isHighlighted={this.isPhase(RightPanelPhases.FilePanel)}
|
||||
onClick={this.onFilesClicked}
|
||||
analytics={['Right Panel', 'File List Button', 'click']}
|
||||
/>,
|
||||
<HeaderButton key="notifsButton" name="notifsButton"
|
||||
title={_t('Notifications')}
|
||||
isHighlighted={this.isPhase(RIGHT_PANEL_PHASES.NotificationPanel)}
|
||||
onClick={this._onNotificationsClicked}
|
||||
isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)}
|
||||
onClick={this.onNotificationsClicked}
|
||||
analytics={['Right Panel', 'Notification List Button', 'click']}
|
||||
/>,
|
||||
];
|
|
@ -40,7 +40,7 @@ import E2EIcon from "../rooms/E2EIcon";
|
|||
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
||||
import {textualPowerLevel} from '../../../Roles';
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||
import EncryptionPanel from "./EncryptionPanel";
|
||||
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
|
||||
import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification';
|
||||
|
@ -1480,7 +1480,7 @@ const UserInfoHeader = ({onClose, member, e2eStatus}) => {
|
|||
</React.Fragment>;
|
||||
};
|
||||
|
||||
const UserInfo = ({user, groupId, roomId, onClose, phase=RIGHT_PANEL_PHASES.RoomMemberInfo, ...props}) => {
|
||||
const UserInfo = ({user, groupId, roomId, onClose, phase=RightPanelPhases.RoomMemberInfo, ...props}) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
|
||||
// Load room if we are given a room id and memoize it
|
||||
|
@ -1500,8 +1500,8 @@ const UserInfo = ({user, groupId, roomId, onClose, phase=RIGHT_PANEL_PHASES.Room
|
|||
|
||||
let content;
|
||||
switch (phase) {
|
||||
case RIGHT_PANEL_PHASES.RoomMemberInfo:
|
||||
case RIGHT_PANEL_PHASES.GroupMemberInfo:
|
||||
case RightPanelPhases.RoomMemberInfo:
|
||||
case RightPanelPhases.GroupMemberInfo:
|
||||
content = (
|
||||
<BasicUserInfo
|
||||
room={room}
|
||||
|
@ -1511,7 +1511,7 @@ const UserInfo = ({user, groupId, roomId, onClose, phase=RIGHT_PANEL_PHASES.Room
|
|||
isRoomEncrypted={isRoomEncrypted} />
|
||||
);
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.EncryptionPanel:
|
||||
case RightPanelPhases.EncryptionPanel:
|
||||
classes.push("mx_UserInfo_smallAvatar");
|
||||
content = (
|
||||
<EncryptionPanel {...props} member={member} onClose={onClose} isRoomEncrypted={isRoomEncrypted} />
|
||||
|
|
|
@ -15,12 +15,15 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
import * as sdk from '../../../index';
|
||||
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||
import {SCAN_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
|
||||
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
|
||||
import {ReciprocateQRCode} from "matrix-js-sdk/src/crypto/verification/QRCode";
|
||||
import {SAS} from "matrix-js-sdk/src/crypto/verification/SAS";
|
||||
|
||||
import VerificationQRCode from "../elements/crypto/VerificationQRCode";
|
||||
import {_t} from "../../../languageHandler";
|
||||
|
@ -36,37 +39,51 @@ import {
|
|||
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import Spinner from "../elements/Spinner";
|
||||
|
||||
export default class VerificationPanel extends React.PureComponent {
|
||||
static propTypes = {
|
||||
layout: PropTypes.string,
|
||||
request: PropTypes.object.isRequired,
|
||||
member: PropTypes.object.isRequired,
|
||||
phase: PropTypes.oneOf([
|
||||
// XXX: Should be defined in matrix-js-sdk
|
||||
enum VerificationPhase {
|
||||
PHASE_UNSENT,
|
||||
PHASE_REQUESTED,
|
||||
PHASE_READY,
|
||||
PHASE_DONE,
|
||||
PHASE_STARTED,
|
||||
PHASE_CANCELLED,
|
||||
PHASE_DONE,
|
||||
]).isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
isRoomEncrypted: PropTypes.bool,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this._hasVerifier = false;
|
||||
}
|
||||
|
||||
renderQRPhase() {
|
||||
interface IProps {
|
||||
layout: string;
|
||||
request: VerificationRequest;
|
||||
member: RoomMember;
|
||||
phase: VerificationPhase;
|
||||
onClose: () => void;
|
||||
isRoomEncrypted: boolean;
|
||||
inDialog: boolean;
|
||||
key: number;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
sasEvent?: SAS;
|
||||
emojiButtonClicked?: boolean;
|
||||
reciprocateButtonClicked?: boolean;
|
||||
reciprocateQREvent?: ReciprocateQRCode;
|
||||
}
|
||||
|
||||
export default class VerificationPanel extends React.PureComponent<IProps, IState> {
|
||||
private hasVerifier: boolean;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.hasVerifier = false;
|
||||
}
|
||||
|
||||
private renderQRPhase() {
|
||||
const {member, request} = this.props;
|
||||
const showSAS = request.otherPartySupportsMethod(verificationMethods.SAS);
|
||||
const showQR = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
||||
const showSAS: boolean = request.otherPartySupportsMethod(verificationMethods.SAS);
|
||||
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const brand = SdkConfig.get().brand;
|
||||
|
||||
const noCommonMethodError = !showSAS && !showQR ?
|
||||
const noCommonMethodError: JSX.Element = !showSAS && !showQR ?
|
||||
<p>{_t(
|
||||
"The session you are trying to verify doesn't support scanning a " +
|
||||
"QR code or emoji verification, which is what %(brand)s supports. Try " +
|
||||
|
@ -77,41 +94,41 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
|
||||
if (this.props.layout === 'dialog') {
|
||||
// HACK: This is a terrible idea.
|
||||
let qrBlock;
|
||||
let sasBlock;
|
||||
let qrBlockDialog: JSX.Element;
|
||||
let sasBlockDialog: JSX.Element;
|
||||
if (showQR) {
|
||||
qrBlock =
|
||||
qrBlockDialog =
|
||||
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
||||
<p>{_t("Scan this unique code")}</p>
|
||||
<VerificationQRCode qrCodeData={request.qrCodeData} />
|
||||
</div>;
|
||||
}
|
||||
if (showSAS) {
|
||||
sasBlock =
|
||||
sasBlockDialog =
|
||||
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
||||
<p>{_t("Compare unique emoji")}</p>
|
||||
<span className='mx_VerificationPanel_QRPhase_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span>
|
||||
<AccessibleButton disabled={this.state.emojiButtonClicked} onClick={this._startSAS} kind='primary'>
|
||||
<AccessibleButton disabled={this.state.emojiButtonClicked} onClick={this.startSAS} kind='primary'>
|
||||
{_t("Start")}
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
||||
const or = qrBlock && sasBlock ?
|
||||
const or = qrBlockDialog && sasBlockDialog ?
|
||||
<div className='mx_VerificationPanel_QRPhase_betweenText'>{_t("or")}</div> : null;
|
||||
return (
|
||||
<div>
|
||||
{_t("Verify this session by completing one of the following:")}
|
||||
<div className='mx_VerificationPanel_QRPhase_startOptions'>
|
||||
{qrBlock}
|
||||
{qrBlockDialog}
|
||||
{or}
|
||||
{sasBlock}
|
||||
{sasBlockDialog}
|
||||
{noCommonMethodError}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let qrBlock;
|
||||
let qrBlock: JSX.Element;
|
||||
if (showQR) {
|
||||
qrBlock = <div className="mx_UserInfo_container">
|
||||
<h3>{_t("Verify by scanning")}</h3>
|
||||
|
@ -125,7 +142,7 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
</div>;
|
||||
}
|
||||
|
||||
let sasBlock;
|
||||
let sasBlock: JSX.Element;
|
||||
if (showSAS) {
|
||||
const disabled = this.state.emojiButtonClicked;
|
||||
const sasLabel = showQR ?
|
||||
|
@ -140,7 +157,7 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
disabled={disabled}
|
||||
kind="primary"
|
||||
className="mx_UserInfo_wideButton mx_VerificationPanel_verifyByEmojiButton"
|
||||
onClick={this._startSAS}
|
||||
onClick={this.startSAS}
|
||||
>
|
||||
{_t("Verify by emoji")}
|
||||
</AccessibleButton>
|
||||
|
@ -159,22 +176,22 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
</React.Fragment>;
|
||||
}
|
||||
|
||||
_onReciprocateYesClick = () => {
|
||||
private onReciprocateYesClick = () => {
|
||||
this.setState({reciprocateButtonClicked: true});
|
||||
this.state.reciprocateQREvent.confirm();
|
||||
};
|
||||
|
||||
_onReciprocateNoClick = () => {
|
||||
private onReciprocateNoClick = () => {
|
||||
this.setState({reciprocateButtonClicked: true});
|
||||
this.state.reciprocateQREvent.cancel();
|
||||
};
|
||||
|
||||
_getDevice() {
|
||||
private getDevice() {
|
||||
const deviceId = this.props.request && this.props.request.channel.deviceId;
|
||||
return MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId);
|
||||
}
|
||||
|
||||
renderQRReciprocatePhase() {
|
||||
private renderQRReciprocatePhase() {
|
||||
const {member, request} = this.props;
|
||||
let Button;
|
||||
// a bit of a hack, but the FormButton should only be used in the right panel
|
||||
|
@ -189,7 +206,7 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
_t("Almost there! Is %(displayName)s showing the same shield?", {
|
||||
displayName: member.displayName || member.name || member.userId,
|
||||
});
|
||||
let body;
|
||||
let body: JSX.Element;
|
||||
if (this.state.reciprocateQREvent) {
|
||||
// riot web doesn't support scanning yet, so assume here we're the client being scanned.
|
||||
//
|
||||
|
@ -202,11 +219,11 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
<Button
|
||||
label={_t("No")} kind="danger"
|
||||
disabled={this.state.reciprocateButtonClicked}
|
||||
onClick={this._onReciprocateNoClick}>{_t("No")}</Button>
|
||||
onClick={this.onReciprocateNoClick}>{_t("No")}</Button>
|
||||
<Button
|
||||
label={_t("Yes")} kind="primary"
|
||||
disabled={this.state.reciprocateButtonClicked}
|
||||
onClick={this._onReciprocateYesClick}>{_t("Yes")}</Button>
|
||||
onClick={this.onReciprocateYesClick}>{_t("Yes")}</Button>
|
||||
</div>
|
||||
</React.Fragment>;
|
||||
} else {
|
||||
|
@ -218,10 +235,10 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
</div>;
|
||||
}
|
||||
|
||||
renderVerifiedPhase() {
|
||||
private renderVerifiedPhase() {
|
||||
const {member, request} = this.props;
|
||||
|
||||
let text;
|
||||
let text: string;
|
||||
if (!request.isSelfVerification) {
|
||||
if (this.props.isRoomEncrypted) {
|
||||
text = _t("Verify all users in a room to ensure it's secure.");
|
||||
|
@ -230,9 +247,9 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
let description;
|
||||
let description: string;
|
||||
if (request.isSelfVerification) {
|
||||
const device = this._getDevice();
|
||||
const device = this.getDevice();
|
||||
if (!device) {
|
||||
// This can happen if the device is logged out while we're still showing verification
|
||||
// UI for it.
|
||||
|
@ -264,19 +281,19 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
renderCancelledPhase() {
|
||||
private renderCancelledPhase() {
|
||||
const {member, request} = this.props;
|
||||
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
let startAgainInstruction;
|
||||
let startAgainInstruction: string;
|
||||
if (request.isSelfVerification) {
|
||||
startAgainInstruction = _t("Start verification again from the notification.");
|
||||
} else {
|
||||
startAgainInstruction = _t("Start verification again from their profile.");
|
||||
}
|
||||
|
||||
let text;
|
||||
let text: string;
|
||||
if (request.cancellationCode === "m.timeout") {
|
||||
text = _t("Verification timed out.") + ` ${startAgainInstruction}`;
|
||||
} else if (request.cancellingUserId === request.otherUserId) {
|
||||
|
@ -304,7 +321,7 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const {member, phase, request} = this.props;
|
||||
|
||||
const displayName = member.displayName || member.name || member.userId;
|
||||
|
@ -321,10 +338,10 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
const emojis = this.state.sasEvent ?
|
||||
<VerificationShowSas
|
||||
displayName={displayName}
|
||||
device={this._getDevice()}
|
||||
device={this.getDevice()}
|
||||
sas={this.state.sasEvent.sas}
|
||||
onCancel={this._onSasMismatchesClick}
|
||||
onDone={this._onSasMatchesClick}
|
||||
onCancel={this.onSasMismatchesClick}
|
||||
onDone={this.onSasMatchesClick}
|
||||
inDialog={this.props.inDialog}
|
||||
isSelf={request.isSelfVerification}
|
||||
/> : <Spinner />;
|
||||
|
@ -345,7 +362,7 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
return null;
|
||||
}
|
||||
|
||||
_startSAS = async () => {
|
||||
private startSAS = async () => {
|
||||
this.setState({emojiButtonClicked: true});
|
||||
const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS);
|
||||
try {
|
||||
|
@ -355,31 +372,31 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
_onSasMatchesClick = () => {
|
||||
private onSasMatchesClick = () => {
|
||||
this.state.sasEvent.confirm();
|
||||
};
|
||||
|
||||
_onSasMismatchesClick = () => {
|
||||
private onSasMismatchesClick = () => {
|
||||
this.state.sasEvent.mismatch();
|
||||
};
|
||||
|
||||
_updateVerifierState = () => {
|
||||
private updateVerifierState = () => {
|
||||
const {request} = this.props;
|
||||
const {sasEvent, reciprocateQREvent} = request.verifier;
|
||||
request.verifier.off('show_sas', this._updateVerifierState);
|
||||
request.verifier.off('show_reciprocate_qr', this._updateVerifierState);
|
||||
request.verifier.off('show_sas', this.updateVerifierState);
|
||||
request.verifier.off('show_reciprocate_qr', this.updateVerifierState);
|
||||
this.setState({sasEvent, reciprocateQREvent});
|
||||
};
|
||||
|
||||
_onRequestChange = async () => {
|
||||
private onRequestChange = async () => {
|
||||
const {request} = this.props;
|
||||
const hadVerifier = this._hasVerifier;
|
||||
this._hasVerifier = !!request.verifier;
|
||||
if (!hadVerifier && this._hasVerifier) {
|
||||
request.verifier.on('show_sas', this._updateVerifierState);
|
||||
request.verifier.on('show_reciprocate_qr', this._updateVerifierState);
|
||||
const hadVerifier = this.hasVerifier;
|
||||
this.hasVerifier = !!request.verifier;
|
||||
if (!hadVerifier && this.hasVerifier) {
|
||||
request.verifier.on('show_sas', this.updateVerifierState);
|
||||
request.verifier.on('show_reciprocate_qr', this.updateVerifierState);
|
||||
try {
|
||||
// on the requester side, this is also awaited in _startSAS,
|
||||
// on the requester side, this is also awaited in startSAS,
|
||||
// but that's ok as verify should return the same promise.
|
||||
await request.verifier.verify();
|
||||
} catch (err) {
|
||||
|
@ -388,23 +405,22 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount() {
|
||||
const {request} = this.props;
|
||||
request.on("change", this._onRequestChange);
|
||||
request.on("change", this.onRequestChange);
|
||||
if (request.verifier) {
|
||||
const {request} = this.props;
|
||||
const {sasEvent, reciprocateQREvent} = request.verifier;
|
||||
this.setState({sasEvent, reciprocateQREvent});
|
||||
}
|
||||
this._onRequestChange();
|
||||
this.onRequestChange();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount() {
|
||||
const {request} = this.props;
|
||||
if (request.verifier) {
|
||||
request.verifier.off('show_sas', this._updateVerifierState);
|
||||
request.verifier.off('show_reciprocate_qr', this._updateVerifierState);
|
||||
request.verifier.off('show_sas', this.updateVerifierState);
|
||||
request.verifier.off('show_reciprocate_qr', this.updateVerifierState);
|
||||
}
|
||||
request.off("change", this._onRequestChange);
|
||||
request.off("change", this.onRequestChange);
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
|||
import {ContextMenu} from "../../structures/ContextMenu";
|
||||
import {WidgetType} from "../../../widgets/WidgetType";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
||||
// This should be below the dialog level (4000), but above the rest of the UI (1000-2000).
|
||||
// We sit in a context menu, so this should be given to the context menu.
|
||||
|
@ -181,7 +182,7 @@ export default class Stickerpicker extends React.Component {
|
|||
case "stickerpicker_close":
|
||||
this.setState({showStickers: false});
|
||||
break;
|
||||
case "after_right_panel_phase_change":
|
||||
case Action.AfterRightPanelPhaseChange:
|
||||
case "show_left_panel":
|
||||
case "hide_left_panel":
|
||||
this.setState({showStickers: false});
|
||||
|
|
|
@ -19,7 +19,8 @@ import React from "react";
|
|||
import * as sdk from "../../../index";
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||
import {SetRightPanelPhasePayload} from "../../../dispatcher/payloads/SetRightPanelPhasePayload"
|
||||
import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import ToastStore from "../../../stores/ToastStore";
|
||||
|
@ -27,6 +28,7 @@ import Modal from "../../../Modal";
|
|||
import GenericToast from "./GenericToast";
|
||||
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import {DeviceInfo} from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
||||
interface IProps {
|
||||
toastKey: string;
|
||||
|
@ -104,9 +106,9 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
|||
room_id: request.channel.roomId,
|
||||
should_peek: false,
|
||||
});
|
||||
dis.dispatch({
|
||||
action: "set_right_panel_phase",
|
||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
||||
dis.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.EncryptionPanel,
|
||||
refireParams: {
|
||||
verificationRequest: request,
|
||||
member: cli.getUser(request.otherUserId),
|
||||
|
|
|
@ -79,4 +79,19 @@ export enum Action {
|
|||
* Changes room based on room list order and payload parameters. Should be used with ViewRoomDeltaPayload.
|
||||
*/
|
||||
ViewRoomDelta = "view_room_delta",
|
||||
|
||||
/**
|
||||
* Sets the phase for the right panel. Should be used with SetRightPanelPhasePayload.
|
||||
*/
|
||||
SetRightPanelPhase = "set_right_panel_phase",
|
||||
|
||||
/**
|
||||
* Toggles the right panel. Should be used with ToggleRightPanelPayload.
|
||||
*/
|
||||
ToggleRightPanel = "toggle_right_panel",
|
||||
|
||||
/**
|
||||
* Trigged after the phase of the right panel is set. Should be used with AfterRightPanelPhaseChangePayload.
|
||||
*/
|
||||
AfterRightPanelPhaseChange = "after_right_panel_phase_change",
|
||||
}
|
||||
|
|
30
src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts
Normal file
30
src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
|
||||
import { SetRightPanelPhaseRefireParams } from "./SetRightPanelPhasePayload";
|
||||
import { ActionPayload } from "../payloads";
|
||||
import { Action } from "../actions";
|
||||
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
|
||||
interface AfterRightPanelPhaseChangeAction extends ActionPayload {
|
||||
action: Action.AfterRightPanelPhaseChange;
|
||||
phase: RightPanelPhases;
|
||||
verificationRequestPromise?: Promise<VerificationRequest>;
|
||||
}
|
||||
|
||||
export type AfterRightPanelPhaseChangePayload
|
||||
= AfterRightPanelPhaseChangeAction & SetRightPanelPhaseRefireParams;
|
37
src/dispatcher/payloads/SetRightPanelPhasePayload.ts
Normal file
37
src/dispatcher/payloads/SetRightPanelPhasePayload.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
|
||||
import { ActionPayload } from "../payloads";
|
||||
import { Action } from "../actions";
|
||||
|
||||
export interface SetRightPanelPhasePayload extends ActionPayload {
|
||||
action: Action.SetRightPanelPhase;
|
||||
|
||||
phase: RightPanelPhases;
|
||||
refireParams?: SetRightPanelPhaseRefireParams;
|
||||
}
|
||||
|
||||
export interface SetRightPanelPhaseRefireParams {
|
||||
member?: RoomMember;
|
||||
verificationRequest?: VerificationRequest;
|
||||
groupId?: string;
|
||||
groupRoomId?: string;
|
||||
// XXX: The type for event should 'view_3pid_invite' action's payload
|
||||
event?: any;
|
||||
}
|
27
src/dispatcher/payloads/ToggleRightPanelPayload.ts
Normal file
27
src/dispatcher/payloads/ToggleRightPanelPayload.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { ActionPayload } from "../payloads";
|
||||
import { Action } from "../actions";
|
||||
|
||||
export interface ToggleRightPanelPayload extends ActionPayload {
|
||||
action: Action.ToggleRightPanel;
|
||||
|
||||
/**
|
||||
* The type of room that the panel is toggled in.
|
||||
*/
|
||||
type: "group" | "room";
|
||||
}
|
|
@ -28,7 +28,7 @@ import CustomStatusController from "./controllers/CustomStatusController";
|
|||
import ThemeController from './controllers/ThemeController';
|
||||
import PushToMatrixClientController from './controllers/PushToMatrixClientController';
|
||||
import ReloadOnChangeController from "./controllers/ReloadOnChangeController";
|
||||
import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases";
|
||||
import {RightPanelPhases} from "../stores/RightPanelStorePhases";
|
||||
import FontSizeController from './controllers/FontSizeController';
|
||||
import SystemFontController from './controllers/SystemFontController';
|
||||
import UseSystemFontController from './controllers/UseSystemFontController';
|
||||
|
@ -534,11 +534,11 @@ export const SETTINGS = {
|
|||
},
|
||||
"lastRightPanelPhaseForRoom": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
default: RIGHT_PANEL_PHASES.RoomMemberInfo,
|
||||
default: RightPanelPhases.RoomMemberInfo,
|
||||
},
|
||||
"lastRightPanelPhaseForGroup": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
default: RIGHT_PANEL_PHASES.GroupMemberList,
|
||||
default: RightPanelPhases.GroupMemberList,
|
||||
},
|
||||
"enableEventIndexing": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
|
|
|
@ -18,161 +18,177 @@ import dis from '../dispatcher/dispatcher';
|
|||
import {pendingVerificationRequestForUser} from '../verification';
|
||||
import {Store} from 'flux/utils';
|
||||
import SettingsStore, {SettingLevel} from "../settings/SettingsStore";
|
||||
import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "./RightPanelStorePhases";
|
||||
import {RightPanelPhases, RIGHT_PANEL_PHASES_NO_ARGS} from "./RightPanelStorePhases";
|
||||
import {ActionPayload} from "../dispatcher/payloads";
|
||||
import {Action} from '../dispatcher/actions';
|
||||
|
||||
const INITIAL_STATE = {
|
||||
interface RightPanelStoreState {
|
||||
// Whether or not to show the right panel at all. We split out rooms and groups
|
||||
// because they're different flows for the user to follow.
|
||||
showRoomPanel: SettingsStore.getValue("showRightPanelInRoom"),
|
||||
showGroupPanel: SettingsStore.getValue("showRightPanelInGroup"),
|
||||
showRoomPanel: boolean;
|
||||
showGroupPanel: boolean;
|
||||
|
||||
// The last phase (screen) the right panel was showing
|
||||
lastRoomPhase: SettingsStore.getValue("lastRightPanelPhaseForRoom"),
|
||||
lastGroupPhase: SettingsStore.getValue("lastRightPanelPhaseForGroup"),
|
||||
lastRoomPhase: RightPanelPhases;
|
||||
lastGroupPhase: RightPanelPhases;
|
||||
|
||||
// Extra information about the last phase
|
||||
lastRoomPhaseParams: {[key: string]: any};
|
||||
}
|
||||
|
||||
const INITIAL_STATE: RightPanelStoreState = {
|
||||
showRoomPanel: SettingsStore.getValue("showRightPanelInRoom"),
|
||||
showGroupPanel: SettingsStore.getValue("showRightPanelInGroup"),
|
||||
lastRoomPhase: SettingsStore.getValue("lastRightPanelPhaseForRoom"),
|
||||
lastGroupPhase: SettingsStore.getValue("lastRightPanelPhaseForGroup"),
|
||||
lastRoomPhaseParams: {},
|
||||
};
|
||||
|
||||
const GROUP_PHASES = Object.keys(RIGHT_PANEL_PHASES).filter(k => k.startsWith("Group"));
|
||||
const GROUP_PHASES = [
|
||||
RightPanelPhases.GroupMemberList,
|
||||
RightPanelPhases.GroupRoomList,
|
||||
RightPanelPhases.GroupRoomInfo,
|
||||
RightPanelPhases.GroupMemberInfo,
|
||||
];
|
||||
|
||||
const MEMBER_INFO_PHASES = [
|
||||
RIGHT_PANEL_PHASES.RoomMemberInfo,
|
||||
RIGHT_PANEL_PHASES.Room3pidMemberInfo,
|
||||
RIGHT_PANEL_PHASES.EncryptionPanel,
|
||||
RightPanelPhases.RoomMemberInfo,
|
||||
RightPanelPhases.Room3pidMemberInfo,
|
||||
RightPanelPhases.EncryptionPanel,
|
||||
];
|
||||
|
||||
/**
|
||||
* A class for tracking the state of the right panel between layouts and
|
||||
* sessions.
|
||||
*/
|
||||
export default class RightPanelStore extends Store {
|
||||
static _instance;
|
||||
export default class RightPanelStore extends Store<ActionPayload> {
|
||||
private static instance: RightPanelStore;
|
||||
private state: RightPanelStoreState;
|
||||
|
||||
constructor() {
|
||||
super(dis);
|
||||
|
||||
// Initialise state
|
||||
this._state = INITIAL_STATE;
|
||||
this.state = INITIAL_STATE;
|
||||
}
|
||||
|
||||
get isOpenForRoom(): boolean {
|
||||
return this._state.showRoomPanel;
|
||||
return this.state.showRoomPanel;
|
||||
}
|
||||
|
||||
get isOpenForGroup(): boolean {
|
||||
return this._state.showGroupPanel;
|
||||
return this.state.showGroupPanel;
|
||||
}
|
||||
|
||||
get roomPanelPhase(): string {
|
||||
return this._state.lastRoomPhase;
|
||||
get roomPanelPhase(): RightPanelPhases {
|
||||
return this.state.lastRoomPhase;
|
||||
}
|
||||
|
||||
get groupPanelPhase(): string {
|
||||
return this._state.lastGroupPhase;
|
||||
get groupPanelPhase(): RightPanelPhases {
|
||||
return this.state.lastGroupPhase;
|
||||
}
|
||||
|
||||
get visibleRoomPanelPhase(): string {
|
||||
get visibleRoomPanelPhase(): RightPanelPhases {
|
||||
return this.isOpenForRoom ? this.roomPanelPhase : null;
|
||||
}
|
||||
|
||||
get visibleGroupPanelPhase(): string {
|
||||
get visibleGroupPanelPhase(): RightPanelPhases {
|
||||
return this.isOpenForGroup ? this.groupPanelPhase : null;
|
||||
}
|
||||
|
||||
get roomPanelPhaseParams(): any {
|
||||
return this._state.lastRoomPhaseParams || {};
|
||||
return this.state.lastRoomPhaseParams || {};
|
||||
}
|
||||
|
||||
_setState(newState) {
|
||||
this._state = Object.assign(this._state, newState);
|
||||
private setState(newState: Partial<RightPanelStoreState>) {
|
||||
this.state = Object.assign(this.state, newState);
|
||||
|
||||
SettingsStore.setValue(
|
||||
"showRightPanelInRoom",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
this._state.showRoomPanel,
|
||||
this.state.showRoomPanel,
|
||||
);
|
||||
SettingsStore.setValue(
|
||||
"showRightPanelInGroup",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
this._state.showGroupPanel,
|
||||
this.state.showGroupPanel,
|
||||
);
|
||||
|
||||
if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this._state.lastRoomPhase)) {
|
||||
if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this.state.lastRoomPhase)) {
|
||||
SettingsStore.setValue(
|
||||
"lastRightPanelPhaseForRoom",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
this._state.lastRoomPhase,
|
||||
this.state.lastRoomPhase,
|
||||
);
|
||||
}
|
||||
if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this._state.lastGroupPhase)) {
|
||||
if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this.state.lastGroupPhase)) {
|
||||
SettingsStore.setValue(
|
||||
"lastRightPanelPhaseForGroup",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
this._state.lastGroupPhase,
|
||||
this.state.lastGroupPhase,
|
||||
);
|
||||
}
|
||||
|
||||
this.__emitChange();
|
||||
}
|
||||
|
||||
__onDispatch(payload) {
|
||||
__onDispatch(payload: ActionPayload) {
|
||||
switch (payload.action) {
|
||||
case 'view_room':
|
||||
case 'view_group':
|
||||
// Reset to the member list if we're viewing member info
|
||||
if (MEMBER_INFO_PHASES.includes(this._state.lastRoomPhase)) {
|
||||
this._setState({lastRoomPhase: RIGHT_PANEL_PHASES.RoomMemberList, lastRoomPhaseParams: {}});
|
||||
if (MEMBER_INFO_PHASES.includes(this.state.lastRoomPhase)) {
|
||||
this.setState({lastRoomPhase: RightPanelPhases.RoomMemberList, lastRoomPhaseParams: {}});
|
||||
}
|
||||
|
||||
// Do the same for groups
|
||||
if (this._state.lastGroupPhase === RIGHT_PANEL_PHASES.GroupMemberInfo) {
|
||||
this._setState({lastGroupPhase: RIGHT_PANEL_PHASES.GroupMemberList});
|
||||
if (this.state.lastGroupPhase === RightPanelPhases.GroupMemberInfo) {
|
||||
this.setState({lastGroupPhase: RightPanelPhases.GroupMemberList});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'set_right_panel_phase': {
|
||||
case Action.SetRightPanelPhase: {
|
||||
let targetPhase = payload.phase;
|
||||
let refireParams = payload.refireParams;
|
||||
// redirect to EncryptionPanel if there is an ongoing verification request
|
||||
if (targetPhase === RIGHT_PANEL_PHASES.RoomMemberInfo && payload.refireParams) {
|
||||
if (targetPhase === RightPanelPhases.RoomMemberInfo && payload.refireParams) {
|
||||
const {member} = payload.refireParams;
|
||||
const pendingRequest = pendingVerificationRequestForUser(member);
|
||||
if (pendingRequest) {
|
||||
targetPhase = RIGHT_PANEL_PHASES.EncryptionPanel;
|
||||
targetPhase = RightPanelPhases.EncryptionPanel;
|
||||
refireParams = {
|
||||
verificationRequest: pendingRequest,
|
||||
member,
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!RIGHT_PANEL_PHASES[targetPhase]) {
|
||||
if (!RightPanelPhases[targetPhase]) {
|
||||
console.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GROUP_PHASES.includes(targetPhase)) {
|
||||
if (targetPhase === this._state.lastGroupPhase) {
|
||||
this._setState({
|
||||
showGroupPanel: !this._state.showGroupPanel,
|
||||
if (targetPhase === this.state.lastGroupPhase) {
|
||||
this.setState({
|
||||
showGroupPanel: !this.state.showGroupPanel,
|
||||
});
|
||||
} else {
|
||||
this._setState({
|
||||
this.setState({
|
||||
lastGroupPhase: targetPhase,
|
||||
showGroupPanel: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (targetPhase === this._state.lastRoomPhase && !refireParams) {
|
||||
this._setState({
|
||||
showRoomPanel: !this._state.showRoomPanel,
|
||||
if (targetPhase === this.state.lastRoomPhase && !refireParams) {
|
||||
this.setState({
|
||||
showRoomPanel: !this.state.showRoomPanel,
|
||||
});
|
||||
} else {
|
||||
this._setState({
|
||||
this.setState({
|
||||
lastRoomPhase: targetPhase,
|
||||
showRoomPanel: true,
|
||||
lastRoomPhaseParams: refireParams || {},
|
||||
|
@ -182,27 +198,27 @@ export default class RightPanelStore extends Store {
|
|||
|
||||
// Let things like the member info panel actually open to the right member.
|
||||
dis.dispatch({
|
||||
action: 'after_right_panel_phase_change',
|
||||
action: Action.AfterRightPanelPhaseChange,
|
||||
phase: targetPhase,
|
||||
...(refireParams || {}),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'toggle_right_panel':
|
||||
case Action.ToggleRightPanel:
|
||||
if (payload.type === "room") {
|
||||
this._setState({ showRoomPanel: !this._state.showRoomPanel });
|
||||
this.setState({ showRoomPanel: !this.state.showRoomPanel });
|
||||
} else { // group
|
||||
this._setState({ showGroupPanel: !this._state.showGroupPanel });
|
||||
this.setState({ showGroupPanel: !this.state.showGroupPanel });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static getSharedInstance(): RightPanelStore {
|
||||
if (!RightPanelStore._instance) {
|
||||
RightPanelStore._instance = new RightPanelStore();
|
||||
if (!RightPanelStore.instance) {
|
||||
RightPanelStore.instance = new RightPanelStore();
|
||||
}
|
||||
return RightPanelStore._instance;
|
||||
return RightPanelStore.instance;
|
||||
}
|
||||
}
|
|
@ -15,28 +15,28 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
// These are in their own file because of circular imports being a problem.
|
||||
export const RIGHT_PANEL_PHASES = Object.freeze({
|
||||
export enum RightPanelPhases {
|
||||
// Room stuff
|
||||
RoomMemberList: 'RoomMemberList',
|
||||
FilePanel: 'FilePanel',
|
||||
NotificationPanel: 'NotificationPanel',
|
||||
RoomMemberInfo: 'RoomMemberInfo',
|
||||
EncryptionPanel: 'EncryptionPanel',
|
||||
RoomMemberList = 'RoomMemberList',
|
||||
FilePanel = 'FilePanel',
|
||||
NotificationPanel = 'NotificationPanel',
|
||||
RoomMemberInfo = 'RoomMemberInfo',
|
||||
EncryptionPanel = 'EncryptionPanel',
|
||||
|
||||
Room3pidMemberInfo: 'Room3pidMemberInfo',
|
||||
Room3pidMemberInfo = 'Room3pidMemberInfo',
|
||||
// Group stuff
|
||||
GroupMemberList: 'GroupMemberList',
|
||||
GroupRoomList: 'GroupRoomList',
|
||||
GroupRoomInfo: 'GroupRoomInfo',
|
||||
GroupMemberInfo: 'GroupMemberInfo',
|
||||
});
|
||||
GroupMemberList = 'GroupMemberList',
|
||||
GroupRoomList = 'GroupRoomList',
|
||||
GroupRoomInfo = 'GroupRoomInfo',
|
||||
GroupMemberInfo = 'GroupMemberInfo',
|
||||
}
|
||||
|
||||
// These are the phases that are safe to persist (the ones that don't require additional
|
||||
// arguments).
|
||||
export const RIGHT_PANEL_PHASES_NO_ARGS = [
|
||||
RIGHT_PANEL_PHASES.NotificationPanel,
|
||||
RIGHT_PANEL_PHASES.FilePanel,
|
||||
RIGHT_PANEL_PHASES.RoomMemberList,
|
||||
RIGHT_PANEL_PHASES.GroupMemberList,
|
||||
RIGHT_PANEL_PHASES.GroupRoomList,
|
||||
RightPanelPhases.NotificationPanel,
|
||||
RightPanelPhases.FilePanel,
|
||||
RightPanelPhases.RoomMemberList,
|
||||
RightPanelPhases.GroupMemberList,
|
||||
RightPanelPhases.GroupRoomList,
|
||||
];
|
|
@ -19,10 +19,11 @@ import dis from "./dispatcher/dispatcher";
|
|||
import Modal from './Modal';
|
||||
import * as sdk from './index';
|
||||
import { _t } from './languageHandler';
|
||||
import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases";
|
||||
import {RightPanelPhases} from "./stores/RightPanelStorePhases";
|
||||
import {findDMForUser} from './createRoom';
|
||||
import {accessSecretStorage} from './CrossSigningManager';
|
||||
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||
import {Action} from './dispatcher/actions';
|
||||
|
||||
async function enable4SIfNeeded() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
@ -91,8 +92,8 @@ export async function verifyDevice(user, device) {
|
|||
verificationMethods.SAS,
|
||||
);
|
||||
dis.dispatch({
|
||||
action: "set_right_panel_phase",
|
||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.EncryptionPanel,
|
||||
refireParams: {member: user, verificationRequestPromise},
|
||||
});
|
||||
} else if (action === "legacy") {
|
||||
|
@ -120,8 +121,8 @@ export async function legacyVerifyUser(user) {
|
|||
}
|
||||
const verificationRequestPromise = cli.requestVerification(user.userId);
|
||||
dis.dispatch({
|
||||
action: "set_right_panel_phase",
|
||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.EncryptionPanel,
|
||||
refireParams: {member: user, verificationRequestPromise},
|
||||
});
|
||||
}
|
||||
|
@ -132,8 +133,8 @@ export async function verifyUser(user) {
|
|||
}
|
||||
const existingRequest = pendingVerificationRequestForUser(user);
|
||||
dis.dispatch({
|
||||
action: "set_right_panel_phase",
|
||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.EncryptionPanel,
|
||||
refireParams: {
|
||||
member: user,
|
||||
verificationRequest: existingRequest,
|
||||
|
|
Loading…
Reference in a new issue