Merge remote-tracking branch 'upstream/develop' into fix/user-info-dm/17877
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
commit
8094a34384
839 changed files with 35725 additions and 16736 deletions
|
@ -43,8 +43,8 @@ interface IGroupProps {
|
|||
|
||||
export const Group: React.FC<IGroupProps> = ({ className, title, children }) => {
|
||||
return <div className={classNames("mx_BaseCard_Group", className)}>
|
||||
<h1>{title}</h1>
|
||||
{children}
|
||||
<h1>{ title }</h1>
|
||||
{ children }
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ const EncryptionInfo: React.FC<IProps> = ({
|
|||
} else {
|
||||
content = (
|
||||
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={onStartVerification}>
|
||||
{_t("Start Verification")}
|
||||
{ _t("Start Verification") }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
@ -75,17 +75,17 @@ const EncryptionInfo: React.FC<IProps> = ({
|
|||
if (isRoomEncrypted) {
|
||||
description = (
|
||||
<div>
|
||||
<p>{_t("Messages in this room are end-to-end encrypted.")}</p>
|
||||
<p>{_t("Your messages are secured and only you and the recipient have " +
|
||||
"the unique keys to unlock them.")}</p>
|
||||
<p>{ _t("Messages in this room are end-to-end encrypted.") }</p>
|
||||
<p>{ _t("Your messages are secured and only you and the recipient have " +
|
||||
"the unique keys to unlock them.") }</p>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
description = (
|
||||
<div>
|
||||
<p>{_t("Messages in this room are not end-to-end encrypted.")}</p>
|
||||
<p>{_t("In encrypted rooms, your messages are secured and only you and the recipient have " +
|
||||
"the unique keys to unlock them.")}</p>
|
||||
<p>{ _t("Messages in this room are not end-to-end encrypted.") }</p>
|
||||
<p>{ _t("In encrypted rooms, your messages are secured and only you and the recipient have " +
|
||||
"the unique keys to unlock them.") }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -96,14 +96,14 @@ const EncryptionInfo: React.FC<IProps> = ({
|
|||
|
||||
return <React.Fragment>
|
||||
<div className="mx_UserInfo_container">
|
||||
<h3>{_t("Encryption")}</h3>
|
||||
<h3>{ _t("Encryption") }</h3>
|
||||
{ description }
|
||||
</div>
|
||||
<div className="mx_UserInfo_container">
|
||||
<h3>{_t("Verify User")}</h3>
|
||||
<h3>{ _t("Verify User") }</h3>
|
||||
<div>
|
||||
<p>{_t("For extra security, verify this user by checking a one-time code on both of your devices.")}</p>
|
||||
<p>{_t("To be secure, do this in person or use a trusted way to communicate.")}</p>
|
||||
<p>{ _t("For extra security, verify this user by checking a one-time code on both of your devices.") }</p>
|
||||
<p>{ _t("To be secure, do this in person or use a trusted way to communicate.") }</p>
|
||||
{ content }
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -57,7 +57,7 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
|||
// state to show a spinner immediately after clicking "start verification",
|
||||
// before we have a request
|
||||
const [isRequesting, setRequesting] = useState(false);
|
||||
const [phase, setPhase] = useState(request && request.phase);
|
||||
const [phase, setPhase] = useState(request?.phase);
|
||||
useEffect(() => {
|
||||
setRequest(verificationRequest);
|
||||
if (verificationRequest) {
|
||||
|
@ -87,12 +87,12 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
|||
headerImage: require("../../../../res/img/e2e/warning.svg"),
|
||||
title: _t("Your messages are not secure"),
|
||||
description: <div>
|
||||
{_t("One of the following may be compromised:")}
|
||||
{ _t("One of the following may be compromised:") }
|
||||
<ul>
|
||||
<li>{_t("Your homeserver")}</li>
|
||||
<li>{_t("The homeserver the user you’re verifying is connected to")}</li>
|
||||
<li>{_t("Yours, or the other users’ internet connection")}</li>
|
||||
<li>{_t("Yours, or the other users’ session")}</li>
|
||||
<li>{ _t("Your homeserver") }</li>
|
||||
<li>{ _t("The homeserver the user you’re verifying is connected to") }</li>
|
||||
<li>{ _t("Yours, or the other users’ internet connection") }</li>
|
||||
<li>{ _t("Yours, or the other users’ session") }</li>
|
||||
</ul>
|
||||
</div>,
|
||||
onFinished: onClose,
|
||||
|
|
|
@ -99,7 +99,7 @@ export default abstract class HeaderButtons<P = {}> extends React.Component<IPro
|
|||
|
||||
public render() {
|
||||
return <div className="mx_HeaderButtons">
|
||||
{this.renderButtons()}
|
||||
{ this.renderButtons() }
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ const PinnedMessagesCard = ({ room, onClose }: IProps) => {
|
|||
<h2>{ _t("Nothing pinned, yet") }</h2>
|
||||
{ _t("If you have permissions, open the menu on any message and select " +
|
||||
"<b>Pin</b> to stick them here.", {}, {
|
||||
b: sub => <b>{ sub }</b>,
|
||||
b: sub => <b>{ sub }</b>,
|
||||
}) }
|
||||
</div>
|
||||
</div>;
|
||||
|
|
|
@ -148,7 +148,7 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
|||
yOffset={-48}
|
||||
>
|
||||
<WidgetAvatar app={app} />
|
||||
<span>{name}</span>
|
||||
<span>{ name }</span>
|
||||
{ subtitle }
|
||||
</AccessibleTooltipButton>
|
||||
|
||||
|
@ -220,6 +220,13 @@ const onRoomFilesClick = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const onRoomThreadsClick = () => {
|
||||
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.ThreadPanel,
|
||||
});
|
||||
};
|
||||
|
||||
const onRoomSettingsClick = () => {
|
||||
defaultDispatcher.dispatch({ action: "open_room_settings" });
|
||||
};
|
||||
|
@ -256,7 +263,7 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
|||
<h2 title={name}>
|
||||
{ name }
|
||||
</h2>
|
||||
)}
|
||||
) }
|
||||
</RoomName>
|
||||
<div className="mx_RoomSummaryCard_alias" title={alias}>
|
||||
{ alias }
|
||||
|
@ -268,16 +275,21 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
|||
return <BaseCard header={header} className="mx_RoomSummaryCard" onClose={onClose}>
|
||||
<Group title={_t("About")} className="mx_RoomSummaryCard_aboutGroup">
|
||||
<Button className="mx_RoomSummaryCard_icon_people" onClick={onRoomMembersClick}>
|
||||
{_t("%(count)s people", { count: memberCount })}
|
||||
{ _t("%(count)s people", { count: memberCount }) }
|
||||
</Button>
|
||||
<Button className="mx_RoomSummaryCard_icon_files" onClick={onRoomFilesClick}>
|
||||
{_t("Show files")}
|
||||
{ _t("Show files") }
|
||||
</Button>
|
||||
{ SettingsStore.getValue("feature_thread") && (
|
||||
<Button className="mx_RoomSummaryCard_icon_threads" onClick={onRoomThreadsClick}>
|
||||
{ _t("Show threads") }
|
||||
</Button>
|
||||
) }
|
||||
<Button className="mx_RoomSummaryCard_icon_share" onClick={onShareRoomClick}>
|
||||
{_t("Share room")}
|
||||
{ _t("Share room") }
|
||||
</Button>
|
||||
<Button className="mx_RoomSummaryCard_icon_settings" onClick={onRoomSettingsClick}>
|
||||
{_t("Room settings")}
|
||||
{ _t("Room settings") }
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ import RoomName from "../elements/RoomName";
|
|||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import UIStore from "../../../stores/UIStore";
|
||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
|
||||
export interface IDevice {
|
||||
deviceId: string;
|
||||
|
@ -204,10 +205,10 @@ function DeviceItem({ userId, device }: {userId: string, device: IDevice}) {
|
|||
|
||||
if (isVerified) {
|
||||
return (
|
||||
<div className={classes} title={device.deviceId} >
|
||||
<div className={classes} title={device.deviceId}>
|
||||
<div className={iconClasses} />
|
||||
<div className="mx_UserInfo_device_name">{deviceName}</div>
|
||||
<div className="mx_UserInfo_device_trusted">{trustedLabel}</div>
|
||||
<div className="mx_UserInfo_device_name">{ deviceName }</div>
|
||||
<div className="mx_UserInfo_device_trusted">{ trustedLabel }</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
|
@ -218,8 +219,8 @@ function DeviceItem({ userId, device }: {userId: string, device: IDevice}) {
|
|||
onClick={onDeviceClick}
|
||||
>
|
||||
<div className={iconClasses} />
|
||||
<div className="mx_UserInfo_device_name">{deviceName}</div>
|
||||
<div className="mx_UserInfo_device_trusted">{trustedLabel}</div>
|
||||
<div className="mx_UserInfo_device_name">{ deviceName }</div>
|
||||
<div className="mx_UserInfo_device_trusted">{ trustedLabel }</div>
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
@ -236,7 +237,7 @@ function DevicesSection({ devices, userId, loading }: {devices: IDevice[], userI
|
|||
return <Spinner />;
|
||||
}
|
||||
if (devices === null) {
|
||||
return <>{_t("Unable to load session list")}</>;
|
||||
return <>{ _t("Unable to load session list") }</>;
|
||||
}
|
||||
const isMe = userId === cli.getUserId();
|
||||
const deviceTrusts = devices.map(d => cli.checkDeviceTrust(userId, d.deviceId));
|
||||
|
@ -281,14 +282,14 @@ function DevicesSection({ devices, userId, loading }: {devices: IDevice[], userI
|
|||
expandButton = (<AccessibleButton className="mx_UserInfo_expand mx_linkButton"
|
||||
onClick={() => setExpanded(false)}
|
||||
>
|
||||
<div>{expandHideCaption}</div>
|
||||
<div>{ expandHideCaption }</div>
|
||||
</AccessibleButton>);
|
||||
} else {
|
||||
expandButton = (<AccessibleButton className="mx_UserInfo_expand mx_linkButton"
|
||||
onClick={() => setExpanded(true)}
|
||||
>
|
||||
<div className={expandIconClasses} />
|
||||
<div>{expandCountCaption}</div>
|
||||
<div>{ expandCountCaption }</div>
|
||||
</AccessibleButton>);
|
||||
}
|
||||
}
|
||||
|
@ -305,8 +306,8 @@ function DevicesSection({ devices, userId, loading }: {devices: IDevice[], userI
|
|||
|
||||
return (
|
||||
<div className="mx_UserInfo_devices">
|
||||
<div>{deviceList}</div>
|
||||
<div>{expandButton}</div>
|
||||
<div>{ deviceList }</div>
|
||||
<div>{ expandButton }</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -384,7 +385,7 @@ const UserOptionsSection: React.FC<{
|
|||
}
|
||||
|
||||
insertPillButton = (
|
||||
<AccessibleButton onClick={onInsertPillButton} className={"mx_UserInfo_field"}>
|
||||
<AccessibleButton onClick={onInsertPillButton} className="mx_UserInfo_field">
|
||||
{ _t('Mention') }
|
||||
</AccessibleButton>
|
||||
);
|
||||
|
@ -427,7 +428,7 @@ const UserOptionsSection: React.FC<{
|
|||
let directMessageButton;
|
||||
if (!isMe) {
|
||||
directMessageButton = (
|
||||
<AccessibleButton onClick={() => openDMForUser(cli, member.userId)} className="mx_UserInfo_field">
|
||||
<AccessibleButton onClick={() => { openDMForUser(cli, member.userId); }} className="mx_UserInfo_field">
|
||||
{ findDMForUser(cli, member.userId) ? _t("Open chat") : _t('Direct message') }
|
||||
</AccessibleButton>
|
||||
);
|
||||
|
@ -728,7 +729,7 @@ const MuteToggleButton: React.FC<IBaseRoomProps> = ({ member, room, powerLevels,
|
|||
// if muting self, warn as it may be irreversible
|
||||
if (target === cli.getUserId()) {
|
||||
try {
|
||||
if (!(await warnSelfDemote(SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()))) return;
|
||||
if (!(await warnSelfDemote(SpaceStore.spacesEnabled && room?.isSpaceRoom()))) return;
|
||||
} catch (e) {
|
||||
console.error("Failed to warn about self demotion: ", e);
|
||||
return;
|
||||
|
@ -817,7 +818,7 @@ const RoomAdminToolsContainer: React.FC<IBaseRoomProps> = ({
|
|||
if (canAffectUser && me.powerLevel >= kickPowerLevel) {
|
||||
kickButton = <RoomKickButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
|
||||
}
|
||||
if (me.powerLevel >= redactPowerLevel && (!SettingsStore.getValue("feature_spaces") || !room.isSpaceRoom())) {
|
||||
if (me.powerLevel >= redactPowerLevel && (!SpaceStore.spacesEnabled || !room.isSpaceRoom())) {
|
||||
redactButton = (
|
||||
<RedactMessagesButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />
|
||||
);
|
||||
|
@ -825,7 +826,7 @@ const RoomAdminToolsContainer: React.FC<IBaseRoomProps> = ({
|
|||
if (canAffectUser && me.powerLevel >= banPowerLevel) {
|
||||
banButton = <BanToggleButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />;
|
||||
}
|
||||
if (canAffectUser && me.powerLevel >= editPowerLevel) {
|
||||
if (canAffectUser && me.powerLevel >= editPowerLevel && !room.isSpaceRoom()) {
|
||||
muteButton = (
|
||||
<MuteToggleButton
|
||||
member={member}
|
||||
|
@ -850,7 +851,7 @@ const RoomAdminToolsContainer: React.FC<IBaseRoomProps> = ({
|
|||
return <div />;
|
||||
};
|
||||
|
||||
interface GroupMember {
|
||||
export interface GroupMember {
|
||||
userId: string;
|
||||
displayname?: string; // XXX: GroupMember objects are inconsistent :((
|
||||
avatarUrl?: string;
|
||||
|
@ -1037,7 +1038,7 @@ const PowerLevelSection: React.FC<{
|
|||
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
|
||||
return (
|
||||
<div className="mx_UserInfo_profileField">
|
||||
<div className="mx_UserInfo_roleDescription">{role}</div>
|
||||
<div className="mx_UserInfo_roleDescription">{ role }</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1096,7 +1097,7 @@ const PowerLevelEditor: React.FC<{
|
|||
} else if (myUserId === target) {
|
||||
// If we are changing our own PL it can only ever be decreasing, which we cannot reverse.
|
||||
try {
|
||||
if (!(await warnSelfDemote(SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()))) return;
|
||||
if (!(await warnSelfDemote(SpaceStore.spacesEnabled && room?.isSpaceRoom()))) return;
|
||||
} catch (e) {
|
||||
console.error("Failed to warn about self demotion: ", e);
|
||||
}
|
||||
|
@ -1266,7 +1267,7 @@ const BasicUserInfo: React.FC<{
|
|||
if (isSynapseAdmin && member.userId.endsWith(`:${MatrixClientPeg.getHomeserverName()}`)) {
|
||||
synapseDeactivateButton = (
|
||||
<AccessibleButton onClick={onSynapseDeactivate} className="mx_UserInfo_field mx_UserInfo_destructive">
|
||||
{_t("Deactivate user")}
|
||||
{ _t("Deactivate user") }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
@ -1277,7 +1278,9 @@ const BasicUserInfo: React.FC<{
|
|||
// hide the Roles section for DMs as it doesn't make sense there
|
||||
if (!DMRoomMap.shared().getUserIdForRoomId((member as RoomMember).roomId)) {
|
||||
memberDetails = <div className="mx_UserInfo_container">
|
||||
<h3>{ _t("Role") }</h3>
|
||||
<h3>{ _t("Role in <RoomName/>", {}, {
|
||||
RoomName: () => <b>{ room.name }</b>,
|
||||
}) }</h3>
|
||||
<PowerLevelSection
|
||||
powerLevels={powerLevels}
|
||||
user={member as RoomMember}
|
||||
|
@ -1326,10 +1329,10 @@ const BasicUserInfo: React.FC<{
|
|||
if (!isRoomEncrypted) {
|
||||
if (!cryptoEnabled) {
|
||||
text = _t("This client does not support end-to-end encryption.");
|
||||
} else if (room && (!SettingsStore.getValue("feature_spaces") || !room.isSpaceRoom())) {
|
||||
} else if (room && (!SpaceStore.spacesEnabled || !room.isSpaceRoom())) {
|
||||
text = _t("Messages in this room are not end-to-end encrypted.");
|
||||
}
|
||||
} else if (!SettingsStore.getValue("feature_spaces") || !room.isSpaceRoom()) {
|
||||
} else if (!SpaceStore.spacesEnabled || !room.isSpaceRoom()) {
|
||||
text = _t("Messages in this room are end-to-end encrypted.");
|
||||
}
|
||||
|
||||
|
@ -1352,14 +1355,17 @@ const BasicUserInfo: React.FC<{
|
|||
if (hasCrossSigningKeys !== undefined) {
|
||||
// Note: mx_UserInfo_verifyButton is for the end-to-end tests
|
||||
verifyButton = (
|
||||
<AccessibleButton className="mx_UserInfo_field mx_UserInfo_verifyButton" onClick={() => {
|
||||
if (hasCrossSigningKeys) {
|
||||
verifyUser(member as User);
|
||||
} else {
|
||||
legacyVerifyUser(member as User);
|
||||
}
|
||||
}}>
|
||||
{_t("Verify")}
|
||||
<AccessibleButton
|
||||
className="mx_UserInfo_field mx_UserInfo_verifyButton"
|
||||
onClick={() => {
|
||||
if (hasCrossSigningKeys) {
|
||||
verifyUser(member as User);
|
||||
} else {
|
||||
legacyVerifyUser(member as User);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{ _t("Verify") }
|
||||
</AccessibleButton>
|
||||
);
|
||||
} else if (!showDeviceListSpinner) {
|
||||
|
@ -1373,12 +1379,15 @@ const BasicUserInfo: React.FC<{
|
|||
let editDevices;
|
||||
if (member.userId == cli.getUserId()) {
|
||||
editDevices = (<p>
|
||||
<AccessibleButton className="mx_UserInfo_field" onClick={() => {
|
||||
dis.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Security,
|
||||
});
|
||||
}}>
|
||||
<AccessibleButton
|
||||
className="mx_UserInfo_field"
|
||||
onClick={() => {
|
||||
dis.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Security,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{ _t("Edit devices") }
|
||||
</AccessibleButton>
|
||||
</p>);
|
||||
|
@ -1405,7 +1414,7 @@ const BasicUserInfo: React.FC<{
|
|||
canInvite={roomPermissions.canInvite}
|
||||
isIgnored={isIgnored}
|
||||
member={member as RoomMember}
|
||||
isSpace={SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()}
|
||||
isSpace={SpaceStore.spacesEnabled && room?.isSpaceRoom()}
|
||||
/>
|
||||
|
||||
{ adminToolsContainer }
|
||||
|
@ -1517,8 +1526,8 @@ const UserInfoHeader: React.FC<{
|
|||
</div>
|
||||
<div>{ member.userId }</div>
|
||||
<div className="mx_UserInfo_profileStatus">
|
||||
{presenceLabel}
|
||||
{statusLabel}
|
||||
{ presenceLabel }
|
||||
{ statusLabel }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1566,11 +1575,12 @@ const UserInfo: React.FC<IProps> = ({
|
|||
// We have no previousPhase for when viewing a UserInfo from a Group or without a Room at this time
|
||||
if (room && phase === RightPanelPhases.EncryptionPanel) {
|
||||
previousPhase = RightPanelPhases.RoomMemberInfo;
|
||||
refireParams = { member: member };
|
||||
refireParams = { member };
|
||||
} else if (room?.isSpaceRoom() && SpaceStore.spacesEnabled) {
|
||||
previousPhase = previousPhase = RightPanelPhases.SpaceMemberList;
|
||||
refireParams = { space: room };
|
||||
} else if (room) {
|
||||
previousPhase = previousPhase = SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()
|
||||
? RightPanelPhases.SpaceMemberList
|
||||
: RightPanelPhases.RoomMemberList;
|
||||
previousPhase = RightPanelPhases.RoomMemberList;
|
||||
}
|
||||
|
||||
const onEncryptionPanelClose = () => {
|
||||
|
@ -1617,7 +1627,7 @@ const UserInfo: React.FC<IProps> = ({
|
|||
}
|
||||
|
||||
let scopeHeader;
|
||||
if (SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()) {
|
||||
if (SpaceStore.spacesEnabled && room?.isSpaceRoom()) {
|
||||
scopeHeader = <div className="mx_RightPanel_scopeHeader">
|
||||
<RoomAvatar room={room} height={32} width={32} />
|
||||
<RoomName room={room} />
|
||||
|
|
|
@ -29,43 +29,27 @@ import VerificationQRCode from "../elements/crypto/VerificationQRCode";
|
|||
import { _t } from "../../../languageHandler";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import E2EIcon from "../rooms/E2EIcon";
|
||||
import {
|
||||
PHASE_READY,
|
||||
PHASE_DONE,
|
||||
PHASE_STARTED,
|
||||
PHASE_CANCELLED,
|
||||
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import { Phase } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import VerificationShowSas from "../verification/VerificationShowSas";
|
||||
|
||||
// XXX: Should be defined in matrix-js-sdk
|
||||
enum VerificationPhase {
|
||||
PHASE_UNSENT,
|
||||
PHASE_REQUESTED,
|
||||
PHASE_READY,
|
||||
PHASE_DONE,
|
||||
PHASE_STARTED,
|
||||
PHASE_CANCELLED,
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
layout: string;
|
||||
request: VerificationRequest;
|
||||
member: RoomMember | User;
|
||||
phase: VerificationPhase;
|
||||
phase: Phase;
|
||||
onClose: () => void;
|
||||
isRoomEncrypted: boolean;
|
||||
inDialog: boolean;
|
||||
key: number;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
sasEvent?: SAS;
|
||||
sasEvent?: SAS["sasEvent"];
|
||||
emojiButtonClicked?: boolean;
|
||||
reciprocateButtonClicked?: boolean;
|
||||
reciprocateQREvent?: ReciprocateQRCode;
|
||||
reciprocateQREvent?: ReciprocateQRCode["reciprocateQREvent"];
|
||||
}
|
||||
|
||||
@replaceableComponent("views.right_panel.VerificationPanel")
|
||||
|
@ -85,12 +69,12 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
const brand = SdkConfig.get().brand;
|
||||
|
||||
const noCommonMethodError: JSX.Element = !showSAS && !showQR ?
|
||||
<p>{_t(
|
||||
<p>{ _t(
|
||||
"The session you are trying to verify doesn't support scanning a " +
|
||||
"QR code or emoji verification, which is what %(brand)s supports. Try " +
|
||||
"with a different client.",
|
||||
{ brand },
|
||||
)}</p> :
|
||||
) }</p> :
|
||||
null;
|
||||
|
||||
if (this.props.layout === 'dialog') {
|
||||
|
@ -100,31 +84,31 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
if (showQR) {
|
||||
qrBlockDialog =
|
||||
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
||||
<p>{_t("Scan this unique code")}</p>
|
||||
<p>{ _t("Scan this unique code") }</p>
|
||||
<VerificationQRCode qrCodeData={request.qrCodeData} />
|
||||
</div>;
|
||||
}
|
||||
if (showSAS) {
|
||||
sasBlockDialog = <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")}
|
||||
{ _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'>
|
||||
{_t("Start")}
|
||||
{ _t("Start") }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
||||
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 (
|
||||
<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'>
|
||||
{qrBlockDialog}
|
||||
{or}
|
||||
{sasBlockDialog}
|
||||
{noCommonMethodError}
|
||||
{ qrBlockDialog }
|
||||
{ or }
|
||||
{ sasBlockDialog }
|
||||
{ noCommonMethodError }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -133,10 +117,10 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
let qrBlock: JSX.Element;
|
||||
if (showQR) {
|
||||
qrBlock = <div className="mx_UserInfo_container">
|
||||
<h3>{_t("Verify by scanning")}</h3>
|
||||
<p>{_t("Ask %(displayName)s to scan your code:", {
|
||||
<h3>{ _t("Verify by scanning") }</h3>
|
||||
<p>{ _t("Ask %(displayName)s to scan your code:", {
|
||||
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
|
||||
})}</p>
|
||||
}) }</p>
|
||||
|
||||
<div className="mx_VerificationPanel_qrCode">
|
||||
<VerificationQRCode qrCodeData={request.qrCodeData} />
|
||||
|
@ -153,28 +137,28 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
|
||||
// Note: mx_VerificationPanel_verifyByEmojiButton is for the end-to-end tests
|
||||
sasBlock = <div className="mx_UserInfo_container">
|
||||
<h3>{_t("Verify by emoji")}</h3>
|
||||
<p>{sasLabel}</p>
|
||||
<h3>{ _t("Verify by emoji") }</h3>
|
||||
<p>{ sasLabel }</p>
|
||||
<AccessibleButton
|
||||
disabled={disabled}
|
||||
kind="primary"
|
||||
className="mx_UserInfo_wideButton mx_VerificationPanel_verifyByEmojiButton"
|
||||
onClick={this.startSAS}
|
||||
>
|
||||
{_t("Verify by emoji")}
|
||||
{ _t("Verify by emoji") }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const noCommonMethodBlock = noCommonMethodError ?
|
||||
<div className="mx_UserInfo_container">{noCommonMethodError}</div> :
|
||||
<div className="mx_UserInfo_container">{ noCommonMethodError }</div> :
|
||||
null;
|
||||
|
||||
// TODO: add way to open camera to scan a QR code
|
||||
return <React.Fragment>
|
||||
{qrBlock}
|
||||
{sasBlock}
|
||||
{noCommonMethodBlock}
|
||||
{ qrBlock }
|
||||
{ sasBlock }
|
||||
{ noCommonMethodBlock }
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
|
@ -204,7 +188,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
if (this.state.reciprocateQREvent) {
|
||||
// Element Web doesn't support scanning yet, so assume here we're the client being scanned.
|
||||
body = <React.Fragment>
|
||||
<p>{description}</p>
|
||||
<p>{ description }</p>
|
||||
<E2EIcon isUser={true} status="verified" size={128} hideTooltip={true} />
|
||||
<div className="mx_VerificationPanel_reciprocateButtons">
|
||||
<AccessibleButton
|
||||
|
@ -227,7 +211,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
body = <p><Spinner /></p>;
|
||||
}
|
||||
return <div className="mx_UserInfo_container mx_VerificationPanel_reciprocate_section">
|
||||
<h3>{_t("Verify by scanning")}</h3>
|
||||
<h3>{ _t("Verify by scanning") }</h3>
|
||||
{ body }
|
||||
</div>;
|
||||
}
|
||||
|
@ -266,12 +250,12 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
|
||||
return (
|
||||
<div className="mx_UserInfo_container mx_VerificationPanel_verified_section">
|
||||
<h3>{_t("Verified")}</h3>
|
||||
<p>{description}</p>
|
||||
<h3>{ _t("Verified") }</h3>
|
||||
<p>{ description }</p>
|
||||
<E2EIcon isUser={true} status="verified" size={128} hideTooltip={true} />
|
||||
{ text ? <p>{ text }</p> : null }
|
||||
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={this.props.onClose}>
|
||||
{_t("Got it")}
|
||||
{ _t("Got it") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
|
@ -305,11 +289,11 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
|
||||
return (
|
||||
<div className="mx_UserInfo_container">
|
||||
<h3>{_t("Verification cancelled")}</h3>
|
||||
<h3>{ _t("Verification cancelled") }</h3>
|
||||
<p>{ text }</p>
|
||||
|
||||
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={this.props.onClose}>
|
||||
{_t("Got it")}
|
||||
{ _t("Got it") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
|
@ -321,9 +305,9 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
const displayName = (member as User).displayName || (member as RoomMember).name || member.userId;
|
||||
|
||||
switch (phase) {
|
||||
case PHASE_READY:
|
||||
case Phase.Ready:
|
||||
return this.renderQRPhase();
|
||||
case PHASE_STARTED:
|
||||
case Phase.Started:
|
||||
switch (request.chosenMethod) {
|
||||
case verificationMethods.RECIPROCATE_QR_CODE:
|
||||
return this.renderQRReciprocatePhase();
|
||||
|
@ -339,16 +323,16 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
isSelf={request.isSelfVerification}
|
||||
/> : <Spinner />;
|
||||
return <div className="mx_UserInfo_container">
|
||||
<h3>{_t("Compare emoji")}</h3>
|
||||
<h3>{ _t("Compare emoji") }</h3>
|
||||
{ emojis }
|
||||
</div>;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
case PHASE_DONE:
|
||||
case Phase.Done:
|
||||
return this.renderVerifiedPhase();
|
||||
case PHASE_CANCELLED:
|
||||
case Phase.Cancelled:
|
||||
return this.renderCancelledPhase();
|
||||
}
|
||||
console.error("VerificationPanel unhandled phase:", phase);
|
||||
|
@ -375,7 +359,8 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
|
||||
private updateVerifierState = () => {
|
||||
const { request } = this.props;
|
||||
const { sasEvent, reciprocateQREvent } = request.verifier;
|
||||
const sasEvent = (request.verifier as SAS).sasEvent;
|
||||
const reciprocateQREvent = (request.verifier as ReciprocateQRCode).reciprocateQREvent;
|
||||
request.verifier.off('show_sas', this.updateVerifierState);
|
||||
request.verifier.off('show_reciprocate_qr', this.updateVerifierState);
|
||||
this.setState({ sasEvent, reciprocateQREvent });
|
||||
|
@ -402,7 +387,8 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
const { request } = this.props;
|
||||
request.on("change", this.onRequestChange);
|
||||
if (request.verifier) {
|
||||
const { sasEvent, reciprocateQREvent } = request.verifier;
|
||||
const sasEvent = (request.verifier as SAS).sasEvent;
|
||||
const reciprocateQREvent = (request.verifier as ReciprocateQRCode).reciprocateQREvent;
|
||||
this.setState({ sasEvent, reciprocateQREvent });
|
||||
}
|
||||
this.onRequestChange();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue