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

This commit is contained in:
Michael Telatynski 2020-04-23 10:12:56 +01:00
commit e06b5f8cf3
18 changed files with 184 additions and 128 deletions

View file

@ -446,7 +446,8 @@ export function bodyToHtml(content, highlights, opts={}) {
// their username. Permalinks (links in pills) can be any URL
// now, so we just check for an HTTP-looking thing.
(
content.formatted_body == undefined ||
strippedBody === safeBody || // replies have the html fallbacks, account for that here
content.formatted_body === undefined ||
(!content.formatted_body.includes("http:") &&
!content.formatted_body.includes("https:"))
);

View file

@ -83,7 +83,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
};
this._fetchBackupInfo();
this._queryKeyUploadAuth();
if (this.state.accountPassword) {
// If we have an account password in memory, let's simplify and
// assume it means password auth is also supported for device
// signing key upload as well. This avoids hitting the server to
// test auth flows, which may be slow under high load.
this.state.canUploadKeysWithPasswordOnly = true;
} else {
this._queryKeyUploadAuth();
}
MatrixClientPeg.get().on('crypto.keyBackupStatus', this._onKeyBackupStatusChange);
}

View file

@ -64,7 +64,7 @@ export default class AutocompleteProvider {
commandRegex = this.forcedCommandRegex || /\S+/g;
}
if (commandRegex === null) {
if (!commandRegex) {
return null;
}

View file

@ -102,7 +102,7 @@ export default class UserProvider extends AutocompleteProvider {
const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar');
// lazy-load user list into matcher
if (this.users === null) this._makeUsers();
if (!this.users) this._makeUsers();
let completions = [];
const {command, range} = this.getCurrentCommand(rawQuery, selection, force);
@ -158,7 +158,7 @@ export default class UserProvider extends AutocompleteProvider {
}
onUserSpoke(user: RoomMember) {
if (this.users === null) return;
if (!this.users) return;
if (!user) return;
if (user.userId === MatrixClientPeg.get().credentials.userId) return;

View file

@ -60,7 +60,7 @@ export default class CompleteSecurity extends React.Component {
if (phase === PHASE_INTRO) {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
title = _t("Verify this session");
title = _t("Verify this login");
} else if (phase === PHASE_DONE) {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_verified" />;
title = _t("Session verified");
@ -69,7 +69,7 @@ export default class CompleteSecurity extends React.Component {
title = _t("Are you sure?");
} else if (phase === PHASE_BUSY) {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
title = _t("Verify this session");
title = _t("Verify this login");
} else {
throw new Error(`Unknown phase ${phase}`);
}

View file

@ -108,31 +108,35 @@ export default class SetupEncryptionBody extends React.Component {
member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)}
/>;
} else if (phase === PHASE_INTRO) {
const ButtonPlaceholder = sdk.getComponent("elements.ButtonPlaceholder");
return (
<div>
<p>{_t(
"Use an existing session to verify this one, " +
"Confirm your identity by verifying this login from one of your other sessions, " +
"granting it access to encrypted messages.",
)}</p>
<p>{_t(
"If you cant access one, <button>use your recovery key or recovery passphrase.</button>",
{}, {
button: sub => <AccessibleButton element="span"
className="mx_linkButton"
onClick={this._onUsePassphraseClick}
>
{sub}
</AccessibleButton>,
})}</p>
"This requires the latest Riot on your other devices:",
)}</p>
<div className="mx_CompleteSecurity_clients">
<div className="mx_CompleteSecurity_clients_desktop">
<div>Riot Web</div>
<div>Riot Desktop</div>
</div>
<div className="mx_CompleteSecurity_clients_mobile">
<div>Riot iOS</div>
<div>Riot X for Android</div>
</div>
<p>{_t("or another cross-signing capable Matrix client")}</p>
</div>
<div className="mx_CompleteSecurity_actionRow">
<AccessibleButton
kind="danger"
onClick={this.onSkipClick}
>
<AccessibleButton kind="link" onClick={this.onSkipClick}>
{_t("Use Recovery Passphrase or Key")}
</AccessibleButton>
<AccessibleButton kind="danger" onClick={this.onSkipClick}>
{_t("Skip")}
</AccessibleButton>
<ButtonPlaceholder>{_t("Use your other device to continue…")}</ButtonPlaceholder>
</div>
</div>
);
@ -150,7 +154,7 @@ export default class SetupEncryptionBody extends React.Component {
}
return (
<div>
<div className="mx_CompleteSecurity_heroIcon mx_E2EIcon_verified"></div>
<div className="mx_CompleteSecurity_heroIcon mx_E2EIcon_verified" />
{message}
<div className="mx_CompleteSecurity_actionRow">
<AccessibleButton

View file

@ -102,11 +102,15 @@ export default createReactClass({
"No identity server is configured so you cannot add an email address in order to " +
"reset your password in the future.",
);
} else {
} else if (this._showEmail()) {
desc = _t(
"If you don't specify an email address, you won't be able to reset your password. " +
"Are you sure?",
);
} else {
// user can't set an e-mail so don't prompt them to
self._doSubmit(ev);
return;
}
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");

View file

@ -26,30 +26,6 @@ import { _t } from '../../../languageHandler';
import InteractiveAuth, {ERROR_USER_CANCELLED} from "../../structures/InteractiveAuth";
import {DEFAULT_PHASE, PasswordAuthEntry, SSOAuthEntry} from "../auth/InteractiveAuthEntryComponents";
const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: {
body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."),
continueText: _t("Single Sign On"),
continueKind: "danger",
},
[SSOAuthEntry.PHASE_POSTAUTH]: {
body: _t("Are you sure you want to deactivate your account? This is irreversible."),
continueText: _t("Confirm account deactivation"),
continueKind: "danger",
},
};
// This is the same as aestheticsForStagePhases in InteractiveAuthDialog minus the `title`
const DEACTIVATE_AESTHETICS = {
[SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics,
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics,
[PasswordAuthEntry.LOGIN_TYPE]: {
[DEFAULT_PHASE]: {
body: _t("To continue, please enter your password:"),
},
},
};
export default class DeactivateAccountDialog extends React.Component {
constructor(props) {
super(props);
@ -84,6 +60,30 @@ export default class DeactivateAccountDialog extends React.Component {
}
_onStagePhaseChange = (stage, phase) => {
const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: {
body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."),
continueText: _t("Single Sign On"),
continueKind: "danger",
},
[SSOAuthEntry.PHASE_POSTAUTH]: {
body: _t("Are you sure you want to deactivate your account? This is irreversible."),
continueText: _t("Confirm account deactivation"),
continueKind: "danger",
},
};
// This is the same as aestheticsForStagePhases in InteractiveAuthDialog minus the `title`
const DEACTIVATE_AESTHETICS = {
[SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics,
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics,
[PasswordAuthEntry.LOGIN_TYPE]: {
[DEFAULT_PHASE]: {
body: _t("To continue, please enter your password:"),
},
},
};
const aesthetics = DEACTIVATE_AESTHETICS[stage];
let bodyText = null;
let continueText = null;

View file

@ -1,19 +0,0 @@
/*
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.
*/
export default function ButtonPlaceholder(props) {
return <div className="mx_ButtonPlaceholder">{props.children}</div>;
}

View file

@ -259,7 +259,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`];
if (selectedCompletion && this.containerRef.current) {
const domNode = ReactDOM.findDOMNode(selectedCompletion);
const offsetTop = domNode && domNode.offsetTop;
const offsetTop = domNode && (domNode as HTMLElement).offsetTop;
if (offsetTop > this.containerRef.current.scrollTop + this.containerRef.current.offsetHeight ||
offsetTop < this.containerRef.current.scrollTop) {
this.containerRef.current.scrollTop = offsetTop - this.containerRef.current.offsetTop;

View file

@ -25,11 +25,13 @@ import Analytics from "../../../../../Analytics";
import Modal from "../../../../../Modal";
import * as sdk from "../../../../..";
import {sleep} from "../../../../../utils/promise";
import dis from "../../../../../dispatcher";
export class IgnoredUser extends React.Component {
static propTypes = {
userId: PropTypes.string.isRequired,
onUnignored: PropTypes.func.isRequired,
inProgress: PropTypes.bool.isRequired,
};
_onUnignoreClicked = (e) => {
@ -40,7 +42,7 @@ export class IgnoredUser extends React.Component {
const id = `mx_SecurityUserSettingsTab_ignoredUser_${this.props.userId}`;
return (
<div className='mx_SecurityUserSettingsTab_ignoredUser'>
<AccessibleButton onClick={this._onUnignoreClicked} kind='primary_sm' aria-describedby={id}>
<AccessibleButton onClick={this._onUnignoreClicked} kind='primary_sm' aria-describedby={id} disabled={this.props.inProgress}>
{ _t('Unignore') }
</AccessibleButton>
<span id={id}>{ this.props.userId }</span>
@ -58,9 +60,29 @@ export default class SecurityUserSettingsTab extends React.Component {
this.state = {
ignoredUserIds: MatrixClientPeg.get().getIgnoredUsers(),
waitingUnignored: [],
managingInvites: false,
invitedRoomAmt: invitedRooms.length,
};
this._onAction = this._onAction.bind(this);
}
_onAction({action}) {
if (action === "ignore_state_changed") {
const ignoredUserIds = MatrixClientPeg.get().getIgnoredUsers();
const newWaitingUnignored = this.state.waitingUnignored.filter(e=> ignoredUserIds.includes(e));
this.setState({ignoredUserIds, waitingUnignored: newWaitingUnignored});
}
}
componentDidMount() {
this.dispatcherRef = dis.register(this._onAction);
}
componentWillUnmount() {
dis.unregister(this.dispatcherRef);
}
_updateBlacklistDevicesFlag = (checked) => {
@ -86,16 +108,15 @@ export default class SecurityUserSettingsTab extends React.Component {
};
_onUserUnignored = async (userId) => {
// Don't use this.state to get the ignored user list as it might be
// ever so slightly outdated. Instead, prefer to get a fresh list and
// update that.
const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers();
const index = ignoredUsers.indexOf(userId);
const {ignoredUserIds, waitingUnignored} = this.state;
const currentlyIgnoredUserIds = ignoredUserIds.filter(e => !waitingUnignored.includes(e));
const index = currentlyIgnoredUserIds.indexOf(userId);
if (index !== -1) {
ignoredUsers.splice(index, 1);
MatrixClientPeg.get().setIgnoredUsers(ignoredUsers);
currentlyIgnoredUserIds.splice(index, 1);
this.setState(({waitingUnignored}) => ({waitingUnignored: [...waitingUnignored, userId]}));
MatrixClientPeg.get().setIgnoredUsers(currentlyIgnoredUserIds);
}
this.setState({ignoredUsers});
};
_getInvitedRooms = () => {
@ -201,10 +222,17 @@ export default class SecurityUserSettingsTab extends React.Component {
}
_renderIgnoredUsers() {
if (!this.state.ignoredUserIds || this.state.ignoredUserIds.length === 0) return null;
const {waitingUnignored, ignoredUserIds} = this.state;
const userIds = this.state.ignoredUserIds
.map((u) => <IgnoredUser userId={u} onUnignored={this._onUserUnignored} key={u} />);
if (!ignoredUserIds || ignoredUserIds.length === 0) return null;
const userIds = ignoredUserIds
.map((u) => <IgnoredUser
userId={u}
onUnignored={this._onUserUnignored}
key={u}
inProgress={waitingUnignored.includes(u)}
/>);
return (
<div className='mx_SettingsTab_section'>

View file

@ -1580,12 +1580,12 @@
"You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ": "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ",
"Incompatible Database": "Incompatible Database",
"Continue With Encryption Disabled": "Continue With Encryption Disabled",
"Server did not require any authentication": "Server did not require any authentication",
"Server did not return valid authentication information.": "Server did not return valid authentication information.",
"Confirm your account deactivation by using Single Sign On to prove your identity.": "Confirm your account deactivation by using Single Sign On to prove your identity.",
"Are you sure you want to deactivate your account? This is irreversible.": "Are you sure you want to deactivate your account? This is irreversible.",
"Confirm account deactivation": "Confirm account deactivation",
"To continue, please enter your password:": "To continue, please enter your password:",
"Server did not require any authentication": "Server did not require any authentication",
"Server did not return valid authentication information.": "Server did not return valid authentication information.",
"There was a problem communicating with the server. Please try again.": "There was a problem communicating with the server. Please try again.",
"This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>": "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. <b>This action is irreversible.</b>",
"Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.": "Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.",
@ -2067,6 +2067,7 @@
"Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
"Could not load user profile": "Could not load user profile",
"Verify this login": "Verify this login",
"Session verified": "Session verified",
"Failed to send email": "Failed to send email",
"The email address linked to your account must be entered.": "The email address linked to your account must be entered.",
@ -2120,9 +2121,10 @@
"You can now close this window or <a>log in</a> to your new account.": "You can now close this window or <a>log in</a> to your new account.",
"Registration Successful": "Registration Successful",
"Create your account": "Create your account",
"Use an existing session to verify this one, granting it access to encrypted messages.": "Use an existing session to verify this one, granting it access to encrypted messages.",
"If you cant access one, <button>use your recovery key or recovery passphrase.</button>": "If you cant access one, <button>use your recovery key or recovery passphrase.</button>",
"Use your other device to continue…": "Use your other device to continue…",
"Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.",
"This requires the latest Riot on your other devices:": "This requires the latest Riot on your other devices:",
"or another cross-signing capable Matrix client": "or another cross-signing capable Matrix client",
"Use Recovery Passphrase or Key": "Use Recovery Passphrase or Key",
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
"Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.",
"Without completing security on this session, it wont have access to encrypted messages.": "Without completing security on this session, it wont have access to encrypted messages.",