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 CallContainer from '../views/voip/CallContainer';
|
||||||
import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload";
|
import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload";
|
||||||
import RoomListStore from "../../stores/room-list/RoomListStore";
|
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)
|
// 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.
|
// 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:
|
case Key.PERIOD:
|
||||||
if (ctrlCmdOnly && (this.props.page_type === "room_view" || this.props.page_type === "group_view")) {
|
if (ctrlCmdOnly && (this.props.page_type === "room_view" || this.props.page_type === "group_view")) {
|
||||||
dis.dispatch({
|
dis.dispatch<ToggleRightPanelPayload>({
|
||||||
action: 'toggle_right_panel',
|
action: Action.ToggleRightPanel,
|
||||||
type: this.props.page_type === "room_view" ? "room" : "group",
|
type: this.props.page_type === "room_view" ? "room" : "group",
|
||||||
});
|
});
|
||||||
handled = true;
|
handled = true;
|
||||||
|
|
|
@ -26,7 +26,7 @@ import dis from '../../dispatcher/dispatcher';
|
||||||
import RateLimitedFunc from '../../ratelimitedfunc';
|
import RateLimitedFunc from '../../ratelimitedfunc';
|
||||||
import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
||||||
import GroupStore from '../../stores/GroupStore';
|
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 RightPanelStore from "../../stores/RightPanelStore";
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
import {Action} from "../../dispatcher/actions";
|
import {Action} from "../../dispatcher/actions";
|
||||||
|
@ -75,8 +75,8 @@ export default class RightPanel extends React.Component {
|
||||||
const userForPanel = this._getUserForPanel();
|
const userForPanel = this._getUserForPanel();
|
||||||
if (this.props.groupId) {
|
if (this.props.groupId) {
|
||||||
if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.groupPanelPhase)) {
|
if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.groupPanelPhase)) {
|
||||||
dis.dispatch({action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.GroupMemberList});
|
dis.dispatch({action: Action.SetRightPanelPhase, phase: RightPanelPhases.GroupMemberList});
|
||||||
return RIGHT_PANEL_PHASES.GroupMemberList;
|
return RightPanelPhases.GroupMemberList;
|
||||||
}
|
}
|
||||||
return rps.groupPanelPhase;
|
return rps.groupPanelPhase;
|
||||||
} else if (userForPanel) {
|
} else if (userForPanel) {
|
||||||
|
@ -98,11 +98,11 @@ export default class RightPanel extends React.Component {
|
||||||
) {
|
) {
|
||||||
return rps.roomPanelPhase;
|
return rps.roomPanelPhase;
|
||||||
}
|
}
|
||||||
return RIGHT_PANEL_PHASES.RoomMemberInfo;
|
return RightPanelPhases.RoomMemberInfo;
|
||||||
} else {
|
} else {
|
||||||
if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.roomPanelPhase)) {
|
if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.roomPanelPhase)) {
|
||||||
dis.dispatch({action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.RoomMemberList});
|
dis.dispatch({action: Action.SetRightPanelPhase, phase: RightPanelPhases.RoomMemberList});
|
||||||
return RIGHT_PANEL_PHASES.RoomMemberList;
|
return RightPanelPhases.RoomMemberList;
|
||||||
}
|
}
|
||||||
return rps.roomPanelPhase;
|
return rps.roomPanelPhase;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ export default class RightPanel extends React.Component {
|
||||||
onInviteToGroupButtonClick() {
|
onInviteToGroupButtonClick() {
|
||||||
showGroupInviteDialog(this.props.groupId).then(() => {
|
showGroupInviteDialog(this.props.groupId).then(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: RIGHT_PANEL_PHASES.GroupMemberList,
|
phase: RightPanelPhases.GroupMemberList,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -165,9 +165,9 @@ export default class RightPanel extends React.Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// redraw the badge on the membership list
|
// 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();
|
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) {
|
member.userId === this.state.member.userId) {
|
||||||
// refresh the member info (e.g. new power level)
|
// refresh the member info (e.g. new power level)
|
||||||
this._delayedUpdate();
|
this._delayedUpdate();
|
||||||
|
@ -175,7 +175,7 @@ export default class RightPanel extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction(payload) {
|
onAction(payload) {
|
||||||
if (payload.action === "after_right_panel_phase_change") {
|
if (payload.action === Action.AfterRightPanelPhaseChange) {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: payload.phase,
|
phase: payload.phase,
|
||||||
groupRoomId: payload.groupRoomId,
|
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.
|
// or the member list if we were in the member panel... phew.
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.ViewUser,
|
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 />;
|
let panel = <div />;
|
||||||
|
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case RIGHT_PANEL_PHASES.RoomMemberList:
|
case RightPanelPhases.RoomMemberList:
|
||||||
if (this.props.roomId) {
|
if (this.props.roomId) {
|
||||||
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />;
|
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.GroupMemberList:
|
case RightPanelPhases.GroupMemberList:
|
||||||
if (this.props.groupId) {
|
if (this.props.groupId) {
|
||||||
panel = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
|
panel = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.GroupRoomList:
|
case RightPanelPhases.GroupRoomList:
|
||||||
panel = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
|
panel = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.RoomMemberInfo:
|
case RightPanelPhases.RoomMemberInfo:
|
||||||
case RIGHT_PANEL_PHASES.EncryptionPanel:
|
case RightPanelPhases.EncryptionPanel:
|
||||||
panel = <UserInfo
|
panel = <UserInfo
|
||||||
user={this.state.member}
|
user={this.state.member}
|
||||||
roomId={this.props.roomId}
|
roomId={this.props.roomId}
|
||||||
|
@ -250,26 +250,26 @@ export default class RightPanel extends React.Component {
|
||||||
verificationRequestPromise={this.state.verificationRequestPromise}
|
verificationRequestPromise={this.state.verificationRequestPromise}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.Room3pidMemberInfo:
|
case RightPanelPhases.Room3pidMemberInfo:
|
||||||
panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.roomId} />;
|
panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.roomId} />;
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.GroupMemberInfo:
|
case RightPanelPhases.GroupMemberInfo:
|
||||||
panel = <UserInfo
|
panel = <UserInfo
|
||||||
user={this.state.member}
|
user={this.state.member}
|
||||||
groupId={this.props.groupId}
|
groupId={this.props.groupId}
|
||||||
key={this.state.member.userId}
|
key={this.state.member.userId}
|
||||||
onClose={this.onCloseUserInfo} />;
|
onClose={this.onCloseUserInfo} />;
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.GroupRoomInfo:
|
case RightPanelPhases.GroupRoomInfo:
|
||||||
panel = <GroupRoomInfo
|
panel = <GroupRoomInfo
|
||||||
groupRoomId={this.state.groupRoomId}
|
groupRoomId={this.state.groupRoomId}
|
||||||
groupId={this.props.groupId}
|
groupId={this.props.groupId}
|
||||||
key={this.state.groupRoomId} />;
|
key={this.state.groupRoomId} />;
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.NotificationPanel:
|
case RightPanelPhases.NotificationPanel:
|
||||||
panel = <NotificationPanel />;
|
panel = <NotificationPanel />;
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.FilePanel:
|
case RightPanelPhases.FilePanel:
|
||||||
panel = <FilePanel roomId={this.props.roomId} resizeNotifier={this.props.resizeNotifier} />;
|
panel = <FilePanel roomId={this.props.roomId} resizeNotifier={this.props.resizeNotifier} />;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,9 @@ import GroupStore from '../../../stores/GroupStore';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { showGroupInviteDialog } from '../../../GroupAddressPicker';
|
import { showGroupInviteDialog } from '../../../GroupAddressPicker';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
||||||
|
|
||||||
|
@ -164,9 +165,9 @@ export default createReactClass({
|
||||||
onInviteToGroupButtonClick() {
|
onInviteToGroupButtonClick() {
|
||||||
showGroupInviteDialog(this.props.groupId).then(() => {
|
showGroupInviteDialog(this.props.groupId).then(() => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'set_right_panel_phase',
|
action: Action.SetRightPanelPhase,
|
||||||
phase: RIGHT_PANEL_PHASES.GroupMemberList,
|
phase: RightPanelPhases.GroupMemberList,
|
||||||
groupId: this.props.groupId,
|
refireParams: { groupId: this.props.groupId },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,7 +22,8 @@ import { _t } from '../../../languageHandler';
|
||||||
import {getNameForEventRoom, userLabelForEventRoom}
|
import {getNameForEventRoom, userLabelForEventRoom}
|
||||||
from '../../../utils/KeyVerificationStateObserver';
|
from '../../../utils/KeyVerificationStateObserver';
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
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 {
|
export default class MKeyVerificationRequest extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -48,8 +49,8 @@ export default class MKeyVerificationRequest extends React.Component {
|
||||||
const {verificationRequest} = this.props.mxEvent;
|
const {verificationRequest} = this.props.mxEvent;
|
||||||
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
|
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "set_right_panel_phase",
|
action: Action.SetRightPanelPhase,
|
||||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
phase: RightPanelPhases.EncryptionPanel,
|
||||||
refireParams: {verificationRequest, member},
|
refireParams: {verificationRequest, member},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,10 +15,10 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
|
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
|
||||||
|
|
||||||
export const PendingActionSpinner = ({text}) => {
|
export const PendingActionSpinner = ({text}) => {
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
|
@ -28,7 +28,17 @@ export const PendingActionSpinner = ({text}) => {
|
||||||
</div>;
|
</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,
|
waitingForOtherParty,
|
||||||
waitingForNetwork,
|
waitingForNetwork,
|
||||||
member,
|
member,
|
||||||
|
@ -36,10 +46,10 @@ const EncryptionInfo = ({
|
||||||
isRoomEncrypted,
|
isRoomEncrypted,
|
||||||
inDialog,
|
inDialog,
|
||||||
isSelfVerification,
|
isSelfVerification,
|
||||||
}) => {
|
}: IProps) => {
|
||||||
let content;
|
let content: JSX.Element;
|
||||||
if (waitingForOtherParty || waitingForNetwork) {
|
if (waitingForOtherParty || waitingForNetwork) {
|
||||||
let text;
|
let text: string;
|
||||||
if (waitingForOtherParty) {
|
if (waitingForOtherParty) {
|
||||||
if (isSelfVerification) {
|
if (isSelfVerification) {
|
||||||
text = _t("Waiting for you to accept on your other session…");
|
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) {
|
if (isRoomEncrypted) {
|
||||||
description = (
|
description = (
|
||||||
<div>
|
<div>
|
||||||
|
@ -97,10 +107,5 @@ const EncryptionInfo = ({
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
};
|
};
|
||||||
EncryptionInfo.propTypes = {
|
|
||||||
member: PropTypes.object.isRequired,
|
|
||||||
onStartVerification: PropTypes.func.isRequired,
|
|
||||||
request: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EncryptionInfo;
|
export default EncryptionInfo;
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useCallback, useEffect, useState} from "react";
|
import React, {useCallback, useEffect, useState} from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import EncryptionInfo from "./EncryptionInfo";
|
import EncryptionInfo from "./EncryptionInfo";
|
||||||
import VerificationPanel from "./VerificationPanel";
|
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 {PHASE_REQUESTED, PHASE_UNSENT} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import {_t} from "../../../languageHandler";
|
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
|
// cancellation codes which constitute a key mismatch
|
||||||
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
|
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 {verificationRequest, verificationRequestPromise, member, onClose, layout, isRoomEncrypted} = props;
|
||||||
const [request, setRequest] = useState(verificationRequest);
|
const [request, setRequest] = useState(verificationRequest);
|
||||||
// state to show a spinner immediately after clicking "start verification",
|
// state to show a spinner immediately after clicking "start verification",
|
||||||
|
@ -48,10 +59,10 @@ const EncryptionPanel = (props) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function awaitPromise() {
|
async function awaitPromise() {
|
||||||
setRequesting(true);
|
setRequesting(true);
|
||||||
const request = await verificationRequestPromise;
|
const requestFromPromise = await verificationRequestPromise;
|
||||||
setRequesting(false);
|
setRequesting(false);
|
||||||
setRequest(request);
|
setRequest(requestFromPromise);
|
||||||
setPhase(request.phase);
|
setPhase(requestFromPromise.phase);
|
||||||
}
|
}
|
||||||
if (verificationRequestPromise) {
|
if (verificationRequestPromise) {
|
||||||
awaitPromise();
|
awaitPromise();
|
||||||
|
@ -90,7 +101,7 @@ const EncryptionPanel = (props) => {
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request]);
|
||||||
|
|
||||||
let cancelButton;
|
let cancelButton: JSX.Element;
|
||||||
if (layout !== "dialog" && request && request.pending) {
|
if (layout !== "dialog" && request && request.pending) {
|
||||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
cancelButton = (<AccessibleButton
|
cancelButton = (<AccessibleButton
|
||||||
|
@ -104,9 +115,9 @@ const EncryptionPanel = (props) => {
|
||||||
setRequesting(true);
|
setRequesting(true);
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const roomId = await ensureDMExists(cli, member.userId);
|
const roomId = await ensureDMExists(cli, member.userId);
|
||||||
const verificationRequest = await cli.requestVerificationDM(member.userId, roomId);
|
const verificationRequest_ = await cli.requestVerificationDM(member.userId, roomId);
|
||||||
setRequest(verificationRequest);
|
setRequest(verificationRequest_);
|
||||||
setPhase(verificationRequest.phase);
|
setPhase(verificationRequest_.phase);
|
||||||
}, [member.userId]);
|
}, [member.userId]);
|
||||||
|
|
||||||
const requested =
|
const requested =
|
||||||
|
@ -144,12 +155,5 @@ const EncryptionPanel = (props) => {
|
||||||
</React.Fragment>);
|
</React.Fragment>);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
EncryptionPanel.propTypes = {
|
|
||||||
member: PropTypes.object.isRequired,
|
|
||||||
onClose: PropTypes.func.isRequired,
|
|
||||||
verificationRequest: PropTypes.object,
|
|
||||||
layout: PropTypes.string,
|
|
||||||
inDialog: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EncryptionPanel;
|
export default EncryptionPanel;
|
|
@ -21,65 +21,68 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import HeaderButton from './HeaderButton';
|
import HeaderButton from './HeaderButton';
|
||||||
import HeaderButtons, {HEADER_KIND_GROUP} from './HeaderButtons';
|
import HeaderButtons, {HeaderKind} from './HeaderButtons';
|
||||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
import {Action} from "../../../dispatcher/actions";
|
import {Action} from "../../../dispatcher/actions";
|
||||||
import {ActionPayload} from "../../../dispatcher/payloads";
|
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||||
|
import {ViewUserPayload} from "../../../dispatcher/payloads/ViewUserPayload";
|
||||||
|
|
||||||
const GROUP_PHASES = [
|
const GROUP_PHASES = [
|
||||||
RIGHT_PANEL_PHASES.GroupMemberInfo,
|
RightPanelPhases.GroupMemberInfo,
|
||||||
RIGHT_PANEL_PHASES.GroupMemberList,
|
RightPanelPhases.GroupMemberList,
|
||||||
];
|
];
|
||||||
const ROOM_PHASES = [
|
const ROOM_PHASES = [
|
||||||
RIGHT_PANEL_PHASES.GroupRoomList,
|
RightPanelPhases.GroupRoomList,
|
||||||
RIGHT_PANEL_PHASES.GroupRoomInfo,
|
RightPanelPhases.GroupRoomInfo,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
interface IProps {}
|
||||||
|
|
||||||
export default class GroupHeaderButtons extends HeaderButtons {
|
export default class GroupHeaderButtons extends HeaderButtons {
|
||||||
constructor(props) {
|
constructor(props: IProps) {
|
||||||
super(props, HEADER_KIND_GROUP);
|
super(props, HeaderKind.Group);
|
||||||
this._onMembersClicked = this._onMembersClicked.bind(this);
|
this.onMembersClicked = this.onMembersClicked.bind(this);
|
||||||
this._onRoomsClicked = this._onRoomsClicked.bind(this);
|
this.onRoomsClicked = this.onRoomsClicked.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction(payload: ActionPayload) {
|
protected onAction(payload: ActionPayload) {
|
||||||
super.onAction(payload);
|
super.onAction(payload);
|
||||||
|
|
||||||
if (payload.action === Action.ViewUser) {
|
if (payload.action === Action.ViewUser) {
|
||||||
if (payload.member) {
|
if ((payload as ViewUserPayload).member) {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member});
|
||||||
} else {
|
} else {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList);
|
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||||
}
|
}
|
||||||
} else if (payload.action === "view_group") {
|
} else if (payload.action === "view_group") {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList);
|
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||||
} else if (payload.action === "view_group_room") {
|
} else if (payload.action === "view_group_room") {
|
||||||
this.setPhase(
|
this.setPhase(
|
||||||
RIGHT_PANEL_PHASES.GroupRoomInfo,
|
RightPanelPhases.GroupRoomInfo,
|
||||||
{groupRoomId: payload.groupRoomId, groupId: payload.groupId},
|
{groupRoomId: payload.groupRoomId, groupId: payload.groupId},
|
||||||
);
|
);
|
||||||
} else if (payload.action === "view_group_room_list") {
|
} 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") {
|
} 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") {
|
} else if (payload.action === "view_group_user") {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberInfo, {member: payload.member});
|
this.setPhase(RightPanelPhases.GroupMemberInfo, {member: payload.member});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMembersClicked() {
|
private onMembersClicked() {
|
||||||
if (this.state.phase === RIGHT_PANEL_PHASES.GroupMemberInfo) {
|
if (this.state.phase === RightPanelPhases.GroupMemberInfo) {
|
||||||
// send the active phase to trigger a toggle
|
// send the active phase to trigger a toggle
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.GroupMemberInfo);
|
this.setPhase(RightPanelPhases.GroupMemberInfo);
|
||||||
} else {
|
} else {
|
||||||
// This toggles for us, if needed
|
// 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 toggles for us, if needed
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.GroupRoomList);
|
this.setPhase(RightPanelPhases.GroupRoomList);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderButtons() {
|
renderButtons() {
|
||||||
|
@ -87,13 +90,13 @@ export default class GroupHeaderButtons extends HeaderButtons {
|
||||||
<HeaderButton key="groupMembersButton" name="groupMembersButton"
|
<HeaderButton key="groupMembersButton" name="groupMembersButton"
|
||||||
title={_t('Members')}
|
title={_t('Members')}
|
||||||
isHighlighted={this.isPhase(GROUP_PHASES)}
|
isHighlighted={this.isPhase(GROUP_PHASES)}
|
||||||
onClick={this._onMembersClicked}
|
onClick={this.onMembersClicked}
|
||||||
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
<HeaderButton key="roomsButton" name="roomsButton"
|
<HeaderButton key="roomsButton" name="roomsButton"
|
||||||
title={_t('Rooms')}
|
title={_t('Rooms')}
|
||||||
isHighlighted={this.isPhase(ROOM_PHASES)}
|
isHighlighted={this.isPhase(ROOM_PHASES)}
|
||||||
onClick={this._onRoomsClicked}
|
onClick={this.onRoomsClicked}
|
||||||
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
];
|
];
|
|
@ -19,23 +19,38 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Analytics from '../../../Analytics';
|
import Analytics from '../../../Analytics';
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
|
||||||
export default class HeaderButton extends React.Component {
|
interface IProps {
|
||||||
constructor() {
|
// Whether this button is highlighted
|
||||||
super();
|
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);
|
this.onClick = this.onClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(ev) {
|
private onClick() {
|
||||||
Analytics.trackEvent(...this.props.analytics);
|
Analytics.trackEvent(...this.props.analytics);
|
||||||
this.props.onClick();
|
this.props.onClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
public render() {
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
mx_RightPanel_headerButton: true,
|
mx_RightPanel_headerButton: true,
|
||||||
mx_RightPanel_headerButton_highlight: this.props.isHighlighted,
|
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 React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import HeaderButton from './HeaderButton';
|
import HeaderButton from './HeaderButton';
|
||||||
import HeaderButtons, {HEADER_KIND_ROOM} from './HeaderButtons';
|
import HeaderButtons, {HeaderKind} from './HeaderButtons';
|
||||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
import {Action} from "../../../dispatcher/actions";
|
import {Action} from "../../../dispatcher/actions";
|
||||||
import {ActionPayload} from "../../../dispatcher/payloads";
|
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||||
|
|
||||||
const MEMBER_PHASES = [
|
const MEMBER_PHASES = [
|
||||||
RIGHT_PANEL_PHASES.RoomMemberList,
|
RightPanelPhases.RoomMemberList,
|
||||||
RIGHT_PANEL_PHASES.RoomMemberInfo,
|
RightPanelPhases.RoomMemberInfo,
|
||||||
RIGHT_PANEL_PHASES.EncryptionPanel,
|
RightPanelPhases.EncryptionPanel,
|
||||||
RIGHT_PANEL_PHASES.Room3pidMemberInfo,
|
RightPanelPhases.Room3pidMemberInfo,
|
||||||
];
|
];
|
||||||
|
|
||||||
export default class RoomHeaderButtons extends HeaderButtons {
|
export default class RoomHeaderButtons extends HeaderButtons {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props, HEADER_KIND_ROOM);
|
super(props, HeaderKind.Room);
|
||||||
this._onMembersClicked = this._onMembersClicked.bind(this);
|
this.onMembersClicked = this.onMembersClicked.bind(this);
|
||||||
this._onFilesClicked = this._onFilesClicked.bind(this);
|
this.onFilesClicked = this.onFilesClicked.bind(this);
|
||||||
this._onNotificationsClicked = this._onNotificationsClicked.bind(this);
|
this.onNotificationsClicked = this.onNotificationsClicked.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction(payload: ActionPayload) {
|
protected onAction(payload: ActionPayload) {
|
||||||
super.onAction(payload);
|
super.onAction(payload);
|
||||||
if (payload.action === Action.ViewUser) {
|
if (payload.action === Action.ViewUser) {
|
||||||
if (payload.member) {
|
if (payload.member) {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member});
|
||||||
} else {
|
} else {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList);
|
this.setPhase(RightPanelPhases.RoomMemberList);
|
||||||
}
|
}
|
||||||
} else if (payload.action === "view_3pid_invite") {
|
} else if (payload.action === "view_3pid_invite") {
|
||||||
if (payload.event) {
|
if (payload.event) {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.Room3pidMemberInfo, {event: payload.event});
|
this.setPhase(RightPanelPhases.Room3pidMemberInfo, {event: payload.event});
|
||||||
} else {
|
} else {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList);
|
this.setPhase(RightPanelPhases.RoomMemberList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMembersClicked() {
|
private onMembersClicked() {
|
||||||
if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberInfo) {
|
if (this.state.phase === RightPanelPhases.RoomMemberInfo) {
|
||||||
// send the active phase to trigger a toggle
|
// 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
|
// 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 {
|
} else {
|
||||||
// This toggles for us, if needed
|
// 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 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 toggles for us, if needed
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.NotificationPanel);
|
this.setPhase(RightPanelPhases.NotificationPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderButtons() {
|
public renderButtons() {
|
||||||
return [
|
return [
|
||||||
<HeaderButton key="membersButton" name="membersButton"
|
<HeaderButton key="membersButton" name="membersButton"
|
||||||
title={_t('Members')}
|
title={_t('Members')}
|
||||||
isHighlighted={this.isPhase(MEMBER_PHASES)}
|
isHighlighted={this.isPhase(MEMBER_PHASES)}
|
||||||
onClick={this._onMembersClicked}
|
onClick={this.onMembersClicked}
|
||||||
analytics={['Right Panel', 'Member List Button', 'click']}
|
analytics={['Right Panel', 'Member List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
<HeaderButton key="filesButton" name="filesButton"
|
<HeaderButton key="filesButton" name="filesButton"
|
||||||
title={_t('Files')}
|
title={_t('Files')}
|
||||||
isHighlighted={this.isPhase(RIGHT_PANEL_PHASES.FilePanel)}
|
isHighlighted={this.isPhase(RightPanelPhases.FilePanel)}
|
||||||
onClick={this._onFilesClicked}
|
onClick={this.onFilesClicked}
|
||||||
analytics={['Right Panel', 'File List Button', 'click']}
|
analytics={['Right Panel', 'File List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
<HeaderButton key="notifsButton" name="notifsButton"
|
<HeaderButton key="notifsButton" name="notifsButton"
|
||||||
title={_t('Notifications')}
|
title={_t('Notifications')}
|
||||||
isHighlighted={this.isPhase(RIGHT_PANEL_PHASES.NotificationPanel)}
|
isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)}
|
||||||
onClick={this._onNotificationsClicked}
|
onClick={this.onNotificationsClicked}
|
||||||
analytics={['Right Panel', 'Notification List Button', 'click']}
|
analytics={['Right Panel', 'Notification List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
];
|
];
|
|
@ -40,7 +40,7 @@ import E2EIcon from "../rooms/E2EIcon";
|
||||||
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
||||||
import {textualPowerLevel} from '../../../Roles';
|
import {textualPowerLevel} from '../../../Roles';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
import EncryptionPanel from "./EncryptionPanel";
|
import EncryptionPanel from "./EncryptionPanel";
|
||||||
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
|
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
|
||||||
import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification';
|
import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification';
|
||||||
|
@ -1480,7 +1480,7 @@ const UserInfoHeader = ({onClose, member, e2eStatus}) => {
|
||||||
</React.Fragment>;
|
</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);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
// Load room if we are given a room id and memoize it
|
// 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;
|
let content;
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case RIGHT_PANEL_PHASES.RoomMemberInfo:
|
case RightPanelPhases.RoomMemberInfo:
|
||||||
case RIGHT_PANEL_PHASES.GroupMemberInfo:
|
case RightPanelPhases.GroupMemberInfo:
|
||||||
content = (
|
content = (
|
||||||
<BasicUserInfo
|
<BasicUserInfo
|
||||||
room={room}
|
room={room}
|
||||||
|
@ -1511,7 +1511,7 @@ const UserInfo = ({user, groupId, roomId, onClose, phase=RIGHT_PANEL_PHASES.Room
|
||||||
isRoomEncrypted={isRoomEncrypted} />
|
isRoomEncrypted={isRoomEncrypted} />
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case RIGHT_PANEL_PHASES.EncryptionPanel:
|
case RightPanelPhases.EncryptionPanel:
|
||||||
classes.push("mx_UserInfo_smallAvatar");
|
classes.push("mx_UserInfo_smallAvatar");
|
||||||
content = (
|
content = (
|
||||||
<EncryptionPanel {...props} member={member} onClose={onClose} isRoomEncrypted={isRoomEncrypted} />
|
<EncryptionPanel {...props} member={member} onClose={onClose} isRoomEncrypted={isRoomEncrypted} />
|
||||||
|
|
|
@ -15,12 +15,15 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||||
import {SCAN_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
|
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 VerificationQRCode from "../elements/crypto/VerificationQRCode";
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
|
@ -36,37 +39,51 @@ import {
|
||||||
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||||
import Spinner from "../elements/Spinner";
|
import Spinner from "../elements/Spinner";
|
||||||
|
|
||||||
export default class VerificationPanel extends React.PureComponent {
|
// XXX: Should be defined in matrix-js-sdk
|
||||||
static propTypes = {
|
enum VerificationPhase {
|
||||||
layout: PropTypes.string,
|
PHASE_UNSENT,
|
||||||
request: PropTypes.object.isRequired,
|
PHASE_REQUESTED,
|
||||||
member: PropTypes.object.isRequired,
|
PHASE_READY,
|
||||||
phase: PropTypes.oneOf([
|
PHASE_DONE,
|
||||||
PHASE_UNSENT,
|
PHASE_STARTED,
|
||||||
PHASE_REQUESTED,
|
PHASE_CANCELLED,
|
||||||
PHASE_READY,
|
}
|
||||||
PHASE_STARTED,
|
|
||||||
PHASE_CANCELLED,
|
|
||||||
PHASE_DONE,
|
|
||||||
]).isRequired,
|
|
||||||
onClose: PropTypes.func.isRequired,
|
|
||||||
isRoomEncrypted: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
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);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
this._hasVerifier = false;
|
this.hasVerifier = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderQRPhase() {
|
private renderQRPhase() {
|
||||||
const {member, request} = this.props;
|
const {member, request} = this.props;
|
||||||
const showSAS = request.otherPartySupportsMethod(verificationMethods.SAS);
|
const showSAS: boolean = request.otherPartySupportsMethod(verificationMethods.SAS);
|
||||||
const showQR = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
|
|
||||||
const noCommonMethodError = !showSAS && !showQR ?
|
const noCommonMethodError: JSX.Element = !showSAS && !showQR ?
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"The session you are trying to verify doesn't support scanning a " +
|
"The session you are trying to verify doesn't support scanning a " +
|
||||||
"QR code or emoji verification, which is what %(brand)s supports. Try " +
|
"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') {
|
if (this.props.layout === 'dialog') {
|
||||||
// HACK: This is a terrible idea.
|
// HACK: This is a terrible idea.
|
||||||
let qrBlock;
|
let qrBlockDialog: JSX.Element;
|
||||||
let sasBlock;
|
let sasBlockDialog: JSX.Element;
|
||||||
if (showQR) {
|
if (showQR) {
|
||||||
qrBlock =
|
qrBlockDialog =
|
||||||
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
||||||
<p>{_t("Scan this unique code")}</p>
|
<p>{_t("Scan this unique code")}</p>
|
||||||
<VerificationQRCode qrCodeData={request.qrCodeData} />
|
<VerificationQRCode qrCodeData={request.qrCodeData} />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
if (showSAS) {
|
if (showSAS) {
|
||||||
sasBlock =
|
sasBlockDialog =
|
||||||
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
||||||
<p>{_t("Compare unique emoji")}</p>
|
<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>
|
<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")}
|
{_t("Start")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
const or = qrBlock && sasBlock ?
|
const or = qrBlockDialog && sasBlockDialog ?
|
||||||
<div className='mx_VerificationPanel_QRPhase_betweenText'>{_t("or")}</div> : null;
|
<div className='mx_VerificationPanel_QRPhase_betweenText'>{_t("or")}</div> : null;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{_t("Verify this session by completing one of the following:")}
|
{_t("Verify this session by completing one of the following:")}
|
||||||
<div className='mx_VerificationPanel_QRPhase_startOptions'>
|
<div className='mx_VerificationPanel_QRPhase_startOptions'>
|
||||||
{qrBlock}
|
{qrBlockDialog}
|
||||||
{or}
|
{or}
|
||||||
{sasBlock}
|
{sasBlockDialog}
|
||||||
{noCommonMethodError}
|
{noCommonMethodError}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let qrBlock;
|
let qrBlock: JSX.Element;
|
||||||
if (showQR) {
|
if (showQR) {
|
||||||
qrBlock = <div className="mx_UserInfo_container">
|
qrBlock = <div className="mx_UserInfo_container">
|
||||||
<h3>{_t("Verify by scanning")}</h3>
|
<h3>{_t("Verify by scanning")}</h3>
|
||||||
|
@ -125,7 +142,7 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sasBlock;
|
let sasBlock: JSX.Element;
|
||||||
if (showSAS) {
|
if (showSAS) {
|
||||||
const disabled = this.state.emojiButtonClicked;
|
const disabled = this.state.emojiButtonClicked;
|
||||||
const sasLabel = showQR ?
|
const sasLabel = showQR ?
|
||||||
|
@ -140,7 +157,7 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
kind="primary"
|
kind="primary"
|
||||||
className="mx_UserInfo_wideButton mx_VerificationPanel_verifyByEmojiButton"
|
className="mx_UserInfo_wideButton mx_VerificationPanel_verifyByEmojiButton"
|
||||||
onClick={this._startSAS}
|
onClick={this.startSAS}
|
||||||
>
|
>
|
||||||
{_t("Verify by emoji")}
|
{_t("Verify by emoji")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
@ -159,22 +176,22 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onReciprocateYesClick = () => {
|
private onReciprocateYesClick = () => {
|
||||||
this.setState({reciprocateButtonClicked: true});
|
this.setState({reciprocateButtonClicked: true});
|
||||||
this.state.reciprocateQREvent.confirm();
|
this.state.reciprocateQREvent.confirm();
|
||||||
};
|
};
|
||||||
|
|
||||||
_onReciprocateNoClick = () => {
|
private onReciprocateNoClick = () => {
|
||||||
this.setState({reciprocateButtonClicked: true});
|
this.setState({reciprocateButtonClicked: true});
|
||||||
this.state.reciprocateQREvent.cancel();
|
this.state.reciprocateQREvent.cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
_getDevice() {
|
private getDevice() {
|
||||||
const deviceId = this.props.request && this.props.request.channel.deviceId;
|
const deviceId = this.props.request && this.props.request.channel.deviceId;
|
||||||
return MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId);
|
return MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderQRReciprocatePhase() {
|
private renderQRReciprocatePhase() {
|
||||||
const {member, request} = this.props;
|
const {member, request} = this.props;
|
||||||
let Button;
|
let Button;
|
||||||
// a bit of a hack, but the FormButton should only be used in the right panel
|
// 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?", {
|
_t("Almost there! Is %(displayName)s showing the same shield?", {
|
||||||
displayName: member.displayName || member.name || member.userId,
|
displayName: member.displayName || member.name || member.userId,
|
||||||
});
|
});
|
||||||
let body;
|
let body: JSX.Element;
|
||||||
if (this.state.reciprocateQREvent) {
|
if (this.state.reciprocateQREvent) {
|
||||||
// riot web doesn't support scanning yet, so assume here we're the client being scanned.
|
// 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
|
<Button
|
||||||
label={_t("No")} kind="danger"
|
label={_t("No")} kind="danger"
|
||||||
disabled={this.state.reciprocateButtonClicked}
|
disabled={this.state.reciprocateButtonClicked}
|
||||||
onClick={this._onReciprocateNoClick}>{_t("No")}</Button>
|
onClick={this.onReciprocateNoClick}>{_t("No")}</Button>
|
||||||
<Button
|
<Button
|
||||||
label={_t("Yes")} kind="primary"
|
label={_t("Yes")} kind="primary"
|
||||||
disabled={this.state.reciprocateButtonClicked}
|
disabled={this.state.reciprocateButtonClicked}
|
||||||
onClick={this._onReciprocateYesClick}>{_t("Yes")}</Button>
|
onClick={this.onReciprocateYesClick}>{_t("Yes")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,10 +235,10 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderVerifiedPhase() {
|
private renderVerifiedPhase() {
|
||||||
const {member, request} = this.props;
|
const {member, request} = this.props;
|
||||||
|
|
||||||
let text;
|
let text: string;
|
||||||
if (!request.isSelfVerification) {
|
if (!request.isSelfVerification) {
|
||||||
if (this.props.isRoomEncrypted) {
|
if (this.props.isRoomEncrypted) {
|
||||||
text = _t("Verify all users in a room to ensure it's secure.");
|
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) {
|
if (request.isSelfVerification) {
|
||||||
const device = this._getDevice();
|
const device = this.getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
// This can happen if the device is logged out while we're still showing verification
|
// This can happen if the device is logged out while we're still showing verification
|
||||||
// UI for it.
|
// UI for it.
|
||||||
|
@ -264,19 +281,19 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCancelledPhase() {
|
private renderCancelledPhase() {
|
||||||
const {member, request} = this.props;
|
const {member, request} = this.props;
|
||||||
|
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
let startAgainInstruction;
|
let startAgainInstruction: string;
|
||||||
if (request.isSelfVerification) {
|
if (request.isSelfVerification) {
|
||||||
startAgainInstruction = _t("Start verification again from the notification.");
|
startAgainInstruction = _t("Start verification again from the notification.");
|
||||||
} else {
|
} else {
|
||||||
startAgainInstruction = _t("Start verification again from their profile.");
|
startAgainInstruction = _t("Start verification again from their profile.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let text;
|
let text: string;
|
||||||
if (request.cancellationCode === "m.timeout") {
|
if (request.cancellationCode === "m.timeout") {
|
||||||
text = _t("Verification timed out.") + ` ${startAgainInstruction}`;
|
text = _t("Verification timed out.") + ` ${startAgainInstruction}`;
|
||||||
} else if (request.cancellingUserId === request.otherUserId) {
|
} 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 {member, phase, request} = this.props;
|
||||||
|
|
||||||
const displayName = member.displayName || member.name || member.userId;
|
const displayName = member.displayName || member.name || member.userId;
|
||||||
|
@ -321,10 +338,10 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
const emojis = this.state.sasEvent ?
|
const emojis = this.state.sasEvent ?
|
||||||
<VerificationShowSas
|
<VerificationShowSas
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
device={this._getDevice()}
|
device={this.getDevice()}
|
||||||
sas={this.state.sasEvent.sas}
|
sas={this.state.sasEvent.sas}
|
||||||
onCancel={this._onSasMismatchesClick}
|
onCancel={this.onSasMismatchesClick}
|
||||||
onDone={this._onSasMatchesClick}
|
onDone={this.onSasMatchesClick}
|
||||||
inDialog={this.props.inDialog}
|
inDialog={this.props.inDialog}
|
||||||
isSelf={request.isSelfVerification}
|
isSelf={request.isSelfVerification}
|
||||||
/> : <Spinner />;
|
/> : <Spinner />;
|
||||||
|
@ -345,7 +362,7 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_startSAS = async () => {
|
private startSAS = async () => {
|
||||||
this.setState({emojiButtonClicked: true});
|
this.setState({emojiButtonClicked: true});
|
||||||
const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS);
|
const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS);
|
||||||
try {
|
try {
|
||||||
|
@ -355,31 +372,31 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_onSasMatchesClick = () => {
|
private onSasMatchesClick = () => {
|
||||||
this.state.sasEvent.confirm();
|
this.state.sasEvent.confirm();
|
||||||
};
|
};
|
||||||
|
|
||||||
_onSasMismatchesClick = () => {
|
private onSasMismatchesClick = () => {
|
||||||
this.state.sasEvent.mismatch();
|
this.state.sasEvent.mismatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
_updateVerifierState = () => {
|
private updateVerifierState = () => {
|
||||||
const {request} = this.props;
|
const {request} = this.props;
|
||||||
const {sasEvent, reciprocateQREvent} = request.verifier;
|
const {sasEvent, reciprocateQREvent} = request.verifier;
|
||||||
request.verifier.off('show_sas', this._updateVerifierState);
|
request.verifier.off('show_sas', this.updateVerifierState);
|
||||||
request.verifier.off('show_reciprocate_qr', this._updateVerifierState);
|
request.verifier.off('show_reciprocate_qr', this.updateVerifierState);
|
||||||
this.setState({sasEvent, reciprocateQREvent});
|
this.setState({sasEvent, reciprocateQREvent});
|
||||||
};
|
};
|
||||||
|
|
||||||
_onRequestChange = async () => {
|
private onRequestChange = async () => {
|
||||||
const {request} = this.props;
|
const {request} = this.props;
|
||||||
const hadVerifier = this._hasVerifier;
|
const hadVerifier = this.hasVerifier;
|
||||||
this._hasVerifier = !!request.verifier;
|
this.hasVerifier = !!request.verifier;
|
||||||
if (!hadVerifier && this._hasVerifier) {
|
if (!hadVerifier && this.hasVerifier) {
|
||||||
request.verifier.on('show_sas', this._updateVerifierState);
|
request.verifier.on('show_sas', this.updateVerifierState);
|
||||||
request.verifier.on('show_reciprocate_qr', this._updateVerifierState);
|
request.verifier.on('show_reciprocate_qr', this.updateVerifierState);
|
||||||
try {
|
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.
|
// but that's ok as verify should return the same promise.
|
||||||
await request.verifier.verify();
|
await request.verifier.verify();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -388,23 +405,22 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount() {
|
||||||
const {request} = this.props;
|
const {request} = this.props;
|
||||||
request.on("change", this._onRequestChange);
|
request.on("change", this.onRequestChange);
|
||||||
if (request.verifier) {
|
if (request.verifier) {
|
||||||
const {request} = this.props;
|
|
||||||
const {sasEvent, reciprocateQREvent} = request.verifier;
|
const {sasEvent, reciprocateQREvent} = request.verifier;
|
||||||
this.setState({sasEvent, reciprocateQREvent});
|
this.setState({sasEvent, reciprocateQREvent});
|
||||||
}
|
}
|
||||||
this._onRequestChange();
|
this.onRequestChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
const {request} = this.props;
|
const {request} = this.props;
|
||||||
if (request.verifier) {
|
if (request.verifier) {
|
||||||
request.verifier.off('show_sas', this._updateVerifierState);
|
request.verifier.off('show_sas', this.updateVerifierState);
|
||||||
request.verifier.off('show_reciprocate_qr', 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 {ContextMenu} from "../../structures/ContextMenu";
|
||||||
import {WidgetType} from "../../../widgets/WidgetType";
|
import {WidgetType} from "../../../widgets/WidgetType";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
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).
|
// 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.
|
// 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":
|
case "stickerpicker_close":
|
||||||
this.setState({showStickers: false});
|
this.setState({showStickers: false});
|
||||||
break;
|
break;
|
||||||
case "after_right_panel_phase_change":
|
case Action.AfterRightPanelPhaseChange:
|
||||||
case "show_left_panel":
|
case "show_left_panel":
|
||||||
case "hide_left_panel":
|
case "hide_left_panel":
|
||||||
this.setState({showStickers: false});
|
this.setState({showStickers: false});
|
||||||
|
|
|
@ -19,7 +19,8 @@ import React from "react";
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
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 {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import ToastStore from "../../../stores/ToastStore";
|
import ToastStore from "../../../stores/ToastStore";
|
||||||
|
@ -27,6 +28,7 @@ import Modal from "../../../Modal";
|
||||||
import GenericToast from "./GenericToast";
|
import GenericToast from "./GenericToast";
|
||||||
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||||
import {DeviceInfo} from "matrix-js-sdk/src/crypto/deviceinfo";
|
import {DeviceInfo} from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
toastKey: string;
|
toastKey: string;
|
||||||
|
@ -104,9 +106,9 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
room_id: request.channel.roomId,
|
room_id: request.channel.roomId,
|
||||||
should_peek: false,
|
should_peek: false,
|
||||||
});
|
});
|
||||||
dis.dispatch({
|
dis.dispatch<SetRightPanelPhasePayload>({
|
||||||
action: "set_right_panel_phase",
|
action: Action.SetRightPanelPhase,
|
||||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
phase: RightPanelPhases.EncryptionPanel,
|
||||||
refireParams: {
|
refireParams: {
|
||||||
verificationRequest: request,
|
verificationRequest: request,
|
||||||
member: cli.getUser(request.otherUserId),
|
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.
|
* Changes room based on room list order and payload parameters. Should be used with ViewRoomDeltaPayload.
|
||||||
*/
|
*/
|
||||||
ViewRoomDelta = "view_room_delta",
|
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 ThemeController from './controllers/ThemeController';
|
||||||
import PushToMatrixClientController from './controllers/PushToMatrixClientController';
|
import PushToMatrixClientController from './controllers/PushToMatrixClientController';
|
||||||
import ReloadOnChangeController from "./controllers/ReloadOnChangeController";
|
import ReloadOnChangeController from "./controllers/ReloadOnChangeController";
|
||||||
import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases";
|
import {RightPanelPhases} from "../stores/RightPanelStorePhases";
|
||||||
import FontSizeController from './controllers/FontSizeController';
|
import FontSizeController from './controllers/FontSizeController';
|
||||||
import SystemFontController from './controllers/SystemFontController';
|
import SystemFontController from './controllers/SystemFontController';
|
||||||
import UseSystemFontController from './controllers/UseSystemFontController';
|
import UseSystemFontController from './controllers/UseSystemFontController';
|
||||||
|
@ -534,11 +534,11 @@ export const SETTINGS = {
|
||||||
},
|
},
|
||||||
"lastRightPanelPhaseForRoom": {
|
"lastRightPanelPhaseForRoom": {
|
||||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||||
default: RIGHT_PANEL_PHASES.RoomMemberInfo,
|
default: RightPanelPhases.RoomMemberInfo,
|
||||||
},
|
},
|
||||||
"lastRightPanelPhaseForGroup": {
|
"lastRightPanelPhaseForGroup": {
|
||||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||||
default: RIGHT_PANEL_PHASES.GroupMemberList,
|
default: RightPanelPhases.GroupMemberList,
|
||||||
},
|
},
|
||||||
"enableEventIndexing": {
|
"enableEventIndexing": {
|
||||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||||
|
|
|
@ -18,161 +18,177 @@ import dis from '../dispatcher/dispatcher';
|
||||||
import {pendingVerificationRequestForUser} from '../verification';
|
import {pendingVerificationRequestForUser} from '../verification';
|
||||||
import {Store} from 'flux/utils';
|
import {Store} from 'flux/utils';
|
||||||
import SettingsStore, {SettingLevel} from "../settings/SettingsStore";
|
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
|
// 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.
|
// because they're different flows for the user to follow.
|
||||||
showRoomPanel: SettingsStore.getValue("showRightPanelInRoom"),
|
showRoomPanel: boolean;
|
||||||
showGroupPanel: SettingsStore.getValue("showRightPanelInGroup"),
|
showGroupPanel: boolean;
|
||||||
|
|
||||||
// The last phase (screen) the right panel was showing
|
// The last phase (screen) the right panel was showing
|
||||||
lastRoomPhase: SettingsStore.getValue("lastRightPanelPhaseForRoom"),
|
lastRoomPhase: RightPanelPhases;
|
||||||
lastGroupPhase: SettingsStore.getValue("lastRightPanelPhaseForGroup"),
|
lastGroupPhase: RightPanelPhases;
|
||||||
|
|
||||||
// Extra information about the last phase
|
// 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: {},
|
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 = [
|
const MEMBER_INFO_PHASES = [
|
||||||
RIGHT_PANEL_PHASES.RoomMemberInfo,
|
RightPanelPhases.RoomMemberInfo,
|
||||||
RIGHT_PANEL_PHASES.Room3pidMemberInfo,
|
RightPanelPhases.Room3pidMemberInfo,
|
||||||
RIGHT_PANEL_PHASES.EncryptionPanel,
|
RightPanelPhases.EncryptionPanel,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for tracking the state of the right panel between layouts and
|
* A class for tracking the state of the right panel between layouts and
|
||||||
* sessions.
|
* sessions.
|
||||||
*/
|
*/
|
||||||
export default class RightPanelStore extends Store {
|
export default class RightPanelStore extends Store<ActionPayload> {
|
||||||
static _instance;
|
private static instance: RightPanelStore;
|
||||||
|
private state: RightPanelStoreState;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(dis);
|
super(dis);
|
||||||
|
|
||||||
// Initialise state
|
// Initialise state
|
||||||
this._state = INITIAL_STATE;
|
this.state = INITIAL_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isOpenForRoom(): boolean {
|
get isOpenForRoom(): boolean {
|
||||||
return this._state.showRoomPanel;
|
return this.state.showRoomPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isOpenForGroup(): boolean {
|
get isOpenForGroup(): boolean {
|
||||||
return this._state.showGroupPanel;
|
return this.state.showGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
get roomPanelPhase(): string {
|
get roomPanelPhase(): RightPanelPhases {
|
||||||
return this._state.lastRoomPhase;
|
return this.state.lastRoomPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
get groupPanelPhase(): string {
|
get groupPanelPhase(): RightPanelPhases {
|
||||||
return this._state.lastGroupPhase;
|
return this.state.lastGroupPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
get visibleRoomPanelPhase(): string {
|
get visibleRoomPanelPhase(): RightPanelPhases {
|
||||||
return this.isOpenForRoom ? this.roomPanelPhase : null;
|
return this.isOpenForRoom ? this.roomPanelPhase : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get visibleGroupPanelPhase(): string {
|
get visibleGroupPanelPhase(): RightPanelPhases {
|
||||||
return this.isOpenForGroup ? this.groupPanelPhase : null;
|
return this.isOpenForGroup ? this.groupPanelPhase : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get roomPanelPhaseParams(): any {
|
get roomPanelPhaseParams(): any {
|
||||||
return this._state.lastRoomPhaseParams || {};
|
return this.state.lastRoomPhaseParams || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
_setState(newState) {
|
private setState(newState: Partial<RightPanelStoreState>) {
|
||||||
this._state = Object.assign(this._state, newState);
|
this.state = Object.assign(this.state, newState);
|
||||||
|
|
||||||
SettingsStore.setValue(
|
SettingsStore.setValue(
|
||||||
"showRightPanelInRoom",
|
"showRightPanelInRoom",
|
||||||
null,
|
null,
|
||||||
SettingLevel.DEVICE,
|
SettingLevel.DEVICE,
|
||||||
this._state.showRoomPanel,
|
this.state.showRoomPanel,
|
||||||
);
|
);
|
||||||
SettingsStore.setValue(
|
SettingsStore.setValue(
|
||||||
"showRightPanelInGroup",
|
"showRightPanelInGroup",
|
||||||
null,
|
null,
|
||||||
SettingLevel.DEVICE,
|
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(
|
SettingsStore.setValue(
|
||||||
"lastRightPanelPhaseForRoom",
|
"lastRightPanelPhaseForRoom",
|
||||||
null,
|
null,
|
||||||
SettingLevel.DEVICE,
|
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(
|
SettingsStore.setValue(
|
||||||
"lastRightPanelPhaseForGroup",
|
"lastRightPanelPhaseForGroup",
|
||||||
null,
|
null,
|
||||||
SettingLevel.DEVICE,
|
SettingLevel.DEVICE,
|
||||||
this._state.lastGroupPhase,
|
this.state.lastGroupPhase,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.__emitChange();
|
this.__emitChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
__onDispatch(payload) {
|
__onDispatch(payload: ActionPayload) {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'view_room':
|
case 'view_room':
|
||||||
case 'view_group':
|
case 'view_group':
|
||||||
// Reset to the member list if we're viewing member info
|
// Reset to the member list if we're viewing member info
|
||||||
if (MEMBER_INFO_PHASES.includes(this._state.lastRoomPhase)) {
|
if (MEMBER_INFO_PHASES.includes(this.state.lastRoomPhase)) {
|
||||||
this._setState({lastRoomPhase: RIGHT_PANEL_PHASES.RoomMemberList, lastRoomPhaseParams: {}});
|
this.setState({lastRoomPhase: RightPanelPhases.RoomMemberList, lastRoomPhaseParams: {}});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the same for groups
|
// Do the same for groups
|
||||||
if (this._state.lastGroupPhase === RIGHT_PANEL_PHASES.GroupMemberInfo) {
|
if (this.state.lastGroupPhase === RightPanelPhases.GroupMemberInfo) {
|
||||||
this._setState({lastGroupPhase: RIGHT_PANEL_PHASES.GroupMemberList});
|
this.setState({lastGroupPhase: RightPanelPhases.GroupMemberList});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'set_right_panel_phase': {
|
case Action.SetRightPanelPhase: {
|
||||||
let targetPhase = payload.phase;
|
let targetPhase = payload.phase;
|
||||||
let refireParams = payload.refireParams;
|
let refireParams = payload.refireParams;
|
||||||
// redirect to EncryptionPanel if there is an ongoing verification request
|
// 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 {member} = payload.refireParams;
|
||||||
const pendingRequest = pendingVerificationRequestForUser(member);
|
const pendingRequest = pendingVerificationRequestForUser(member);
|
||||||
if (pendingRequest) {
|
if (pendingRequest) {
|
||||||
targetPhase = RIGHT_PANEL_PHASES.EncryptionPanel;
|
targetPhase = RightPanelPhases.EncryptionPanel;
|
||||||
refireParams = {
|
refireParams = {
|
||||||
verificationRequest: pendingRequest,
|
verificationRequest: pendingRequest,
|
||||||
member,
|
member,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!RIGHT_PANEL_PHASES[targetPhase]) {
|
if (!RightPanelPhases[targetPhase]) {
|
||||||
console.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
|
console.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GROUP_PHASES.includes(targetPhase)) {
|
if (GROUP_PHASES.includes(targetPhase)) {
|
||||||
if (targetPhase === this._state.lastGroupPhase) {
|
if (targetPhase === this.state.lastGroupPhase) {
|
||||||
this._setState({
|
this.setState({
|
||||||
showGroupPanel: !this._state.showGroupPanel,
|
showGroupPanel: !this.state.showGroupPanel,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this._setState({
|
this.setState({
|
||||||
lastGroupPhase: targetPhase,
|
lastGroupPhase: targetPhase,
|
||||||
showGroupPanel: true,
|
showGroupPanel: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (targetPhase === this._state.lastRoomPhase && !refireParams) {
|
if (targetPhase === this.state.lastRoomPhase && !refireParams) {
|
||||||
this._setState({
|
this.setState({
|
||||||
showRoomPanel: !this._state.showRoomPanel,
|
showRoomPanel: !this.state.showRoomPanel,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this._setState({
|
this.setState({
|
||||||
lastRoomPhase: targetPhase,
|
lastRoomPhase: targetPhase,
|
||||||
showRoomPanel: true,
|
showRoomPanel: true,
|
||||||
lastRoomPhaseParams: refireParams || {},
|
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.
|
// Let things like the member info panel actually open to the right member.
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'after_right_panel_phase_change',
|
action: Action.AfterRightPanelPhaseChange,
|
||||||
phase: targetPhase,
|
phase: targetPhase,
|
||||||
...(refireParams || {}),
|
...(refireParams || {}),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'toggle_right_panel':
|
case Action.ToggleRightPanel:
|
||||||
if (payload.type === "room") {
|
if (payload.type === "room") {
|
||||||
this._setState({ showRoomPanel: !this._state.showRoomPanel });
|
this.setState({ showRoomPanel: !this.state.showRoomPanel });
|
||||||
} else { // group
|
} else { // group
|
||||||
this._setState({ showGroupPanel: !this._state.showGroupPanel });
|
this.setState({ showGroupPanel: !this.state.showGroupPanel });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSharedInstance(): RightPanelStore {
|
static getSharedInstance(): RightPanelStore {
|
||||||
if (!RightPanelStore._instance) {
|
if (!RightPanelStore.instance) {
|
||||||
RightPanelStore._instance = new RightPanelStore();
|
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.
|
// 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
|
// Room stuff
|
||||||
RoomMemberList: 'RoomMemberList',
|
RoomMemberList = 'RoomMemberList',
|
||||||
FilePanel: 'FilePanel',
|
FilePanel = 'FilePanel',
|
||||||
NotificationPanel: 'NotificationPanel',
|
NotificationPanel = 'NotificationPanel',
|
||||||
RoomMemberInfo: 'RoomMemberInfo',
|
RoomMemberInfo = 'RoomMemberInfo',
|
||||||
EncryptionPanel: 'EncryptionPanel',
|
EncryptionPanel = 'EncryptionPanel',
|
||||||
|
|
||||||
Room3pidMemberInfo: 'Room3pidMemberInfo',
|
Room3pidMemberInfo = 'Room3pidMemberInfo',
|
||||||
// Group stuff
|
// Group stuff
|
||||||
GroupMemberList: 'GroupMemberList',
|
GroupMemberList = 'GroupMemberList',
|
||||||
GroupRoomList: 'GroupRoomList',
|
GroupRoomList = 'GroupRoomList',
|
||||||
GroupRoomInfo: 'GroupRoomInfo',
|
GroupRoomInfo = 'GroupRoomInfo',
|
||||||
GroupMemberInfo: 'GroupMemberInfo',
|
GroupMemberInfo = 'GroupMemberInfo',
|
||||||
});
|
}
|
||||||
|
|
||||||
// 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 = [
|
||||||
RIGHT_PANEL_PHASES.NotificationPanel,
|
RightPanelPhases.NotificationPanel,
|
||||||
RIGHT_PANEL_PHASES.FilePanel,
|
RightPanelPhases.FilePanel,
|
||||||
RIGHT_PANEL_PHASES.RoomMemberList,
|
RightPanelPhases.RoomMemberList,
|
||||||
RIGHT_PANEL_PHASES.GroupMemberList,
|
RightPanelPhases.GroupMemberList,
|
||||||
RIGHT_PANEL_PHASES.GroupRoomList,
|
RightPanelPhases.GroupRoomList,
|
||||||
];
|
];
|
|
@ -19,10 +19,11 @@ import dis from "./dispatcher/dispatcher";
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import * as sdk from './index';
|
import * as sdk from './index';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases";
|
import {RightPanelPhases} from "./stores/RightPanelStorePhases";
|
||||||
import {findDMForUser} from './createRoom';
|
import {findDMForUser} from './createRoom';
|
||||||
import {accessSecretStorage} from './CrossSigningManager';
|
import {accessSecretStorage} from './CrossSigningManager';
|
||||||
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||||
|
import {Action} from './dispatcher/actions';
|
||||||
|
|
||||||
async function enable4SIfNeeded() {
|
async function enable4SIfNeeded() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -91,8 +92,8 @@ export async function verifyDevice(user, device) {
|
||||||
verificationMethods.SAS,
|
verificationMethods.SAS,
|
||||||
);
|
);
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "set_right_panel_phase",
|
action: Action.SetRightPanelPhase,
|
||||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
phase: RightPanelPhases.EncryptionPanel,
|
||||||
refireParams: {member: user, verificationRequestPromise},
|
refireParams: {member: user, verificationRequestPromise},
|
||||||
});
|
});
|
||||||
} else if (action === "legacy") {
|
} else if (action === "legacy") {
|
||||||
|
@ -120,8 +121,8 @@ export async function legacyVerifyUser(user) {
|
||||||
}
|
}
|
||||||
const verificationRequestPromise = cli.requestVerification(user.userId);
|
const verificationRequestPromise = cli.requestVerification(user.userId);
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "set_right_panel_phase",
|
action: Action.SetRightPanelPhase,
|
||||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
phase: RightPanelPhases.EncryptionPanel,
|
||||||
refireParams: {member: user, verificationRequestPromise},
|
refireParams: {member: user, verificationRequestPromise},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -132,8 +133,8 @@ export async function verifyUser(user) {
|
||||||
}
|
}
|
||||||
const existingRequest = pendingVerificationRequestForUser(user);
|
const existingRequest = pendingVerificationRequestForUser(user);
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "set_right_panel_phase",
|
action: Action.SetRightPanelPhase,
|
||||||
phase: RIGHT_PANEL_PHASES.EncryptionPanel,
|
phase: RightPanelPhases.EncryptionPanel,
|
||||||
refireParams: {
|
refireParams: {
|
||||||
member: user,
|
member: user,
|
||||||
verificationRequest: existingRequest,
|
verificationRequest: existingRequest,
|
||||||
|
|
Loading…
Reference in a new issue