Merge remote-tracking branch 'origin/develop' into dbkr/recovery_keys_over_passphrases
This commit is contained in:
commit
b82a893a79
54 changed files with 996 additions and 227 deletions
|
@ -24,6 +24,7 @@ import withValidation from '../elements/Validation';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import {Key} from "../../../Keyboard";
|
||||
import {privateShouldBeEncrypted} from "../../../createRoom";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'CreateRoomDialog',
|
||||
|
@ -36,7 +37,7 @@ export default createReactClass({
|
|||
const config = SdkConfig.get();
|
||||
return {
|
||||
isPublic: this.props.defaultPublic || false,
|
||||
isEncrypted: true,
|
||||
isEncrypted: privateShouldBeEncrypted(),
|
||||
name: "",
|
||||
topic: "",
|
||||
alias: "",
|
||||
|
@ -193,6 +194,13 @@ export default createReactClass({
|
|||
|
||||
let e2eeSection;
|
||||
if (!this.state.isPublic) {
|
||||
let microcopy;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
microcopy = _t("You can’t disable this later. Bridges & most bots won’t work yet.");
|
||||
} else {
|
||||
microcopy = _t("Your server admin has disabled end-to-end encryption by default " +
|
||||
"in private rooms & Direct Messages.");
|
||||
}
|
||||
e2eeSection = <React.Fragment>
|
||||
<LabelledToggleSwitch
|
||||
label={ _t("Enable end-to-end encryption")}
|
||||
|
@ -200,7 +208,7 @@ export default createReactClass({
|
|||
value={this.state.isEncrypted}
|
||||
className='mx_CreateRoomDialog_e2eSwitch' // for end-to-end tests
|
||||
/>
|
||||
<p>{ _t("You can’t disable this later. Bridges & most bots won’t work yet.") }</p>
|
||||
<p>{ microcopy }</p>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import dis from "../../../dispatcher/dispatcher";
|
|||
import IdentityAuthClient from "../../../IdentityAuthClient";
|
||||
import Modal from "../../../Modal";
|
||||
import {humanizeTime} from "../../../utils/humanize";
|
||||
import createRoom, {canEncryptToAllUsers} from "../../../createRoom";
|
||||
import createRoom, {canEncryptToAllUsers, privateShouldBeEncrypted} from "../../../createRoom";
|
||||
import {inviteMultipleToRoom} from "../../../RoomInvite";
|
||||
import {Key} from "../../../Keyboard";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
@ -575,14 +575,16 @@ export default class InviteDialog extends React.PureComponent {
|
|||
|
||||
const createRoomOptions = {inlineErrors: true};
|
||||
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
|
||||
if (!has3PidMembers) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
|
||||
if (!has3PidMembers) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ export default class UploadConfirmDialog extends React.Component {
|
|||
preview = <div>
|
||||
<div>
|
||||
<img className="mx_UploadConfirmDialog_fileIcon"
|
||||
src={require("../../../../res/img/files.png")}
|
||||
src={require("../../../../res/img/feather-customised/files.svg")}
|
||||
/>
|
||||
{this.props.file.name} ({filesize(this.props.file.size)})
|
||||
</div>
|
||||
|
|
|
@ -227,8 +227,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
loadError: null,
|
||||
});
|
||||
try {
|
||||
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||
const backupKeyStored = await MatrixClientPeg.get().isKeyBackupKeyStored();
|
||||
const cli = MatrixClientPeg.get();
|
||||
const backupInfo = await cli.getKeyBackupVersion();
|
||||
const has4S = await cli.hasSecretStorageKey();
|
||||
const backupKeyStored = has4S && await cli.isKeyBackupKeyStored();
|
||||
this.setState({
|
||||
backupInfo,
|
||||
backupKeyStored,
|
||||
|
|
|
@ -15,8 +15,6 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
|
@ -27,6 +25,7 @@ import AccessibleButton from "./AccessibleButton";
|
|||
import Modal from "../../../Modal";
|
||||
import * as sdk from "../../../index";
|
||||
import {Key} from "../../../Keyboard";
|
||||
import FocusLock from "react-focus-lock";
|
||||
|
||||
export default class ImageView extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -50,16 +49,6 @@ export default class ImageView extends React.Component {
|
|||
this.state = { rotationDegrees: 0 };
|
||||
}
|
||||
|
||||
// XXX: keyboard shortcuts for managing dialogs should be done by the modal
|
||||
// dialog base class somehow, surely...
|
||||
componentDidMount() {
|
||||
document.addEventListener("keydown", this.onKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
}
|
||||
|
||||
onKeyDown = (ev) => {
|
||||
if (ev.key === Key.ESCAPE) {
|
||||
ev.stopPropagation();
|
||||
|
@ -195,7 +184,14 @@ export default class ImageView extends React.Component {
|
|||
const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style};
|
||||
|
||||
return (
|
||||
<div className="mx_ImageView">
|
||||
<FocusLock
|
||||
returnFocus={true}
|
||||
lockProps={{
|
||||
onKeyDown: this.onKeyDown,
|
||||
role: "dialog",
|
||||
}}
|
||||
className="mx_ImageView"
|
||||
>
|
||||
<div className="mx_ImageView_lhs">
|
||||
</div>
|
||||
<div className="mx_ImageView_content">
|
||||
|
@ -231,7 +227,7 @@ export default class ImageView extends React.Component {
|
|||
</div>
|
||||
<div className="mx_ImageView_rhs">
|
||||
</div>
|
||||
</div>
|
||||
</FocusLock>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ export default class Tooltip extends React.Component<IProps> {
|
|||
return style;
|
||||
}
|
||||
|
||||
private renderTooltip() {
|
||||
private renderTooltip = () => {
|
||||
// Add the parent's position to the tooltips, so it's correctly
|
||||
// positioned, also taking into account any window zoom
|
||||
// NOTE: The additional 6 pixels for the left position, is to take account of the
|
||||
|
|
|
@ -27,8 +27,7 @@ const QUICK_REACTIONS = ["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀
|
|||
if (!data) {
|
||||
throw new Error(`Emoji ${emoji} doesn't exist in emojibase`);
|
||||
}
|
||||
// Prefer our unicode value for quick reactions as we sometimes use variation selectors.
|
||||
return Object.assign({}, data, { unicode: emoji });
|
||||
return data;
|
||||
});
|
||||
|
||||
class QuickReactions extends React.Component {
|
||||
|
|
|
@ -25,7 +25,7 @@ import dis from '../../../dispatcher/dispatcher';
|
|||
import Modal from '../../../Modal';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import createRoom from '../../../createRoom';
|
||||
import createRoom, {privateShouldBeEncrypted} from '../../../createRoom';
|
||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
|
@ -108,15 +108,17 @@ async function openDMForUser(matrixClient, userId) {
|
|||
dmUserId: userId,
|
||||
};
|
||||
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
|
||||
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
|
||||
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
|
||||
return Object.keys(devices).length > 0;
|
||||
});
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
|
||||
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
|
||||
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
|
||||
return Object.keys(devices).length > 0;
|
||||
});
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
}
|
||||
}
|
||||
|
||||
createRoom(createRoomOptions);
|
||||
|
|
|
@ -260,7 +260,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
|||
behavior: "auto",
|
||||
block: "nearest",
|
||||
});
|
||||
} else {
|
||||
} else if (this.containerRef.current) {
|
||||
this.containerRef.current.scrollTo({ top: 0 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ export function getHandlerTile(ev) {
|
|||
// fall back to showing hidden events, if we're viewing hidden events
|
||||
// XXX: This is extremely a hack. Possibly these components should have an interface for
|
||||
// declining to render?
|
||||
if (type === "m.key.verification.cancel" && SettingsStore.getValue("showHiddenEventsInTimeline")) {
|
||||
if (type === "m.key.verification.cancel" || type === "m.key.verification.done") {
|
||||
const MKeyVerificationConclusion = sdk.getComponent("messages.MKeyVerificationConclusion");
|
||||
if (!MKeyVerificationConclusion.prototype._shouldRender.call(null, ev, ev.request)) {
|
||||
return;
|
||||
|
|
|
@ -28,6 +28,8 @@ import { Dispatcher } from "flux";
|
|||
import dis from "../../../dispatcher/dispatcher";
|
||||
import RoomSublist2 from "./RoomSublist2";
|
||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
import { IFilterCondition } from "../../../stores/room-list/filters/IFilterCondition";
|
||||
import { NameFilterCondition } from "../../../stores/room-list/filters/NameFilterCondition";
|
||||
|
||||
/*******************************************************************
|
||||
* CAUTION *
|
||||
|
@ -130,6 +132,7 @@ export default class RoomList2 extends React.Component<IProps, IState> {
|
|||
private sublistCollapseStates: { [tagId: string]: boolean } = {};
|
||||
private unfilteredLayout: Layout;
|
||||
private filteredLayout: Layout;
|
||||
private searchFilter: NameFilterCondition = new NameFilterCondition();
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -139,6 +142,21 @@ export default class RoomList2 extends React.Component<IProps, IState> {
|
|||
this.prepareLayouts();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Readonly<IProps>): void {
|
||||
if (prevProps.searchFilter !== this.props.searchFilter) {
|
||||
const hadSearch = !!this.searchFilter.search.trim();
|
||||
const haveSearch = !!this.props.searchFilter.trim();
|
||||
this.searchFilter.search = this.props.searchFilter;
|
||||
if (!hadSearch && haveSearch) {
|
||||
// started a new filter - add the condition
|
||||
RoomListStore.instance.addFilter(this.searchFilter);
|
||||
} else if (hadSearch && !haveSearch) {
|
||||
// cleared a filter - remove the condition
|
||||
RoomListStore.instance.removeFilter(this.searchFilter);
|
||||
} // else the filter hasn't changed enough for us to care here
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
RoomListStore.instance.on(LISTS_UPDATE_EVENT, (store) => {
|
||||
console.log("new lists", store.orderedLists);
|
||||
|
|
|
@ -40,8 +40,8 @@ export default class ProfileSettings extends React.Component {
|
|||
if (avatarUrl) avatarUrl = client.mxcUrlToHttp(avatarUrl, 96, 96, 'crop', false);
|
||||
this.state = {
|
||||
userId: user.userId,
|
||||
originalDisplayName: user.displayName,
|
||||
displayName: user.displayName,
|
||||
originalDisplayName: user.rawDisplayName,
|
||||
displayName: user.rawDisplayName,
|
||||
originalAvatarUrl: avatarUrl,
|
||||
avatarUrl: avatarUrl,
|
||||
avatarFile: null,
|
||||
|
|
|
@ -26,6 +26,7 @@ import Modal from "../../../../../Modal";
|
|||
import * as sdk from "../../../../..";
|
||||
import {sleep} from "../../../../../utils/promise";
|
||||
import dis from "../../../../../dispatcher/dispatcher";
|
||||
import {privateShouldBeEncrypted} from "../../../../../createRoom";
|
||||
|
||||
export class IgnoredUser extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -317,8 +318,17 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
|
||||
const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel');
|
||||
|
||||
let warning;
|
||||
if (!privateShouldBeEncrypted()) {
|
||||
warning = <div className="mx_SecurityUserSettingsTab_warning">
|
||||
{ _t("Your server admin has disabled end-to-end encryption by default " +
|
||||
"in private rooms & Direct Messages.") }
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab mx_SecurityUserSettingsTab">
|
||||
{warning}
|
||||
<div className="mx_SettingsTab_heading">{_t("Security & Privacy")}</div>
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Where you’re logged in")}</span>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue