Merge branches 'develop' and 't3chguy/cmds' of github.com:matrix-org/matrix-react-sdk into t3chguy/cmds
Conflicts: src/SlashCommands.tsx
This commit is contained in:
commit
6e61761012
21 changed files with 498 additions and 111 deletions
156
CHANGELOG.md
156
CHANGELOG.md
|
@ -1,3 +1,159 @@
|
||||||
|
Changes in [2.3.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.3.0) (2020-03-30)
|
||||||
|
===================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.3.0-rc.1...v2.3.0)
|
||||||
|
|
||||||
|
* Upgrade JS SDK to 5.2.0
|
||||||
|
|
||||||
|
Changes in [2.3.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.3.0-rc.1) (2020-03-26)
|
||||||
|
=============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3...v2.3.0-rc.1)
|
||||||
|
|
||||||
|
* Upgrade JS SDK to 5.2.0-rc.1
|
||||||
|
* Add a flag to control whether cross-signing signatures are trusted
|
||||||
|
[\#4277](https://github.com/matrix-org/matrix-react-sdk/pull/4277)
|
||||||
|
* Update from Weblate
|
||||||
|
[\#4282](https://github.com/matrix-org/matrix-react-sdk/pull/4282)
|
||||||
|
* Update copy on SSSS symmetric upgrade toast
|
||||||
|
[\#4281](https://github.com/matrix-org/matrix-react-sdk/pull/4281)
|
||||||
|
* Wait for SSSS upgrade to complete
|
||||||
|
[\#4270](https://github.com/matrix-org/matrix-react-sdk/pull/4270)
|
||||||
|
* Update cross-signing verification copy and fix i18n
|
||||||
|
[\#4278](https://github.com/matrix-org/matrix-react-sdk/pull/4278)
|
||||||
|
* Fix soft-crash on bad permalinks
|
||||||
|
[\#4280](https://github.com/matrix-org/matrix-react-sdk/pull/4280)
|
||||||
|
* Fix: make self-verification wait for incoming request
|
||||||
|
[\#4267](https://github.com/matrix-org/matrix-react-sdk/pull/4267)
|
||||||
|
* Fall back to non-standard persisted api for Safari
|
||||||
|
[\#4272](https://github.com/matrix-org/matrix-react-sdk/pull/4272)
|
||||||
|
* Respond to backup key sharing requests
|
||||||
|
[\#4275](https://github.com/matrix-org/matrix-react-sdk/pull/4275)
|
||||||
|
* Log and display secret sharing cache state
|
||||||
|
[\#4268](https://github.com/matrix-org/matrix-react-sdk/pull/4268)
|
||||||
|
* Support sending config and ready events to capable widgets (Jitsi)
|
||||||
|
[\#4266](https://github.com/matrix-org/matrix-react-sdk/pull/4266)
|
||||||
|
* If cached keys are present in the key backup dialog, use them
|
||||||
|
[\#4273](https://github.com/matrix-org/matrix-react-sdk/pull/4273)
|
||||||
|
* Fix formatbar not hidden on highlighted message sent
|
||||||
|
[\#4265](https://github.com/matrix-org/matrix-react-sdk/pull/4265)
|
||||||
|
* Support Jitsi conferences sent/received on Riot Mobile and older Riot Webs
|
||||||
|
[\#4252](https://github.com/matrix-org/matrix-react-sdk/pull/4252)
|
||||||
|
* Use unified function to check cross-signing is ready
|
||||||
|
[\#4263](https://github.com/matrix-org/matrix-react-sdk/pull/4263)
|
||||||
|
* Migrate SSSS to symmetric
|
||||||
|
[\#4224](https://github.com/matrix-org/matrix-react-sdk/pull/4224)
|
||||||
|
* Migration to symmetric SSSS
|
||||||
|
[\#4242](https://github.com/matrix-org/matrix-react-sdk/pull/4242)
|
||||||
|
* Always display verification request toasts on top
|
||||||
|
[\#4262](https://github.com/matrix-org/matrix-react-sdk/pull/4262)
|
||||||
|
* Fix: assume SAS is supported when starting request with .start
|
||||||
|
[\#4249](https://github.com/matrix-org/matrix-react-sdk/pull/4249)
|
||||||
|
* Fix logout when Olm failed to load.
|
||||||
|
[\#4261](https://github.com/matrix-org/matrix-react-sdk/pull/4261)
|
||||||
|
* Improve naming of Jitsi conferences
|
||||||
|
[\#4251](https://github.com/matrix-org/matrix-react-sdk/pull/4251)
|
||||||
|
* Handle matrix.to user permalink in-room rather than solo
|
||||||
|
[\#4245](https://github.com/matrix-org/matrix-react-sdk/pull/4245)
|
||||||
|
* Fix: filter room list (again) by canonical and alternative aliases
|
||||||
|
[\#4260](https://github.com/matrix-org/matrix-react-sdk/pull/4260)
|
||||||
|
* EventIndex: Add some logging to the file panel populating.
|
||||||
|
[\#4250](https://github.com/matrix-org/matrix-react-sdk/pull/4250)
|
||||||
|
* Update from Weblate
|
||||||
|
[\#4259](https://github.com/matrix-org/matrix-react-sdk/pull/4259)
|
||||||
|
* Migrate RoomView to React Contexts in the hope for better temporal stability
|
||||||
|
[\#4258](https://github.com/matrix-org/matrix-react-sdk/pull/4258)
|
||||||
|
* Update WidgetUtils.js fix Jitsi path
|
||||||
|
[\#4256](https://github.com/matrix-org/matrix-react-sdk/pull/4256)
|
||||||
|
* Fix local jitsi build url fail and missing argument
|
||||||
|
[\#4255](https://github.com/matrix-org/matrix-react-sdk/pull/4255)
|
||||||
|
* Add shortcut CmdOrCtrl+. to toggle right panel
|
||||||
|
[\#4244](https://github.com/matrix-org/matrix-react-sdk/pull/4244)
|
||||||
|
* Improve Keyboard Shortcuts. Add alt-arrows & alt-shift-arrows
|
||||||
|
[\#4241](https://github.com/matrix-org/matrix-react-sdk/pull/4241)
|
||||||
|
* Bring back legacy verification by comparing public device keys
|
||||||
|
[\#4240](https://github.com/matrix-org/matrix-react-sdk/pull/4240)
|
||||||
|
* Searching: Return an empty result if the search term is an empty string.
|
||||||
|
[\#4248](https://github.com/matrix-org/matrix-react-sdk/pull/4248)
|
||||||
|
* Break continuation on showHiddenEvents-rendered events
|
||||||
|
[\#4247](https://github.com/matrix-org/matrix-react-sdk/pull/4247)
|
||||||
|
* Watch for show-RR settings changes, use room-specific and fix margins
|
||||||
|
[\#4246](https://github.com/matrix-org/matrix-react-sdk/pull/4246)
|
||||||
|
* Register Mac electron specific Cmd+, shortcut to User Settings
|
||||||
|
[\#4243](https://github.com/matrix-org/matrix-react-sdk/pull/4243)
|
||||||
|
* Use a local wrapper for Jitsi calls
|
||||||
|
[\#4234](https://github.com/matrix-org/matrix-react-sdk/pull/4234)
|
||||||
|
* Invite Dialog fixes
|
||||||
|
[\#4233](https://github.com/matrix-org/matrix-react-sdk/pull/4233)
|
||||||
|
* RoomPreviewBar word-break the sender name too
|
||||||
|
[\#4239](https://github.com/matrix-org/matrix-react-sdk/pull/4239)
|
||||||
|
* Report to the user when a key signature upload fails
|
||||||
|
[\#4229](https://github.com/matrix-org/matrix-react-sdk/pull/4229)
|
||||||
|
* pre-send megolm keys when possible when a user starts typing
|
||||||
|
[\#4235](https://github.com/matrix-org/matrix-react-sdk/pull/4235)
|
||||||
|
* we don't do mx_fadable anymore so get rid of broken RightPanel disabling
|
||||||
|
[\#4238](https://github.com/matrix-org/matrix-react-sdk/pull/4238)
|
||||||
|
* Fix left left panel overflowing vertically
|
||||||
|
[\#4237](https://github.com/matrix-org/matrix-react-sdk/pull/4237)
|
||||||
|
* Fix custom tags causing left panel to over-expand
|
||||||
|
[\#4236](https://github.com/matrix-org/matrix-react-sdk/pull/4236)
|
||||||
|
* Add Keyboard shortcuts dialog
|
||||||
|
[\#4231](https://github.com/matrix-org/matrix-react-sdk/pull/4231)
|
||||||
|
* Don't use buildkite agent to upload logs
|
||||||
|
[\#4232](https://github.com/matrix-org/matrix-react-sdk/pull/4232)
|
||||||
|
* Remove Gemini Scrollbars
|
||||||
|
[\#4217](https://github.com/matrix-org/matrix-react-sdk/pull/4217)
|
||||||
|
* Room Directory Explore Servers redesign
|
||||||
|
[\#4209](https://github.com/matrix-org/matrix-react-sdk/pull/4209)
|
||||||
|
* Fix redo keyboard shortcut on macOS
|
||||||
|
[\#4110](https://github.com/matrix-org/matrix-react-sdk/pull/4110)
|
||||||
|
* Fix: ensure local state for aliases doesn't get garbled up
|
||||||
|
[\#4230](https://github.com/matrix-org/matrix-react-sdk/pull/4230)
|
||||||
|
* Rename 'jump to bottom' to avoid ublock block
|
||||||
|
[\#4208](https://github.com/matrix-org/matrix-react-sdk/pull/4208)
|
||||||
|
* Restore key backup in background after complete security
|
||||||
|
[\#4225](https://github.com/matrix-org/matrix-react-sdk/pull/4225)
|
||||||
|
* Fix key backup trust text for cross-signing
|
||||||
|
[\#4223](https://github.com/matrix-org/matrix-react-sdk/pull/4223)
|
||||||
|
* Add default on config setting to control call button in composer
|
||||||
|
[\#4227](https://github.com/matrix-org/matrix-react-sdk/pull/4227)
|
||||||
|
* Fix: make alternative addresses UX less confusing
|
||||||
|
[\#4221](https://github.com/matrix-org/matrix-react-sdk/pull/4221)
|
||||||
|
* Wait for verification request on login
|
||||||
|
[\#4222](https://github.com/matrix-org/matrix-react-sdk/pull/4222)
|
||||||
|
* EventIndex: Add support to delete events from the index.
|
||||||
|
[\#4204](https://github.com/matrix-org/matrix-react-sdk/pull/4204)
|
||||||
|
* EventIndex: Remove a checkpoint if the HTTP request returns a 403.
|
||||||
|
[\#4214](https://github.com/matrix-org/matrix-react-sdk/pull/4214)
|
||||||
|
* Move to composer when typing letters with Shift held
|
||||||
|
[\#4216](https://github.com/matrix-org/matrix-react-sdk/pull/4216)
|
||||||
|
* Wrap large room names when previewing them
|
||||||
|
[\#4213](https://github.com/matrix-org/matrix-react-sdk/pull/4213)
|
||||||
|
* Rename Review Devices to Review Sessions
|
||||||
|
[\#4219](https://github.com/matrix-org/matrix-react-sdk/pull/4219)
|
||||||
|
* Fix typo in tabIndex to make React happy
|
||||||
|
[\#4215](https://github.com/matrix-org/matrix-react-sdk/pull/4215)
|
||||||
|
* Proof of concept for custom theme adding
|
||||||
|
[\#4148](https://github.com/matrix-org/matrix-react-sdk/pull/4148)
|
||||||
|
* Remove stuff that yarn install doesn't think we need
|
||||||
|
[\#4205](https://github.com/matrix-org/matrix-react-sdk/pull/4205)
|
||||||
|
* Declare jsx in tsconfig for IDEs
|
||||||
|
[\#4207](https://github.com/matrix-org/matrix-react-sdk/pull/4207)
|
||||||
|
* Fix: best-effort to join room without canonical alias over federation from
|
||||||
|
room directory
|
||||||
|
[\#4210](https://github.com/matrix-org/matrix-react-sdk/pull/4210)
|
||||||
|
* Test for cross-signing homeserver support during login, toasts
|
||||||
|
[\#4206](https://github.com/matrix-org/matrix-react-sdk/pull/4206)
|
||||||
|
* Send verification request to a single device in a way compatible with non-
|
||||||
|
cross-signing
|
||||||
|
[\#4202](https://github.com/matrix-org/matrix-react-sdk/pull/4202)
|
||||||
|
* Fixes for removing local alias
|
||||||
|
[\#4199](https://github.com/matrix-org/matrix-react-sdk/pull/4199)
|
||||||
|
* yarn upgrade
|
||||||
|
[\#4201](https://github.com/matrix-org/matrix-react-sdk/pull/4201)
|
||||||
|
* Support TypeScript for React components
|
||||||
|
[\#4203](https://github.com/matrix-org/matrix-react-sdk/pull/4203)
|
||||||
|
* When room name is changed, show both the old and new name
|
||||||
|
[\#4183](https://github.com/matrix-org/matrix-react-sdk/pull/4183)
|
||||||
|
|
||||||
Changes in [2.2.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.3) (2020-03-17)
|
Changes in [2.2.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.3) (2020-03-17)
|
||||||
===================================================================================================
|
===================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3-rc.1...v2.2.3)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3-rc.1...v2.2.3)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "2.2.3",
|
"version": "2.3.0",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -137,7 +137,7 @@ limitations under the License.
|
||||||
top: -8px;
|
top: -8px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: $neutral-badge-color;
|
background-color: $neutral-badge-color;
|
||||||
color: #ffffff;
|
color: #000;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -430,7 +430,7 @@ async function _startCallApp(roomId, type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confId = `JitsiConference_${generateHumanReadableId()}`;
|
const confId = `JitsiConference${generateHumanReadableId()}`;
|
||||||
const jitsiDomain = SdkConfig.get()['jitsi']['preferredDomain'];
|
const jitsiDomain = SdkConfig.get()['jitsi']['preferredDomain'];
|
||||||
|
|
||||||
let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl();
|
let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl();
|
||||||
|
|
|
@ -85,7 +85,7 @@ class Command {
|
||||||
aliases = [],
|
aliases = [],
|
||||||
args = '',
|
args = '',
|
||||||
description,
|
description,
|
||||||
runFn=undefined,
|
runFn = undefined,
|
||||||
category = CommandCategories.other,
|
category = CommandCategories.other,
|
||||||
hideCompletionAfterSpace = false,
|
hideCompletionAfterSpace = false,
|
||||||
}: {
|
}: {
|
||||||
|
@ -160,6 +160,15 @@ export const Commands = [
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
|
command: 'html',
|
||||||
|
args: '<message>',
|
||||||
|
description: _td('Sends a message as html, without interpreting it as markdown'),
|
||||||
|
runFn: function(roomId, messages) {
|
||||||
|
return success(MatrixClientPeg.get().sendHtmlMessage(roomId, messages, messages));
|
||||||
|
},
|
||||||
|
category: CommandCategories.messages,
|
||||||
|
}),
|
||||||
new Command({
|
new Command({
|
||||||
command: 'ddg',
|
command: 'ddg',
|
||||||
args: '<query>',
|
args: '<query>',
|
||||||
|
|
|
@ -67,8 +67,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._keyInfo = null;
|
this._recoveryKey = null;
|
||||||
this._encodedRecoveryKey = null;
|
|
||||||
this._recoveryKeyNode = null;
|
this._recoveryKeyNode = null;
|
||||||
this._setZxcvbnResultTimeout = null;
|
this._setZxcvbnResultTimeout = null;
|
||||||
|
|
||||||
|
@ -180,7 +179,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDownloadClick = () => {
|
_onDownloadClick = () => {
|
||||||
const blob = new Blob([this._encodedRecoveryKey], {
|
const blob = new Blob([this._recoveryKey.encodedPrivateKey], {
|
||||||
type: 'text/plain;charset=us-ascii',
|
type: 'text/plain;charset=us-ascii',
|
||||||
});
|
});
|
||||||
FileSaver.saveAs(blob, 'recovery-key.txt');
|
FileSaver.saveAs(blob, 'recovery-key.txt');
|
||||||
|
@ -234,14 +233,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
if (force) {
|
if (force) {
|
||||||
await cli.bootstrapSecretStorage({
|
await cli.bootstrapSecretStorage({
|
||||||
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
||||||
createSecretStorageKey: async () => this._keyInfo,
|
createSecretStorageKey: async () => this._recoveryKey,
|
||||||
setupNewKeyBackup: true,
|
setupNewKeyBackup: true,
|
||||||
setupNewSecretStorage: true,
|
setupNewSecretStorage: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await cli.bootstrapSecretStorage({
|
await cli.bootstrapSecretStorage({
|
||||||
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
authUploadDeviceSigningKeys: this._doBootstrapUIAuth,
|
||||||
createSecretStorageKey: async () => this._keyInfo,
|
createSecretStorageKey: async () => this._recoveryKey,
|
||||||
keyBackupInfo: this.state.backupInfo,
|
keyBackupInfo: this.state.backupInfo,
|
||||||
setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup,
|
setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup,
|
||||||
getKeyBackupPassphrase: promptForBackupPassphrase,
|
getKeyBackupPassphrase: promptForBackupPassphrase,
|
||||||
|
@ -299,10 +298,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSkipPassPhraseClick = async () => {
|
_onSkipPassPhraseClick = async () => {
|
||||||
const [keyInfo, encodedRecoveryKey] =
|
this._recoveryKey =
|
||||||
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
|
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
|
||||||
this._keyInfo = keyInfo;
|
|
||||||
this._encodedRecoveryKey = encodedRecoveryKey;
|
|
||||||
this.setState({
|
this.setState({
|
||||||
copied: false,
|
copied: false,
|
||||||
downloaded: false,
|
downloaded: false,
|
||||||
|
@ -335,10 +332,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
|
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
|
||||||
|
|
||||||
const [keyInfo, encodedRecoveryKey] =
|
this._recoveryKey =
|
||||||
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
|
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
|
||||||
this._keyInfo = keyInfo;
|
|
||||||
this._encodedRecoveryKey = encodedRecoveryKey;
|
|
||||||
this.setState({
|
this.setState({
|
||||||
copied: false,
|
copied: false,
|
||||||
downloaded: false,
|
downloaded: false,
|
||||||
|
@ -613,7 +608,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
|
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKey">
|
<div className="mx_CreateSecretStorageDialog_recoveryKey">
|
||||||
<code ref={this._collectRecoveryKeyNode}>{this._encodedRecoveryKey}</code>
|
<code ref={this._collectRecoveryKeyNode}>{this._recoveryKey.encodedPrivateKey}</code>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
||||||
<AccessibleButton kind='primary' className="mx_Dialog_primary" onClick={this._onCopyClick}>
|
<AccessibleButton kind='primary' className="mx_Dialog_primary" onClick={this._onCopyClick}>
|
||||||
|
|
|
@ -37,6 +37,8 @@ export default class EmbeddedPage extends React.PureComponent {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
// Whether to wrap the page in a scrollbar
|
// Whether to wrap the page in a scrollbar
|
||||||
scrollbar: PropTypes.bool,
|
scrollbar: PropTypes.bool,
|
||||||
|
// Map of keys to replace with values, e.g {$placeholder: "value"}
|
||||||
|
replaceMap: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextType = MatrixClientContext;
|
static contextType = MatrixClientContext;
|
||||||
|
@ -81,6 +83,13 @@ export default class EmbeddedPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1));
|
body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1));
|
||||||
|
|
||||||
|
if (this.props.replaceMap) {
|
||||||
|
Object.keys(this.props.replaceMap).forEach(key => {
|
||||||
|
body = body.split(key).join(this.props.replaceMap[key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ page: body });
|
this.setState({ page: body });
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -2022,7 +2022,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
} else if (this.state.view === VIEWS.WELCOME) {
|
} else if (this.state.view === VIEWS.WELCOME) {
|
||||||
const Welcome = sdk.getComponent('auth.Welcome');
|
const Welcome = sdk.getComponent('auth.Welcome');
|
||||||
view = <Welcome />;
|
view = <Welcome {...this.getServerProperties()} />;
|
||||||
} else if (this.state.view === VIEWS.REGISTER) {
|
} else if (this.state.view === VIEWS.REGISTER) {
|
||||||
const Registration = sdk.getComponent('structures.auth.Registration');
|
const Registration = sdk.getComponent('structures.auth.Registration');
|
||||||
view = (
|
view = (
|
||||||
|
|
|
@ -55,6 +55,7 @@ import RightPanelStore from "../../stores/RightPanelStore";
|
||||||
import {haveTileForEvent} from "../views/rooms/EventTile";
|
import {haveTileForEvent} from "../views/rooms/EventTile";
|
||||||
import RoomContext from "../../contexts/RoomContext";
|
import RoomContext from "../../contexts/RoomContext";
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
|
import { shieldStatusForRoom } from '../../utils/ShieldUtils';
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
let debuglog = function() {};
|
let debuglog = function() {};
|
||||||
|
@ -817,40 +818,9 @@ export default createReactClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplication between here and _updateE2eStatus in RoomTile
|
|
||||||
/* At this point, the user has encryption on and cross-signing on */
|
/* At this point, the user has encryption on and cross-signing on */
|
||||||
const e2eMembers = await room.getEncryptionTargetMembers();
|
|
||||||
const verified = [];
|
|
||||||
const unverified = [];
|
|
||||||
e2eMembers.map(({userId}) => userId)
|
|
||||||
.filter((userId) => userId !== this.context.getUserId())
|
|
||||||
.forEach((userId) => {
|
|
||||||
(this.context.checkUserTrust(userId).isCrossSigningVerified() ?
|
|
||||||
verified : unverified).push(userId);
|
|
||||||
});
|
|
||||||
|
|
||||||
debuglog("e2e verified", verified, "unverified", unverified);
|
|
||||||
|
|
||||||
/* Check all verified user devices. */
|
|
||||||
/* Don't alarm if no other users are verified */
|
|
||||||
const targets = (verified.length > 0) ? [...verified, this.context.getUserId()] : verified;
|
|
||||||
for (const userId of targets) {
|
|
||||||
const devices = await this.context.getStoredDevicesForUser(userId);
|
|
||||||
const anyDeviceNotVerified = devices.some(({deviceId}) => {
|
|
||||||
return !this.context.checkDeviceTrust(userId, deviceId).isVerified();
|
|
||||||
});
|
|
||||||
if (anyDeviceNotVerified) {
|
|
||||||
this.setState({
|
|
||||||
e2eStatus: "warning",
|
|
||||||
});
|
|
||||||
debuglog("e2e status set to warning as not all users trust all of their sessions." +
|
|
||||||
" Aborted on user", userId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
e2eStatus: unverified.length === 0 ? "verified" : "normal",
|
e2eStatus: await shieldStatusForRoom(this.context, room),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,12 @@ import React from 'react';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import AuthPage from "./AuthPage";
|
import AuthPage from "./AuthPage";
|
||||||
|
import * as Matrix from "matrix-js-sdk";
|
||||||
|
import {_td} from "../../../languageHandler";
|
||||||
|
import PlatformPeg from "../../../PlatformPeg";
|
||||||
|
|
||||||
|
// translatable strings for Welcome pages
|
||||||
|
_td("Sign in with SSO");
|
||||||
|
|
||||||
export default class Welcome extends React.PureComponent {
|
export default class Welcome extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
|
@ -33,11 +39,24 @@ export default class Welcome extends React.PureComponent {
|
||||||
pageUrl = 'welcome.html';
|
pageUrl = 'welcome.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {hsUrl, isUrl} = this.props.serverConfig;
|
||||||
|
const tmpClient = Matrix.createClient({
|
||||||
|
baseUrl: hsUrl,
|
||||||
|
idBaseUrl: isUrl,
|
||||||
|
});
|
||||||
|
const plaf = PlatformPeg.get();
|
||||||
|
const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthPage>
|
<AuthPage>
|
||||||
<div className="mx_Welcome">
|
<div className="mx_Welcome">
|
||||||
<EmbeddedPage className="mx_WelcomePage"
|
<EmbeddedPage
|
||||||
|
className="mx_WelcomePage"
|
||||||
url={pageUrl}
|
url={pageUrl}
|
||||||
|
replaceMap={{
|
||||||
|
"$riot:ssoUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "sso"),
|
||||||
|
"$riot:casUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "cas"),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,6 +20,7 @@ import MemberAvatar from '../avatars/MemberAvatar';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixEvent, RoomMember} from "matrix-js-sdk";
|
import {MatrixEvent, RoomMember} from "matrix-js-sdk";
|
||||||
import {useStateToggle} from "../../../hooks/useStateToggle";
|
import {useStateToggle} from "../../../hooks/useStateToggle";
|
||||||
|
import AccessibleButton from "./AccessibleButton";
|
||||||
|
|
||||||
const EventListSummary = ({events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText}) => {
|
const EventListSummary = ({events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText}) => {
|
||||||
const [expanded, toggleExpanded] = useStateToggle(startExpanded);
|
const [expanded, toggleExpanded] = useStateToggle(startExpanded);
|
||||||
|
@ -42,24 +43,15 @@ const EventListSummary = ({events, children, threshold=3, onToggle, startExpande
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let body;
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
return (
|
body = <React.Fragment>
|
||||||
<div className="mx_EventListSummary" data-scroll-tokens={eventIds}>
|
<div className="mx_EventListSummary_line"> </div>
|
||||||
<div className={"mx_EventListSummary_toggle"} onClick={toggleExpanded}>
|
{ children }
|
||||||
{ _t('collapse') }
|
</React.Fragment>;
|
||||||
</div>
|
} else {
|
||||||
<div className="mx_EventListSummary_line"> </div>
|
const avatars = summaryMembers.map((m) => <MemberAvatar key={m.userId} member={m} width={14} height={14} />);
|
||||||
{ children }
|
body = (
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const avatars = summaryMembers.map((m) => <MemberAvatar key={m.userId} member={m} width={14} height={14} />);
|
|
||||||
return (
|
|
||||||
<div className="mx_EventListSummary" data-scroll-tokens={eventIds}>
|
|
||||||
<div className={"mx_EventListSummary_toggle"} onClick={toggleExpanded}>
|
|
||||||
{ _t('expand') }
|
|
||||||
</div>
|
|
||||||
<div className="mx_EventTile_line">
|
<div className="mx_EventTile_line">
|
||||||
<div className="mx_EventTile_info">
|
<div className="mx_EventTile_info">
|
||||||
<span className="mx_EventListSummary_avatars" onClick={toggleExpanded}>
|
<span className="mx_EventListSummary_avatars" onClick={toggleExpanded}>
|
||||||
|
@ -70,6 +62,15 @@ const EventListSummary = ({events, children, threshold=3, onToggle, startExpande
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_EventListSummary" data-scroll-tokens={eventIds}>
|
||||||
|
<AccessibleButton className="mx_EventListSummary_toggle" onClick={toggleExpanded} aria-expanded={expanded}>
|
||||||
|
{ expanded ? _t('collapse') : _t('expand') }
|
||||||
|
</AccessibleButton>
|
||||||
|
{ body }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,7 +32,7 @@ function getId() {
|
||||||
export default class Field extends React.PureComponent {
|
export default class Field extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
// The field's ID, which binds the input and label together. Immutable.
|
// The field's ID, which binds the input and label together. Immutable.
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string,
|
||||||
// The element to create. Defaults to "input".
|
// The element to create. Defaults to "input".
|
||||||
// To define options for a select, use <Field><option ... /></Field>
|
// To define options for a select, use <Field><option ... /></Field>
|
||||||
element: PropTypes.oneOf(["input", "select", "textarea"]),
|
element: PropTypes.oneOf(["input", "select", "textarea"]),
|
||||||
|
|
|
@ -68,8 +68,10 @@ export const getE2EStatus = (cli, userId, devices) => {
|
||||||
return hasUnverifiedDevice ? "warning" : "verified";
|
return hasUnverifiedDevice ? "warning" : "verified";
|
||||||
}
|
}
|
||||||
const isMe = userId === cli.getUserId();
|
const isMe = userId === cli.getUserId();
|
||||||
const userVerified = cli.checkUserTrust(userId).isCrossSigningVerified();
|
const userTrust = cli.checkUserTrust(userId);
|
||||||
if (!userVerified) return "normal";
|
if (!userTrust.isCrossSigningVerified()) {
|
||||||
|
return userTrust.wasCrossSigningVerified() ? "warning" : "normal";
|
||||||
|
}
|
||||||
|
|
||||||
const anyDeviceUnverified = devices.some(device => {
|
const anyDeviceUnverified = devices.some(device => {
|
||||||
const { deviceId } = device;
|
const { deviceId } = device;
|
||||||
|
|
|
@ -121,10 +121,10 @@ export default createReactClass({
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const { userId } = this.props.member;
|
const { userId } = this.props.member;
|
||||||
const isMe = userId === cli.getUserId();
|
const isMe = userId === cli.getUserId();
|
||||||
const userVerified = cli.checkUserTrust(userId).isCrossSigningVerified();
|
const userTrust = cli.checkUserTrust(userId);
|
||||||
if (!userVerified) {
|
if (!userTrust.isCrossSigningVerified()) {
|
||||||
this.setState({
|
this.setState({
|
||||||
e2eStatus: "normal",
|
e2eStatus: userTrust.wasCrossSigningVerified() ? "warning" : "normal",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,13 @@ import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
|
||||||
export default class MessageComposerFormatBar extends React.PureComponent {
|
export default class MessageComposerFormatBar extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onAction: PropTypes.func.isRequired,
|
onAction: PropTypes.func.isRequired,
|
||||||
shortcuts: PropTypes.object.isRequired,
|
shortcuts: PropTypes.object.isRequired,
|
||||||
}
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -64,7 +65,7 @@ class FormatButton extends React.PureComponent {
|
||||||
icon: PropTypes.string.isRequired,
|
icon: PropTypes.string.isRequired,
|
||||||
shortcut: PropTypes.string,
|
shortcut: PropTypes.string,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const InteractiveTooltip = sdk.getComponent('elements.InteractiveTooltip');
|
const InteractiveTooltip = sdk.getComponent('elements.InteractiveTooltip');
|
||||||
|
@ -82,11 +83,12 @@ class FormatButton extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InteractiveTooltip content={tooltipContent} forceHidden={!this.props.visible}>
|
<InteractiveTooltip content={tooltipContent} forceHidden={!this.props.visible}>
|
||||||
<span aria-label={this.props.label}
|
<AccessibleButton
|
||||||
role="button"
|
as="span"
|
||||||
onClick={this.props.onClick}
|
role="button"
|
||||||
className={className}>
|
onClick={this.props.onClick}
|
||||||
</span>
|
aria-label={this.props.label}
|
||||||
|
className={className} />
|
||||||
</InteractiveTooltip>
|
</InteractiveTooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import E2EIcon from './E2EIcon';
|
||||||
import InviteOnlyIcon from './InviteOnlyIcon';
|
import InviteOnlyIcon from './InviteOnlyIcon';
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
import rate_limited_func from '../../../ratelimitedfunc';
|
import rate_limited_func from '../../../ratelimitedfunc';
|
||||||
|
import { shieldStatusForRoom } from '../../../utils/ShieldUtils';
|
||||||
|
|
||||||
export default createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'RoomTile',
|
displayName: 'RoomTile',
|
||||||
|
@ -154,35 +155,9 @@ export default createReactClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplication between here and _updateE2eStatus in RoomView
|
/* At this point, the user has encryption on and cross-signing on */
|
||||||
const e2eMembers = await this.props.room.getEncryptionTargetMembers();
|
|
||||||
const verified = [];
|
|
||||||
const unverified = [];
|
|
||||||
e2eMembers.map(({userId}) => userId)
|
|
||||||
.filter((userId) => userId !== cli.getUserId())
|
|
||||||
.forEach((userId) => {
|
|
||||||
(cli.checkUserTrust(userId).isCrossSigningVerified() ?
|
|
||||||
verified : unverified).push(userId);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Check all verified user devices. */
|
|
||||||
/* Don't alarm if no other users are verified */
|
|
||||||
const targets = (verified.length > 0) ? [...verified, cli.getUserId()] : verified;
|
|
||||||
for (const userId of targets) {
|
|
||||||
const devices = await cli.getStoredDevicesForUser(userId);
|
|
||||||
const allDevicesVerified = devices.every(({deviceId}) => {
|
|
||||||
return cli.checkDeviceTrust(userId, deviceId).isVerified();
|
|
||||||
});
|
|
||||||
if (!allDevicesVerified) {
|
|
||||||
this.setState({
|
|
||||||
e2eStatus: "warning",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
e2eStatus: unverified.length === 0 ? "verified" : "normal",
|
e2eStatus: await shieldStatusForRoom(cli, this.props.room),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,7 @@
|
||||||
"Usage": "Usage",
|
"Usage": "Usage",
|
||||||
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message",
|
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message",
|
||||||
"Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown",
|
"Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown",
|
||||||
|
"Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown",
|
||||||
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
|
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
|
||||||
"/ddg is not a command": "/ddg is not a command",
|
"/ddg is not a command": "/ddg is not a command",
|
||||||
"To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.",
|
"To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.",
|
||||||
|
@ -1880,6 +1881,7 @@
|
||||||
"Find other public servers or use a custom server": "Find other public servers or use a custom server",
|
"Find other public servers or use a custom server": "Find other public servers or use a custom server",
|
||||||
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
||||||
"Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />",
|
"Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />",
|
||||||
|
"Sign in with SSO": "Sign in with SSO",
|
||||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Sorry, your browser is <b>not</b> able to run Riot.",
|
"Sorry, your browser is <b>not</b> able to run Riot.": "Sorry, your browser is <b>not</b> able to run Riot.",
|
||||||
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",
|
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",
|
||||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.",
|
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.",
|
||||||
|
|
|
@ -469,6 +469,9 @@ export default class EventIndex extends EventEmitter {
|
||||||
// decryption keys, do we want to retry this checkpoint at a later
|
// decryption keys, do we want to retry this checkpoint at a later
|
||||||
// stage?
|
// stage?
|
||||||
const filteredEvents = matrixEvents.filter(this.isValidEvent);
|
const filteredEvents = matrixEvents.filter(this.isValidEvent);
|
||||||
|
const undecryptableEvents = matrixEvents.filter((ev) => {
|
||||||
|
return ev.isDecryptionFailure();
|
||||||
|
});
|
||||||
|
|
||||||
// Collect the redaction events so we can delete the redacted events
|
// Collect the redaction events so we can delete the redacted events
|
||||||
// from the index.
|
// from the index.
|
||||||
|
@ -503,7 +506,10 @@ export default class EventIndex extends EventEmitter {
|
||||||
console.log(
|
console.log(
|
||||||
"EventIndex: Crawled room",
|
"EventIndex: Crawled room",
|
||||||
client.getRoom(checkpoint.roomId).name,
|
client.getRoom(checkpoint.roomId).name,
|
||||||
"and fetched", events.length, "events.",
|
"and fetched total", matrixEvents.length, "events of which",
|
||||||
|
events.length, "are being added,", redactionEvents.length,
|
||||||
|
"are redacted,", matrixEvents.length - events.length,
|
||||||
|
"are being skipped, undecryptable", undecryptableEvents.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
58
src/utils/ShieldUtils.ts
Normal file
58
src/utils/ShieldUtils.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import DMRoomMap from './DMRoomMap';
|
||||||
|
|
||||||
|
/* For now, a cut-down type spec for the client */
|
||||||
|
interface Client {
|
||||||
|
getUserId: () => string;
|
||||||
|
checkUserTrust: (userId: string) => {
|
||||||
|
isCrossSigningVerified: () => boolean
|
||||||
|
wasCrossSigningVerified: () => boolean
|
||||||
|
};
|
||||||
|
getStoredDevicesForUser: (userId: string) => Promise<[{ deviceId: string }]>;
|
||||||
|
checkDeviceTrust: (userId: string, deviceId: string) => {
|
||||||
|
isVerified: () => boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Room {
|
||||||
|
getEncryptionTargetMembers: () => Promise<[{userId: string}]>;
|
||||||
|
roomId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function shieldStatusForRoom(client: Client, room: Room): Promise<string> {
|
||||||
|
const members = (await room.getEncryptionTargetMembers()).map(({userId}) => userId);
|
||||||
|
const inDMMap = !!DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
||||||
|
|
||||||
|
const verified: string[] = [];
|
||||||
|
const unverified: string[] = [];
|
||||||
|
members.filter((userId) => userId !== client.getUserId())
|
||||||
|
.forEach((userId) => {
|
||||||
|
(client.checkUserTrust(userId).isCrossSigningVerified() ?
|
||||||
|
verified : unverified).push(userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Alarm if any unverified users were verified before. */
|
||||||
|
for (const userId of unverified) {
|
||||||
|
if (client.checkUserTrust(userId).wasCrossSigningVerified()) {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check all verified user devices. */
|
||||||
|
/* Don't alarm if no other users are verified */
|
||||||
|
const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified
|
||||||
|
!inDMMap && // Don't alarm for self in DMs with other users
|
||||||
|
(members.length !== 2) || // Don't alarm for self in 1:1 chats with other users
|
||||||
|
(members.length === 1); // Do alarm for self if we're alone in a room
|
||||||
|
const targets = includeUser ? [...verified, client.getUserId()] : verified;
|
||||||
|
for (const userId of targets) {
|
||||||
|
const devices = await client.getStoredDevicesForUser(userId);
|
||||||
|
const anyDeviceNotVerified = devices.some(({deviceId}) => {
|
||||||
|
return !client.checkDeviceTrust(userId, deviceId).isVerified();
|
||||||
|
});
|
||||||
|
if (anyDeviceNotVerified) {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unverified.length === 0 ? "verified" : "normal";
|
||||||
|
}
|
183
test/utils/ShieldUtils-test.js
Normal file
183
test/utils/ShieldUtils-test.js
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
import { shieldStatusForRoom } from '../../src/utils/ShieldUtils';
|
||||||
|
import DMRoomMap from '../../src/utils/DMRoomMap';
|
||||||
|
|
||||||
|
function mkClient(selfTrust) {
|
||||||
|
return {
|
||||||
|
getUserId: () => "@self:localhost",
|
||||||
|
checkUserTrust: (userId) => ({
|
||||||
|
isCrossSigningVerified: () => userId[1] == "T",
|
||||||
|
wasCrossSigningVerified: () => userId[1] == "T" || userId[1] == "W",
|
||||||
|
}),
|
||||||
|
checkDeviceTrust: (userId, deviceId) => ({
|
||||||
|
isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T",
|
||||||
|
}),
|
||||||
|
getStoredDevicesForUser: async (userId) => ["DEVICE"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("mkClient self-test", function() {
|
||||||
|
test.each([true, false])("behaves well for self-trust=%s", (v) => {
|
||||||
|
const client = mkClient(v);
|
||||||
|
expect(client.checkDeviceTrust("@self:localhost", "DEVICE").isVerified()).toBe(v);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
["@TT:h", true],
|
||||||
|
["@TF:h", true],
|
||||||
|
["@FT:h", false],
|
||||||
|
["@FF:h", false]],
|
||||||
|
)("behaves well for user trust %s", (userId, trust) => {
|
||||||
|
expect(mkClient().checkUserTrust(userId).isCrossSigningVerified()).toBe(trust);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
["@TT:h", true],
|
||||||
|
["@TF:h", false],
|
||||||
|
["@FT:h", true],
|
||||||
|
["@FF:h", false]],
|
||||||
|
)("behaves well for device trust %s", (userId, trust) => {
|
||||||
|
expect(mkClient().checkDeviceTrust(userId, "device").isVerified()).toBe(trust);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("shieldStatusForMembership self-trust behaviour", function() {
|
||||||
|
beforeAll(() => {
|
||||||
|
DMRoomMap._sharedInstance = {
|
||||||
|
getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[[true, true], [true, false],
|
||||||
|
[false, true], [false, false]],
|
||||||
|
)("2 unverified: returns 'normal', self-trust = %s, DM = %s", async (trusted, dm) => {
|
||||||
|
const client = mkClient(trusted);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@FF1:h", "@FF2:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual("normal");
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["verified", true, true], ["verified", true, false],
|
||||||
|
["verified", false, true], ["warning", false, false]],
|
||||||
|
)("2 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => {
|
||||||
|
const client = mkClient(trusted);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@TT2:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["normal", true, true], ["normal", true, false],
|
||||||
|
["normal", false, true], ["warning", false, false]],
|
||||||
|
)("2 mixed: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => {
|
||||||
|
const client = mkClient(trusted);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@FF2:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["verified", true, true], ["verified", true, false],
|
||||||
|
["warning", false, true], ["warning", false, false]],
|
||||||
|
)("0 others: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => {
|
||||||
|
const client = mkClient(trusted);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["verified", true, true], ["verified", true, false],
|
||||||
|
["verified", false, true], ["verified", false, false]],
|
||||||
|
)("1 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => {
|
||||||
|
const client = mkClient(trusted);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@TT:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["normal", true, true], ["normal", true, false],
|
||||||
|
["normal", false, true], ["normal", false, false]],
|
||||||
|
)("1 unverified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => {
|
||||||
|
const client = mkClient(trusted);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("shieldStatusForMembership other-trust behaviour", function() {
|
||||||
|
beforeAll(() => {
|
||||||
|
DMRoomMap._sharedInstance = {
|
||||||
|
getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["warning", true], ["warning", false]],
|
||||||
|
)("1 verified/untrusted: returns '%s', DM = %s", async (result, dm) => {
|
||||||
|
const client = mkClient(true);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["warning", true], ["warning", false]],
|
||||||
|
)("2 verified/untrusted: returns '%s', DM = %s", async (result, dm) => {
|
||||||
|
const client = mkClient(true);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h", "@TT:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["normal", true], ["normal", false]],
|
||||||
|
)("2 unverified/untrusted: returns '%s', DM = %s", async (result, dm) => {
|
||||||
|
const client = mkClient(true);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h", "@FT:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(
|
||||||
|
[["warning", true], ["warning", false]],
|
||||||
|
)("2 was verified: returns '%s', DM = %s", async (result, dm) => {
|
||||||
|
const client = mkClient(true);
|
||||||
|
const room = {
|
||||||
|
roomId: dm ? "DM" : "other",
|
||||||
|
getEncryptionTargetMembers: () => ["@self:localhost", "@WF:h", "@FT:h"].map((userId) => ({userId})),
|
||||||
|
};
|
||||||
|
const status = await shieldStatusForRoom(client, room);
|
||||||
|
expect(status).toEqual(result);
|
||||||
|
});
|
||||||
|
});
|
|
@ -5690,8 +5690,8 @@ mathml-tag-names@^2.0.1:
|
||||||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||||
|
|
||||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
||||||
version "5.1.1"
|
version "5.2.0"
|
||||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/b2e154377a4268441a3b27b183dd7f7018187035"
|
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/223d37ffce674a23ca73702f04b9ba31cfd84196"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.8.3"
|
"@babel/runtime" "^7.8.3"
|
||||||
another-json "^0.2.0"
|
another-json "^0.2.0"
|
||||||
|
|
Loading…
Reference in a new issue