-

+ avatarElement =
;
}
@@ -1058,6 +1171,12 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
title={_t('Close')} />;
}
+ const memberDetails =
;
+
+ const isRoomEncrypted = useIsEncrypted(cli, room);
// undefined means yet to be loaded, null means failed to load, otherwise list of devices
const [devices, setDevices] = useState(undefined);
// Download device lists
@@ -1082,14 +1201,15 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
setDevices(null);
}
}
-
- _downloadDeviceList();
+ if (isRoomEncrypted) {
+ _downloadDeviceList();
+ }
// Handle being unmounted
return () => {
cancelled = true;
};
- }, [cli, user.userId]);
+ }, [cli, user.userId, isRoomEncrypted]);
// Listen to changes
useEffect(() => {
@@ -1106,21 +1226,20 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
}
};
- cli.on("deviceVerificationChanged", onDeviceVerificationChanged);
+ if (isRoomEncrypted) {
+ cli.on("deviceVerificationChanged", onDeviceVerificationChanged);
+ }
// Handle being unmounted
return () => {
cancel = true;
- cli.removeListener("deviceVerificationChanged", onDeviceVerificationChanged);
+ if (isRoomEncrypted) {
+ cli.removeListener("deviceVerificationChanged", onDeviceVerificationChanged);
+ }
};
- }, [cli, user.userId]);
-
- let devicesSection;
- const isRoomEncrypted = _enableDevices && room && cli.isRoomEncrypted(room.roomId);
- if (isRoomEncrypted) {
- devicesSection =
;
- } else {
- let text;
+ }, [cli, user.userId, isRoomEncrypted]);
+ let text;
+ if (!isRoomEncrypted) {
if (!_enableDevices) {
text = _t("This client does not support end-to-end encryption.");
} else if (room) {
@@ -1128,22 +1247,24 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
} else {
// TODO what to render for GroupMember
}
-
- if (text) {
- devicesSection = (
-
-
{ _t("Trust & Devices") }
-
- { text }
-
-
- );
- }
+ } else {
+ text = _t("Messages in this room are end-to-end encrypted.");
}
+ const devicesSection = isRoomEncrypted ?
+ (
) : null;
+ const securitySection = (
+
+
{ _t("Security") }
+
{ text }
+
verifyDevice(user.userId, null)}>{_t("Verify")}
+ { devicesSection }
+
+ );
+
let e2eIcon;
if (isRoomEncrypted && devices) {
- e2eIcon =
;
+ e2eIcon =
;
}
return (
@@ -1153,16 +1274,14 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
-
-
+
+
{ e2eIcon }
{ displayName }
-
- { user.userId }
-
-
+
{ user.userId }
+
{presenceLabel}
{statusLabel}
@@ -1176,11 +1295,9 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
}
- { devicesSection }
-
- { directChatsSection }
-
+ { securitySection }
diff --git a/src/components/views/rooms/E2EIcon.js b/src/components/views/rooms/E2EIcon.js
index 54260e4ee2..d6baa30c8e 100644
--- a/src/components/views/rooms/E2EIcon.js
+++ b/src/components/views/rooms/E2EIcon.js
@@ -36,7 +36,13 @@ export default function(props) {
_t("All devices for this user are trusted") :
_t("All devices in this encrypted room are trusted");
}
- const icon = ();
+
+ let style = null;
+ if (props.size) {
+ style = {width: `${props.size}px`, height: `${props.size}px`};
+ }
+
+ const icon = ();
if (props.onClick) {
return ({ icon });
} else {
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 22f1f914b6..5fcf1e4491 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -606,8 +606,8 @@ module.exports = createReactClass({
mx_EventTile_last: this.props.last,
mx_EventTile_contextual: this.props.contextual,
mx_EventTile_actionBarFocused: this.state.actionBarFocused,
- mx_EventTile_verified: this.state.verified === true,
- mx_EventTile_unverified: this.state.verified === false,
+ mx_EventTile_verified: !isBubbleMessage && this.state.verified === true,
+ mx_EventTile_unverified: !isBubbleMessage && this.state.verified === false,
mx_EventTile_bad: isEncryptionFailure,
mx_EventTile_emote: msgtype === 'm.emote',
mx_EventTile_redacted: isRedacted,
@@ -800,7 +800,7 @@ module.exports = createReactClass({
{ timestamp }
- { this._renderE2EPadlock() }
+ { !isBubbleMessage && this._renderE2EPadlock() }
{ thread }
{ sender }
-
+
{ timestamp }
- { this._renderE2EPadlock() }
+ { !isBubbleMessage && this._renderE2EPadlock() }
{ thread }
{
if (!accepted) return;
- this.context.matrixClient.deactivateSynapseUser(this.props.member.userId);
+ this.context.matrixClient.deactivateSynapseUser(this.props.member.userId).catch(e => {
+ console.error("Failed to deactivate user");
+ console.error(e);
+
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createTrackedDialog('Failed to deactivate Synapse user', '', ErrorDialog, {
+ title: _t('Failed to deactivate user'),
+ description: ((e && e.message) ? e.message : _t("Operation failed")),
+ });
+ });
},
});
},
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index dc9773ad21..655c7030c4 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -118,6 +118,7 @@
"Restricted": "Restricted",
"Moderator": "Moderator",
"Admin": "Admin",
+ "Custom (%(level)s)": "Custom (%(level)s)",
"Start a chat": "Start a chat",
"Who would you like to communicate with?": "Who would you like to communicate with?",
"Email, name or Matrix ID": "Email, name or Matrix ID",
@@ -867,6 +868,7 @@
"Deactivate user?": "Deactivate user?",
"Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?",
"Deactivate user": "Deactivate user",
+ "Failed to deactivate user": "Failed to deactivate user",
"Failed to change power level": "Failed to change power level",
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.",
"Are you sure?": "Are you sure?",
@@ -1066,16 +1068,25 @@
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.",
"Members": "Members",
"Files": "Files",
- "Trust & Devices": "Trust & Devices",
- "Direct messages": "Direct messages",
+ "Trusted": "Trusted",
+ "Not trusted": "Not trusted",
+ "Hide verified Sign-In's": "Hide verified Sign-In's",
+ "%(count)s verified Sign-In's|other": "%(count)s verified Sign-In's",
+ "%(count)s verified Sign-In's|one": "1 verified Sign-In",
+ "Direct message": "Direct message",
+ "Unverify user": "Unverify user",
+ "Options": "Options",
"Remove from community": "Remove from community",
"Disinvite this user from community?": "Disinvite this user from community?",
"Remove this user from community?": "Remove this user from community?",
"Failed to withdraw invitation": "Failed to withdraw invitation",
"Failed to remove user from community": "Failed to remove user from community",
- "Failed to deactivate user": "Failed to deactivate user",
+ "%(role)s in %(roomName)s": "%(role)s in %(roomName)s",
"This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.",
"Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.",
+ "Messages in this room are end-to-end encrypted.": "Messages in this room are end-to-end encrypted.",
+ "Security": "Security",
+ "Verify": "Verify",
"Sunday": "Sunday",
"Monday": "Monday",
"Tuesday": "Tuesday",
@@ -1091,7 +1102,6 @@
"Reply": "Reply",
"Edit": "Edit",
"Message Actions": "Message Actions",
- "Options": "Options",
"Attachment": "Attachment",
"Error decrypting attachment": "Error decrypting attachment",
"Decrypt %(text)s": "Decrypt %(text)s",
diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js
index 980753551a..134870398f 100644
--- a/src/stores/RoomListStore.js
+++ b/src/stores/RoomListStore.js
@@ -515,7 +515,21 @@ class RoomListStore extends Store {
}
if (count !== 1) {
- console.warn(`!! Room ${room.roomId} inserted ${count} times`);
+ console.warn(`!! Room ${room.roomId} inserted ${count} times to ${targetTag}`);
+ }
+
+ // This is a workaround for https://github.com/vector-im/riot-web/issues/11303
+ // The logging is to try and identify what happened exactly.
+ if (count === 0) {
+ // Something went very badly wrong - try to recover the room.
+ // We don't bother checking how the target list is ordered - we're expecting
+ // to just insert it.
+ console.warn(`!! Recovering ${room.roomId} for tag ${targetTag} at position 0`);
+ if (!listsClone[targetTag]) {
+ console.warn(`!! List for tag ${targetTag} does not exist - creating`);
+ listsClone[targetTag] = [];
+ }
+ listsClone[targetTag].splice(0, 0, {room, category});
}
}
diff --git a/src/theme.js b/src/theme.js
index d479170792..8a15c606d7 100644
--- a/src/theme.js
+++ b/src/theme.js
@@ -60,6 +60,22 @@ function getCustomTheme(themeName) {
return customTheme;
}
+/**
+ * Gets the underlying theme name for the given theme. This is usually the theme or
+ * CSS resource that the theme relies upon to load.
+ * @param {string} theme The theme name to get the base of.
+ * @returns {string} The base theme (typically "light" or "dark").
+ */
+export function getBaseTheme(theme) {
+ if (!theme) return "light";
+ if (theme.startsWith("custom-")) {
+ const customTheme = getCustomTheme(theme.substr(7));
+ return customTheme.is_dark ? "dark-custom" : "light-custom";
+ }
+
+ return theme; // it's probably a base theme
+}
+
/**
* Called whenever someone changes the theme
*