Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/17244

 Conflicts:
	src/components/views/room_settings/AliasSettings.tsx
This commit is contained in:
Michael Telatynski 2021-06-22 22:02:09 +01:00
commit 1286be1ffb
182 changed files with 3830 additions and 1987 deletions

View file

@ -17,8 +17,9 @@ limitations under the License.
import React from "react";
import * as sdk from "../../../index";
import {_t} from "../../../languageHandler";
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import { _t } from "../../../languageHandler";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user";
export const PendingActionSpinner = ({text}) => {
const Spinner = sdk.getComponent('elements.Spinner');
@ -31,7 +32,7 @@ export const PendingActionSpinner = ({text}) => {
interface IProps {
waitingForOtherParty: boolean;
waitingForNetwork: boolean;
member: RoomMember;
member: RoomMember | User;
onStartVerification: () => Promise<void>;
isRoomEncrypted: boolean;
inDialog: boolean;
@ -55,7 +56,7 @@ const EncryptionInfo: React.FC<IProps> = ({
text = _t("Accept on your other login…");
} else {
text = _t("Waiting for %(displayName)s to accept…", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
}
} else {

View file

@ -14,28 +14,29 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {useCallback, useEffect, useState} from "react";
import React, { useCallback, useEffect, useState } from "react";
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user";
import { PHASE_REQUESTED, PHASE_UNSENT } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import EncryptionInfo from "./EncryptionInfo";
import VerificationPanel from "./VerificationPanel";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {ensureDMExists} from "../../../createRoom";
import {useEventEmitter} from "../../../hooks/useEventEmitter";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { ensureDMExists } from "../../../createRoom";
import { useEventEmitter } from "../../../hooks/useEventEmitter";
import Modal from "../../../Modal";
import {PHASE_REQUESTED, PHASE_UNSENT} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import * as sdk from "../../../index";
import {_t} from "../../../languageHandler";
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import { _t } from "../../../languageHandler";
import dis from "../../../dispatcher/dispatcher";
import {Action} from "../../../dispatcher/actions";
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
import { Action } from "../../../dispatcher/actions";
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
// cancellation codes which constitute a key mismatch
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
interface IProps {
member: RoomMember;
member: RoomMember | User;
onClose: () => void;
verificationRequest: VerificationRequest;
verificationRequestPromise: Promise<VerificationRequest>;

View file

@ -38,7 +38,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import RoomViewStore from "../../../stores/RoomViewStore";
import MultiInviter from "../../../utils/MultiInviter";
import GroupStore from "../../../stores/GroupStore";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import E2EIcon from "../rooms/E2EIcon";
import { useEventEmitter } from "../../../hooks/useEventEmitter";
import { textualPowerLevel } from '../../../Roles';
@ -48,7 +48,7 @@ import EncryptionPanel from "./EncryptionPanel";
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
import { legacyVerifyUser, verifyDevice, verifyUser } from '../../../verification';
import { Action } from "../../../dispatcher/actions";
import { USER_SECURITY_TAB } from "../dialogs/UserSettingsDialog";
import { UserTab } from "../dialogs/UserSettingsDialog";
import { useIsEncrypted } from "../../../hooks/useIsEncrypted";
import BaseCard from "./BaseCard";
import { E2EStatus } from "../../../utils/ShieldUtils";
@ -68,6 +68,7 @@ import RoomAvatar from "../avatars/RoomAvatar";
import RoomName from "../elements/RoomName";
import { mediaFromMxc } from "../../../customisations/Media";
import UIStore from "../../../stores/UIStore";
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
export interface IDevice {
deviceId: string;
@ -146,7 +147,7 @@ async function openDMForUser(matrixClient: MatrixClient, userId: string) {
type SetUpdating = (updating: boolean) => void;
function useHasCrossSigningKeys(cli: MatrixClient, member: RoomMember, canVerify: boolean, setUpdating: SetUpdating) {
function useHasCrossSigningKeys(cli: MatrixClient, member: User, canVerify: boolean, setUpdating: SetUpdating) {
return useAsyncMemo(async () => {
if (!canVerify) {
return undefined;
@ -368,9 +369,9 @@ const UserOptionsSection: React.FC<{
};
const onInsertPillButton = function() {
dis.dispatch({
action: 'insert_mention',
user_id: member.userId,
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
userId: member.userId,
});
};
@ -967,7 +968,7 @@ interface IRoomPermissions {
canInvite: boolean;
}
function useRoomPermissions(cli: MatrixClient, room: Room, user: User): IRoomPermissions {
function useRoomPermissions(cli: MatrixClient, room: Room, user: RoomMember): IRoomPermissions {
const [roomPermissions, setRoomPermissions] = useState<IRoomPermissions>({
// modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL
modifyLevelMax: -1,
@ -1024,7 +1025,7 @@ function useRoomPermissions(cli: MatrixClient, room: Room, user: User): IRoomPer
}
const PowerLevelSection: React.FC<{
user: User;
user: RoomMember;
room: Room;
roomPermissions: IRoomPermissions;
powerLevels: IPowerLevelsContent;
@ -1033,7 +1034,7 @@ const PowerLevelSection: React.FC<{
return (<PowerLevelEditor user={user} room={room} roomPermissions={roomPermissions} />);
} else {
const powerLevelUsersDefault = powerLevels.users_default || 0;
const powerLevel = parseInt(user.powerLevel, 10);
const powerLevel = user.powerLevel;
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
return (
<div className="mx_UserInfo_profileField">
@ -1044,13 +1045,13 @@ const PowerLevelSection: React.FC<{
};
const PowerLevelEditor: React.FC<{
user: User;
user: RoomMember;
room: Room;
roomPermissions: IRoomPermissions;
}> = ({user, room, roomPermissions}) => {
const cli = useContext(MatrixClientContext);
const [selectedPowerLevel, setSelectedPowerLevel] = useState(parseInt(user.powerLevel, 10));
const [selectedPowerLevel, setSelectedPowerLevel] = useState(user.powerLevel);
const onPowerChange = useCallback(async (powerLevelStr: string) => {
const powerLevel = parseInt(powerLevelStr, 10);
setSelectedPowerLevel(powerLevel);
@ -1227,7 +1228,7 @@ const BasicUserInfo: React.FC<{
setPendingUpdateCount(pendingUpdateCount - 1);
}, [pendingUpdateCount]);
const roomPermissions = useRoomPermissions(cli, room, member);
const roomPermissions = useRoomPermissions(cli, room, member as RoomMember);
const onSynapseDeactivate = useCallback(async () => {
const {finished} = Modal.createTrackedDialog('Synapse User Deactivation', '', QuestionDialog, {
@ -1271,12 +1272,26 @@ const BasicUserInfo: React.FC<{
);
}
let memberDetails;
let adminToolsContainer;
if (room && member.roomId) {
if (room && (member as RoomMember).roomId) {
// 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>
<PowerLevelSection
powerLevels={powerLevels}
user={member as RoomMember}
room={room}
roomPermissions={roomPermissions}
/>
</div>;
}
adminToolsContainer = (
<RoomAdminToolsContainer
powerLevels={powerLevels}
member={member}
member={member as RoomMember}
room={room}
startUpdating={startUpdating}
stopUpdating={stopUpdating}>
@ -1305,20 +1320,6 @@ const BasicUserInfo: React.FC<{
spinner = <Spinner />;
}
let memberDetails;
// hide the Roles section for DMs as it doesn't make sense there
if (room && member.roomId && !DMRoomMap.shared().getUserIdForRoomId(member.roomId)) {
memberDetails = <div className="mx_UserInfo_container">
<h3>{ _t("Role") }</h3>
<PowerLevelSection
powerLevels={powerLevels}
user={member}
room={room}
roomPermissions={roomPermissions}
/>
</div>;
}
// only display the devices list if our client supports E2E
const cryptoEnabled = cli.isCryptoEnabled();
@ -1345,8 +1346,7 @@ const BasicUserInfo: React.FC<{
const setUpdating = (updating) => {
setPendingUpdateCount(count => count + (updating ? 1 : -1));
};
const hasCrossSigningKeys =
useHasCrossSigningKeys(cli, member, canVerify, setUpdating );
const hasCrossSigningKeys = useHasCrossSigningKeys(cli, member as User, canVerify, setUpdating);
const showDeviceListSpinner = devices === undefined;
if (canVerify) {
@ -1355,9 +1355,9 @@ const BasicUserInfo: React.FC<{
verifyButton = (
<AccessibleButton className="mx_UserInfo_field mx_UserInfo_verifyButton" onClick={() => {
if (hasCrossSigningKeys) {
verifyUser(member);
verifyUser(member as User);
} else {
legacyVerifyUser(member);
legacyVerifyUser(member as User);
}
}}>
{_t("Verify")}
@ -1377,7 +1377,7 @@ const BasicUserInfo: React.FC<{
<AccessibleButton className="mx_UserInfo_field" onClick={() => {
dis.dispatch({
action: Action.ViewUserSettings,
initialTabId: USER_SECURITY_TAB,
initialTabId: UserTab.Security,
});
}}>
{ _t("Edit devices") }
@ -1405,7 +1405,7 @@ const BasicUserInfo: React.FC<{
<UserOptionsSection
canInvite={roomPermissions.canInvite}
isIgnored={isIgnored}
member={member}
member={member as RoomMember}
isSpace={SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()}
/>
@ -1424,13 +1424,15 @@ const UserInfoHeader: React.FC<{
const cli = useContext(MatrixClientContext);
const onMemberAvatarClick = useCallback(() => {
const avatarUrl = member.getMxcAvatarUrl ? member.getMxcAvatarUrl() : member.avatarUrl;
const avatarUrl = (member as RoomMember).getMxcAvatarUrl
? (member as RoomMember).getMxcAvatarUrl()
: (member as User).avatarUrl;
if (!avatarUrl) return;
const httpUrl = mediaFromMxc(avatarUrl).srcHttp;
const params = {
src: httpUrl,
name: member.name,
name: (member as RoomMember).name || (member as User).displayName,
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
@ -1442,13 +1444,13 @@ const UserInfoHeader: React.FC<{
<div>
<MemberAvatar
key={member.userId} // to instantly blank the avatar when UserInfo changes members
member={member}
member={member as RoomMember}
width={2 * 0.3 * UIStore.instance.windowHeight} // 2x@30vh
height={2 * 0.3 * UIStore.instance.windowHeight} // 2x@30vh
resizeMethod="scale"
fallbackUserId={member.userId}
onClick={onMemberAvatarClick}
urls={member.avatarUrl ? [member.avatarUrl] : undefined} />
urls={(member as User).avatarUrl ? [(member as User).avatarUrl] : undefined} />
</div>
</div>
</div>
@ -1465,7 +1467,11 @@ const UserInfoHeader: React.FC<{
presenceCurrentlyActive = member.user.currentlyActive;
if (SettingsStore.getValue("feature_custom_status")) {
statusMessage = member.user._unstable_statusMessage;
if ((member as RoomMember).user) {
statusMessage = member.user.unstable_statusMessage;
} else {
statusMessage = (member as unknown as User).unstable_statusMessage;
}
}
}
@ -1496,7 +1502,7 @@ const UserInfoHeader: React.FC<{
e2eIcon = <E2EIcon size={18} status={e2eStatus} isUser={true} />;
}
const displayName = member.rawDisplayName || member.displayname;
const displayName = (member as RoomMember).rawDisplayName || (member as GroupMember).displayname;
return <React.Fragment>
{ avatarElement }
@ -1584,7 +1590,7 @@ const UserInfo: React.FC<IProps> = ({
content = (
<BasicUserInfo
room={room}
member={member}
member={member as User}
groupId={groupId as string}
devices={devices}
isRoomEncrypted={isRoomEncrypted} />
@ -1595,7 +1601,7 @@ const UserInfo: React.FC<IProps> = ({
content = (
<EncryptionPanel
{...props as React.ComponentProps<typeof EncryptionPanel>}
member={member}
member={member as User | RoomMember}
onClose={onEncryptionPanelClose}
isRoomEncrypted={isRoomEncrypted}
/>

View file

@ -22,6 +22,7 @@ import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import {SCAN_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user";
import {ReciprocateQRCode} from "matrix-js-sdk/src/crypto/verification/QRCode";
import {SAS} from "matrix-js-sdk/src/crypto/verification/SAS";
@ -51,7 +52,7 @@ enum VerificationPhase {
interface IProps {
layout: string;
request: VerificationRequest;
member: RoomMember;
member: RoomMember | User;
phase: VerificationPhase;
onClose: () => void;
isRoomEncrypted: boolean;
@ -134,7 +135,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
qrBlock = <div className="mx_UserInfo_container">
<h3>{_t("Verify by scanning")}</h3>
<p>{_t("Ask %(displayName)s to scan your code:", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
})}</p>
<div className="mx_VerificationPanel_qrCode">
@ -194,37 +195,33 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
private renderQRReciprocatePhase() {
const {member, request} = this.props;
let Button;
// a bit of a hack, but the FormButton should only be used in the right panel
// they should probably just be the same component with a css class applied to it?
if (this.props.inDialog) {
Button = sdk.getComponent("elements.AccessibleButton");
} else {
Button = sdk.getComponent("elements.FormButton");
}
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const description = request.isSelfVerification ?
_t("Almost there! Is your other session showing the same shield?") :
_t("Almost there! Is %(displayName)s showing the same shield?", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
let body: JSX.Element;
if (this.state.reciprocateQREvent) {
// Element Web doesn't support scanning yet, so assume here we're the client being scanned.
//
// we're passing both a label and a child string to Button as
// FormButton and AccessibleButton expect this differently
body = <React.Fragment>
<p>{description}</p>
<E2EIcon isUser={true} status="verified" size={128} hideTooltip={true} />
<div className="mx_VerificationPanel_reciprocateButtons">
<Button
label={_t("No")} kind="danger"
<AccessibleButton
kind="danger"
disabled={this.state.reciprocateButtonClicked}
onClick={this.onReciprocateNoClick}>{_t("No")}</Button>
<Button
label={_t("Yes")} kind="primary"
onClick={this.onReciprocateNoClick}
>
{ _t("No") }
</AccessibleButton>
<AccessibleButton
kind="primary"
disabled={this.state.reciprocateButtonClicked}
onClick={this.onReciprocateYesClick}>{_t("Yes")}</Button>
onClick={this.onReciprocateYesClick}
>
{ _t("Yes") }
</AccessibleButton>
</div>
</React.Fragment>;
} else {
@ -264,7 +261,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
}
} else {
description = _t("You've successfully verified %(displayName)s!", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
}
@ -302,7 +299,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
text = _t("You cancelled verification on your other session.");
} else {
text = _t("%(displayName)s cancelled verification.", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
}
text = `${text} ${startAgainInstruction}`;
@ -325,7 +322,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
public render() {
const {member, phase, request} = this.props;
const displayName = member.displayName || member.name || member.userId;
const displayName = (member as User).displayName || (member as RoomMember).name || member.userId;
switch (phase) {
case PHASE_READY: