Merge remote-tracking branch 'origin/experimental' into travis/fix-memberlist-order
This commit is contained in:
commit
cc8fa7911b
46 changed files with 788 additions and 193 deletions
|
@ -30,7 +30,7 @@ popd
|
||||||
if [ "$TRAVIS_BRANCH" = "develop" ]
|
if [ "$TRAVIS_BRANCH" = "develop" ]
|
||||||
then
|
then
|
||||||
# run end to end tests
|
# run end to end tests
|
||||||
git clone https://github.com/matrix-org/matrix-react-end-to-end-tests.git --branch master
|
scripts/fetchdep.sh matrix-org matrix-react-end-to-end-tests master
|
||||||
pushd matrix-react-end-to-end-tests
|
pushd matrix-react-end-to-end-tests
|
||||||
ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web
|
ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web
|
||||||
# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh
|
# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh
|
||||||
|
|
|
@ -124,8 +124,9 @@
|
||||||
"eslint-plugin-flowtype": "^2.30.0",
|
"eslint-plugin-flowtype": "^2.30.0",
|
||||||
"eslint-plugin-react": "^7.7.0",
|
"eslint-plugin-react": "^7.7.0",
|
||||||
"estree-walker": "^0.5.0",
|
"estree-walker": "^0.5.0",
|
||||||
"expect": "^1.16.0",
|
"expect": "^23.6.0",
|
||||||
"flow-parser": "^0.57.3",
|
"flow-parser": "^0.57.3",
|
||||||
|
"jest-mock": "^23.2.0",
|
||||||
"karma": "^3.0.0",
|
"karma": "^3.0.0",
|
||||||
"karma-chrome-launcher": "^0.2.3",
|
"karma-chrome-launcher": "^0.2.3",
|
||||||
"karma-cli": "^1.0.1",
|
"karma-cli": "^1.0.1",
|
||||||
|
|
|
@ -25,8 +25,10 @@
|
||||||
@import "./structures/_ViewSource.scss";
|
@import "./structures/_ViewSource.scss";
|
||||||
@import "./structures/login/_Login.scss";
|
@import "./structures/login/_Login.scss";
|
||||||
@import "./views/avatars/_BaseAvatar.scss";
|
@import "./views/avatars/_BaseAvatar.scss";
|
||||||
|
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
||||||
@import "./views/context_menus/_MessageContextMenu.scss";
|
@import "./views/context_menus/_MessageContextMenu.scss";
|
||||||
@import "./views/context_menus/_RoomTileContextMenu.scss";
|
@import "./views/context_menus/_RoomTileContextMenu.scss";
|
||||||
|
@import "./views/context_menus/_StatusMessageContextMenu.scss";
|
||||||
@import "./views/context_menus/_TagTileContextMenu.scss";
|
@import "./views/context_menus/_TagTileContextMenu.scss";
|
||||||
@import "./views/context_menus/_TopLeftMenu.scss";
|
@import "./views/context_menus/_TopLeftMenu.scss";
|
||||||
@import "./views/dialogs/_BugReportDialog.scss";
|
@import "./views/dialogs/_BugReportDialog.scss";
|
||||||
|
@ -35,7 +37,6 @@
|
||||||
@import "./views/dialogs/_ChatInviteDialog.scss";
|
@import "./views/dialogs/_ChatInviteDialog.scss";
|
||||||
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
|
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
|
||||||
@import "./views/dialogs/_CreateGroupDialog.scss";
|
@import "./views/dialogs/_CreateGroupDialog.scss";
|
||||||
@import "./views/dialogs/_CreateKeyBackupDialog.scss";
|
|
||||||
@import "./views/dialogs/_CreateRoomDialog.scss";
|
@import "./views/dialogs/_CreateRoomDialog.scss";
|
||||||
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
||||||
@import "./views/dialogs/_DevtoolsDialog.scss";
|
@import "./views/dialogs/_DevtoolsDialog.scss";
|
||||||
|
|
|
@ -14,12 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog {
|
.mx_MemberStatusMessageAvatar_hasStatus {
|
||||||
padding-right: 40px;
|
border: 2px solid $accent-color;
|
||||||
}
|
border-radius: 40px;
|
||||||
|
padding-right: 0 !important; /* Override AccessibleButton styling */
|
||||||
.mx_CreateKeyBackupDialog_recoveryKey {
|
|
||||||
padding: 20px;
|
|
||||||
color: $info-plinth-fg-color;
|
|
||||||
background-color: $info-plinth-bg-color;
|
|
||||||
}
|
}
|
55
res/css/views/context_menus/_StatusMessageContextMenu.scss
Normal file
55
res/css/views/context_menus/_StatusMessageContextMenu.scss
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu_message {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 3px 0 0 3px;
|
||||||
|
border: 1px solid $input-border-color;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 7px 7px 7px 9px;
|
||||||
|
width: 135px;
|
||||||
|
background-color: $primary-bg-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu_submit {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu_submitFaded {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu_submit img {
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu hr {
|
||||||
|
border: 0.5px solid $menu-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu_clearIcon {
|
||||||
|
margin: 5px 15px 5px 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu_clear {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_StatusMessageContextMenu_hasStatus .mx_StatusMessageContextMenu_clear {
|
||||||
|
color: $warning-color;
|
||||||
|
}
|
|
@ -14,6 +14,10 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.mx_CreateKeyBackupDialog {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_primaryContainer {
|
.mx_CreateKeyBackupDialog_primaryContainer {
|
||||||
/*FIXME: plinth colour in new theme(s). background-color: $accent-color;*/
|
/*FIXME: plinth colour in new theme(s). background-color: $accent-color;*/
|
||||||
padding: 20px
|
padding: 20px
|
||||||
|
@ -25,9 +29,13 @@ limitations under the License.
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_CreateKeyBackupDialog_passPhraseContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseHelp {
|
.mx_CreateKeyBackupDialog_passPhraseHelp {
|
||||||
float: right;
|
flex: 1;
|
||||||
width: 230px;
|
|
||||||
height: 85px;
|
height: 85px;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
|
@ -38,20 +46,36 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseInput {
|
.mx_CreateKeyBackupDialog_passPhraseInput {
|
||||||
|
flex: none;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
border: 1px solid $accent-color;
|
border: 1px solid $accent-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseMatch {
|
.mx_CreateKeyBackupDialog_passPhraseMatch {
|
||||||
float: right;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_recoveryKeyButtons {
|
.mx_CreateKeyBackupDialog_recoveryKeyHeader {
|
||||||
float: right;
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateKeyBackupDialog_recoveryKeyContainer {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_recoveryKey {
|
.mx_CreateKeyBackupDialog_recoveryKey {
|
||||||
width: 300px;
|
width: 262px;
|
||||||
|
padding: 20px;
|
||||||
|
color: $info-plinth-fg-color;
|
||||||
|
background-color: $info-plinth-bg-color;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateKeyBackupDialog_recoveryKeyButtons {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,3 +107,10 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.mx_EntityTile_subtext {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.5;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: clip;
|
||||||
|
}
|
||||||
|
|
|
@ -132,6 +132,13 @@ limitations under the License.
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_statusMessage {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.5;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: clip;
|
||||||
|
}
|
||||||
.mx_MemberInfo .mx_MemberInfo_scrollContainer {
|
.mx_MemberInfo .mx_MemberInfo_scrollContainer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,15 +48,48 @@ limitations under the License.
|
||||||
left: -12px;
|
left: -12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_avatar {
|
|
||||||
flex: 0;
|
.mx_RoomTile_nameContainer {
|
||||||
padding: 4px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_labelContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_subtext {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 0 0 0 7px;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: clip;
|
||||||
|
position: relative;
|
||||||
|
bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_avatar_container {
|
.mx_RoomTile_avatar_container {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_avatar {
|
||||||
|
flex: 0;
|
||||||
|
padding: 4px;
|
||||||
|
width: 24px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_hasSubtext .mx_RoomTile_avatar {
|
||||||
|
padding-top: 0;
|
||||||
|
vertical-align: super;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomTile_dm {
|
.mx_RoomTile_dm {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -69,7 +102,7 @@ limitations under the License.
|
||||||
flex: 1 5 auto;
|
flex: 1 5 auto;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 6px;
|
padding: 0 6px;
|
||||||
color: $roomtile-name-color;
|
color: $roomtile-name-color;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
17
res/img/icons-checkmark.svg
Normal file
17
res/img/icons-checkmark.svg
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 52.5 (67469) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Tick</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Custom-Status-Copy" transform="translate(-529.000000, -917.000000)" fill-rule="nonzero">
|
||||||
|
<g id="Tick" transform="translate(530.000000, 918.000000)">
|
||||||
|
<circle id="Oval" stroke="#6AAC8C" fill="#75CFA6" cx="9" cy="9" r="9"></circle>
|
||||||
|
<g id="Glyph" transform="translate(8.949747, 7.949747) rotate(-45.000000) translate(-8.949747, -7.949747) translate(4.449747, 5.449747)" fill="#FFFFFF">
|
||||||
|
<rect id="Rectangle" x="0" y="0" width="2" height="5"></rect>
|
||||||
|
<rect id="Rectangle" x="0" y="3" width="9" height="2"></rect>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
org="$1"
|
org="$1"
|
||||||
repo="$2"
|
repo="$2"
|
||||||
|
defbranch="$3"
|
||||||
|
|
||||||
|
[ -z "$defbranch" ] && defbranch="develop"
|
||||||
|
|
||||||
rm -r "$repo" || true
|
rm -r "$repo" || true
|
||||||
|
|
||||||
|
@ -20,5 +23,5 @@ clone $TRAVIS_PULL_REQUEST_BRANCH
|
||||||
clone $TRAVIS_BRANCH
|
clone $TRAVIS_BRANCH
|
||||||
# Try the current branch from Jenkins.
|
# Try the current branch from Jenkins.
|
||||||
clone `"echo $GIT_BRANCH" | sed -e 's/^origin\///'`
|
clone `"echo $GIT_BRANCH" | sed -e 's/^origin\///'`
|
||||||
# Use develop as the last resort.
|
# Use the default branch as the last resort.
|
||||||
clone develop
|
clone $defbranch
|
||||||
|
|
13
src/Login.js
13
src/Login.js
|
@ -204,6 +204,19 @@ export async function sendLoginRequest(hsUrl, isUrl, loginType, loginParams) {
|
||||||
|
|
||||||
const data = await client.login(loginType, loginParams);
|
const data = await client.login(loginType, loginParams);
|
||||||
|
|
||||||
|
const wellknown = data.well_known;
|
||||||
|
if (wellknown) {
|
||||||
|
if (wellknown["m.homeserver"] && wellknown["m.homeserver"]["base_url"]) {
|
||||||
|
hsUrl = wellknown["m.homeserver"]["base_url"];
|
||||||
|
console.log(`Overrode homeserver setting with ${hsUrl} from login response`);
|
||||||
|
}
|
||||||
|
if (wellknown["m.identity_server"] && wellknown["m.identity_server"]["base_url"]) {
|
||||||
|
// TODO: should we prompt here?
|
||||||
|
isUrl = wellknown["m.identity_server"]["base_url"];
|
||||||
|
console.log(`Overrode IS setting with ${isUrl} from login response`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
homeserverUrl: hsUrl,
|
homeserverUrl: hsUrl,
|
||||||
identityServerUrl: isUrl,
|
identityServerUrl: isUrl,
|
||||||
|
|
|
@ -289,11 +289,6 @@ const Notifier = {
|
||||||
const room = MatrixClientPeg.get().getRoom(ev.getRoomId());
|
const room = MatrixClientPeg.get().getRoom(ev.getRoomId());
|
||||||
const actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
|
const actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
|
||||||
if (actions && actions.notify) {
|
if (actions && actions.notify) {
|
||||||
dis.dispatch({
|
|
||||||
action: "event_notification",
|
|
||||||
event: ev,
|
|
||||||
room: room,
|
|
||||||
});
|
|
||||||
if (this.isEnabled()) {
|
if (this.isEnabled()) {
|
||||||
this._displayPopupNotification(ev, room);
|
this._displayPopupNotification(ev, room);
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,7 +392,7 @@ class Tinter {
|
||||||
// XXX: we could just move this all into TintableSvg, but as it's so similar
|
// XXX: we could just move this all into TintableSvg, but as it's so similar
|
||||||
// to the CSS fixup stuff in Tinter (just that the fixups are stored in TintableSvg)
|
// to the CSS fixup stuff in Tinter (just that the fixups are stored in TintableSvg)
|
||||||
// keeping it here for now.
|
// keeping it here for now.
|
||||||
calcSvgFixups(svgs, forceColors) {
|
calcSvgFixups(svgs) {
|
||||||
// go through manually fixing up SVG colours.
|
// go through manually fixing up SVG colours.
|
||||||
// we could do this by stylesheets, but keeping the stylesheets
|
// we could do this by stylesheets, but keeping the stylesheets
|
||||||
// updated would be a PITA, so just brute-force search for the
|
// updated would be a PITA, so just brute-force search for the
|
||||||
|
@ -420,21 +420,13 @@ class Tinter {
|
||||||
const tag = tags[j];
|
const tag = tags[j];
|
||||||
for (let k = 0; k < this.svgAttrs.length; k++) {
|
for (let k = 0; k < this.svgAttrs.length; k++) {
|
||||||
const attr = this.svgAttrs[k];
|
const attr = this.svgAttrs[k];
|
||||||
for (let m = 0; m < this.keyHex.length; m++) { // dev note: don't use L please.
|
for (let l = 0; l < this.keyHex.length; l++) {
|
||||||
// We use a different attribute from the one we're setting
|
if (tag.getAttribute(attr) &&
|
||||||
// because we may also be using forceColors. If we were to
|
tag.getAttribute(attr).toUpperCase() === this.keyHex[l]) {
|
||||||
// check the keyHex against a forceColors value, it may not
|
|
||||||
// match and therefore not change when we need it to.
|
|
||||||
const valAttrName = "mx-val-" + attr;
|
|
||||||
let attribute = tag.getAttribute(valAttrName);
|
|
||||||
if (!attribute) attribute = tag.getAttribute(attr); // fall back to the original
|
|
||||||
if (attribute && (attribute.toUpperCase() === this.keyHex[m] || attribute.toLowerCase() === this.keyRgb[m])) {
|
|
||||||
fixups.push({
|
fixups.push({
|
||||||
node: tag,
|
node: tag,
|
||||||
attr: attr,
|
attr: attr,
|
||||||
refAttr: valAttrName,
|
index: l,
|
||||||
index: m,
|
|
||||||
forceColors: forceColors,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -450,9 +442,7 @@ class Tinter {
|
||||||
if (DEBUG) console.log("applySvgFixups start for " + fixups);
|
if (DEBUG) console.log("applySvgFixups start for " + fixups);
|
||||||
for (let i = 0; i < fixups.length; i++) {
|
for (let i = 0; i < fixups.length; i++) {
|
||||||
const svgFixup = fixups[i];
|
const svgFixup = fixups[i];
|
||||||
const forcedColor = svgFixup.forceColors ? svgFixup.forceColors[svgFixup.index] : null;
|
svgFixup.node.setAttribute(svgFixup.attr, this.colors[svgFixup.index]);
|
||||||
svgFixup.node.setAttribute(svgFixup.attr, forcedColor ? forcedColor : this.colors[svgFixup.index]);
|
|
||||||
svgFixup.node.setAttribute(svgFixup.refAttr, this.colors[svgFixup.index]);
|
|
||||||
}
|
}
|
||||||
if (DEBUG) console.log("applySvgFixups end");
|
if (DEBUG) console.log("applySvgFixups end");
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,17 +239,19 @@ export default React.createClass({
|
||||||
<p>{_t("You'll need it if you log out or lose access to this device.")}</p>
|
<p>{_t("You'll need it if you log out or lose access to this device.")}</p>
|
||||||
|
|
||||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||||
<div className="mx_CreateKeyBackupDialog_passPhraseHelp">
|
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
||||||
{strengthMeter}
|
<input type="password"
|
||||||
{helpText}
|
onChange={this._onPassPhraseChange}
|
||||||
|
onKeyPress={this._onPassPhraseKeyPress}
|
||||||
|
value={this.state.passPhrase}
|
||||||
|
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||||
|
placeholder={_t("Enter a passphrase...")}
|
||||||
|
/>
|
||||||
|
<div className="mx_CreateKeyBackupDialog_passPhraseHelp">
|
||||||
|
{strengthMeter}
|
||||||
|
{helpText}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="password"
|
|
||||||
onChange={this._onPassPhraseChange}
|
|
||||||
onKeyPress={this._onPassPhraseKeyPress}
|
|
||||||
value={this.state.passPhrase}
|
|
||||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
|
||||||
placeholder={_t("Enter a passphrase...")}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogButtons primaryButton={_t('Next')}
|
<DialogButtons primaryButton={_t('Next')}
|
||||||
|
@ -317,16 +319,18 @@ export default React.createClass({
|
||||||
"somewhere safe.",
|
"somewhere safe.",
|
||||||
)}</p>
|
)}</p>
|
||||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||||
{passPhraseMatch}
|
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
||||||
<div>
|
<div>
|
||||||
<input type="password"
|
<input type="password"
|
||||||
onChange={this._onPassPhraseConfirmChange}
|
onChange={this._onPassPhraseConfirmChange}
|
||||||
onKeyPress={this._onPassPhraseConfirmKeyPress}
|
onKeyPress={this._onPassPhraseConfirmKeyPress}
|
||||||
value={this.state.passPhraseConfirm}
|
value={this.state.passPhraseConfirm}
|
||||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||||
placeholder={_t("Repeat your passphrase...")}
|
placeholder={_t("Repeat your passphrase...")}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
{passPhraseMatch}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogButtons primaryButton={_t('Next')}
|
<DialogButtons primaryButton={_t('Next')}
|
||||||
|
@ -351,21 +355,21 @@ export default React.createClass({
|
||||||
<p>{_t("Make a copy of this Recovery Key and keep it safe.")}</p>
|
<p>{_t("Make a copy of this Recovery Key and keep it safe.")}</p>
|
||||||
<p>{bodyText}</p>
|
<p>{bodyText}</p>
|
||||||
<p className="mx_CreateKeyBackupDialog_primaryContainer">
|
<p className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||||
<div>{_t("Your Recovery Key")}</div>
|
<div className="mx_CreateKeyBackupDialog_recoveryKeyHeader">
|
||||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
|
{_t("Your Recovery Key")}
|
||||||
<button onClick={this._onCopyClick}>
|
|
||||||
{_t("Copy to clipboard")}
|
|
||||||
</button>
|
|
||||||
{
|
|
||||||
// FIXME REDESIGN: buttons should be adjacent but insufficient room in current design
|
|
||||||
}
|
|
||||||
<br /><br />
|
|
||||||
<button onClick={this._onDownloadClick}>
|
|
||||||
{_t("Download")}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_CreateKeyBackupDialog_recoveryKey">
|
<div className="mx_CreateKeyBackupDialog_recoveryKeyContainer">
|
||||||
<code ref={this._collectRecoveryKeyNode}>{this._keyBackupInfo.recovery_key}</code>
|
<div className="mx_CreateKeyBackupDialog_recoveryKey">
|
||||||
|
<code ref={this._collectRecoveryKeyNode}>{this._keyBackupInfo.recovery_key}</code>
|
||||||
|
</div>
|
||||||
|
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
|
||||||
|
<button className="mx_Dialog_primary" onClick={this._onCopyClick}>
|
||||||
|
{_t("Copy to clipboard")}
|
||||||
|
</button>
|
||||||
|
<button className="mx_Dialog_primary" onClick={this._onDownloadClick}>
|
||||||
|
{_t("Download")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -91,11 +91,15 @@ class HomePage extends React.Component {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoginClick() {
|
onLoginClick(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
dis.dispatch({ action: 'start_login' });
|
dis.dispatch({ action: 'start_login' });
|
||||||
}
|
}
|
||||||
|
|
||||||
onRegisterClick() {
|
onRegisterClick(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
dis.dispatch({ action: 'start_registration' });
|
dis.dispatch({ action: 'start_registration' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -927,6 +927,10 @@ export default React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_viewHome: function() {
|
_viewHome: function() {
|
||||||
|
// The home page requires the "logged in" view, so we'll set that.
|
||||||
|
this.setStateForNewView({
|
||||||
|
view: VIEWS.LOGGED_IN,
|
||||||
|
});
|
||||||
this._setPage(PageTypes.HomePage);
|
this._setPage(PageTypes.HomePage);
|
||||||
this.notifyNewScreen('home');
|
this.notifyNewScreen('home');
|
||||||
},
|
},
|
||||||
|
@ -1183,10 +1187,7 @@ export default React.createClass({
|
||||||
* @param {string} teamToken
|
* @param {string} teamToken
|
||||||
*/
|
*/
|
||||||
_onLoggedIn: async function(teamToken) {
|
_onLoggedIn: async function(teamToken) {
|
||||||
this.setState({
|
this.setStateForNewView({view: VIEWS.LOGGED_IN});
|
||||||
view: VIEWS.LOGGED_IN,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (teamToken) {
|
if (teamToken) {
|
||||||
// A team member has logged in, not a guest
|
// A team member has logged in, not a guest
|
||||||
this._teamToken = teamToken;
|
this._teamToken = teamToken;
|
||||||
|
|
|
@ -163,6 +163,7 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
|
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
|
||||||
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
|
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
|
||||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||||
|
MatrixClientPeg.get().on("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
||||||
this._fetchMediaConfig();
|
this._fetchMediaConfig();
|
||||||
// Start listening for RoomViewStore updates
|
// Start listening for RoomViewStore updates
|
||||||
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||||
|
@ -451,6 +452,7 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership);
|
MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership);
|
||||||
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
||||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||||
|
MatrixClientPeg.get().removeListener("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.removeEventListener('beforeunload', this.onPageUnload);
|
window.removeEventListener('beforeunload', this.onPageUnload);
|
||||||
|
@ -620,6 +622,11 @@ module.exports = React.createClass({
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onKeyBackupStatus() {
|
||||||
|
// Key backup status changes affect whether the in-room recovery
|
||||||
|
// reminder is displayed.
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,18 @@ module.exports = React.createClass({
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onLoginClick: function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.props.onLoginClick();
|
||||||
|
},
|
||||||
|
|
||||||
|
onRegisterClick: function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.props.onRegisterClick();
|
||||||
|
},
|
||||||
|
|
||||||
showErrorDialog: function(body, title) {
|
showErrorDialog: function(body, title) {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
||||||
|
@ -253,10 +265,10 @@ module.exports = React.createClass({
|
||||||
</form>
|
</form>
|
||||||
{ serverConfigSection }
|
{ serverConfigSection }
|
||||||
{ errorText }
|
{ errorText }
|
||||||
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
|
<a className="mx_Login_create" onClick={this.onLoginClick} href="#">
|
||||||
{ _t('Return to login screen') }
|
{ _t('Return to login screen') }
|
||||||
</a>
|
</a>
|
||||||
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
|
<a className="mx_Login_create" onClick={this.onRegisterClick} href="#">
|
||||||
{ _t('Create an account') }
|
{ _t('Create an account') }
|
||||||
</a>
|
</a>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
|
|
|
@ -214,7 +214,10 @@ module.exports = React.createClass({
|
||||||
}).done();
|
}).done();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onLoginAsGuestClick: function() {
|
_onLoginAsGuestClick: function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
self.setState({
|
self.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
|
@ -297,6 +300,12 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRegisterClick: function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.props.onRegisterClick();
|
||||||
|
},
|
||||||
|
|
||||||
_tryWellKnownDiscovery: async function(serverName) {
|
_tryWellKnownDiscovery: async function(serverName) {
|
||||||
if (!serverName.trim()) {
|
if (!serverName.trim()) {
|
||||||
// Nothing to discover
|
// Nothing to discover
|
||||||
|
@ -567,7 +576,7 @@ module.exports = React.createClass({
|
||||||
{ errorTextSection }
|
{ errorTextSection }
|
||||||
{ this.componentForStep(this.state.currentFlow) }
|
{ this.componentForStep(this.state.currentFlow) }
|
||||||
{ serverConfig }
|
{ serverConfig }
|
||||||
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
|
<a className="mx_Login_create" onClick={this.onRegisterClick} href="#">
|
||||||
{ _t('Create an account') }
|
{ _t('Create an account') }
|
||||||
</a>
|
</a>
|
||||||
{ loginAsGuestJsx }
|
{ loginAsGuestJsx }
|
||||||
|
|
|
@ -363,6 +363,12 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onLoginClick: function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.props.onLoginClick();
|
||||||
|
},
|
||||||
|
|
||||||
_makeRegisterRequest: function(auth) {
|
_makeRegisterRequest: function(auth) {
|
||||||
// Only send the bind params if we're sending username / pw params
|
// Only send the bind params if we're sending username / pw params
|
||||||
// (Since we need to send no params at all to use the ones saved in the
|
// (Since we need to send no params at all to use the ones saved in the
|
||||||
|
@ -468,7 +474,7 @@ module.exports = React.createClass({
|
||||||
let signIn;
|
let signIn;
|
||||||
if (!this.state.doingUIAuth) {
|
if (!this.state.doingUIAuth) {
|
||||||
signIn = (
|
signIn = (
|
||||||
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
|
<a className="mx_Login_create" onClick={this.onLoginClick} href="#">
|
||||||
{ theme === 'status' ? _t('Sign in') : _t('I already have an account') }
|
{ theme === 'status' ? _t('Sign in') : _t('I already have an account') }
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|
120
src/components/views/avatars/MemberStatusMessageAvatar.js
Normal file
120
src/components/views/avatars/MemberStatusMessageAvatar.js
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
import MemberAvatar from '../avatars/MemberAvatar';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import * as ContextualMenu from "../../structures/ContextualMenu";
|
||||||
|
import StatusMessageContextMenu from "../context_menus/StatusMessageContextMenu";
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
|
export default class MemberStatusMessageAvatar extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
member: PropTypes.object.isRequired,
|
||||||
|
width: PropTypes.number,
|
||||||
|
height: PropTypes.number,
|
||||||
|
resizeMethod: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
resizeMethod: 'crop',
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
if (this.props.member.userId !== MatrixClientPeg.get().getUserId()) {
|
||||||
|
throw new Error("Cannot use MemberStatusMessageAvatar on anyone but the logged in user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents);
|
||||||
|
|
||||||
|
if (this.props.member.user) {
|
||||||
|
this.setState({message: this.props.member.user._unstable_statusMessage});
|
||||||
|
} else {
|
||||||
|
this.setState({message: ""});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (MatrixClientPeg.get()) {
|
||||||
|
MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRoomStateEvents = (ev, state) => {
|
||||||
|
if (ev.getStateKey() !== MatrixClientPeg.get().getUserId()) return;
|
||||||
|
if (ev.getType() !== "im.vector.user_status") return;
|
||||||
|
// TODO: We should be relying on `this.props.member.user._unstable_statusMessage`
|
||||||
|
// We don't currently because the js-sdk doesn't emit a specific event for this
|
||||||
|
// change, and we don't want to race it. This should be improved when we rip out
|
||||||
|
// the im.vector.user_status stuff and replace it with a complete solution.
|
||||||
|
this.setState({message: ev.getContent()["status"]});
|
||||||
|
};
|
||||||
|
|
||||||
|
_onClick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const elementRect = e.target.getBoundingClientRect();
|
||||||
|
|
||||||
|
// The window X and Y offsets are to adjust position when zoomed in to page
|
||||||
|
const x = (elementRect.left + window.pageXOffset) - (elementRect.width / 2) + 3;
|
||||||
|
const chevronOffset = 12;
|
||||||
|
let y = elementRect.top + (elementRect.height / 2) + window.pageYOffset;
|
||||||
|
y = y - (chevronOffset + 4); // where 4 is 1/4 the height of the chevron
|
||||||
|
|
||||||
|
ContextualMenu.createMenu(StatusMessageContextMenu, {
|
||||||
|
chevronOffset: chevronOffset,
|
||||||
|
chevronFace: 'bottom',
|
||||||
|
left: x,
|
||||||
|
top: y,
|
||||||
|
menuWidth: 190,
|
||||||
|
user: this.props.member.user,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (!SettingsStore.isFeatureEnabled("feature_custom_status")) {
|
||||||
|
return <MemberAvatar member={this.props.member}
|
||||||
|
width={this.props.width}
|
||||||
|
height={this.props.height}
|
||||||
|
resizeMethod={this.props.resizeMethod} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasStatus = this.props.member.user ? !!this.props.member.user._unstable_statusMessage : false;
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
"mx_MemberStatusMessageAvatar": true,
|
||||||
|
"mx_MemberStatusMessageAvatar_hasStatus": hasStatus,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <AccessibleButton onClick={this._onClick} className={classes} element="div">
|
||||||
|
<MemberAvatar member={this.props.member}
|
||||||
|
width={this.props.width}
|
||||||
|
height={this.props.height}
|
||||||
|
resizeMethod={this.props.resizeMethod} />
|
||||||
|
</AccessibleButton>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
export default class StatusMessageContextMenu extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
// js-sdk User object. Not required because it might not exist.
|
||||||
|
user: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
message: props.user ? props.user._unstable_statusMessage : "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClearClick = async(e) => {
|
||||||
|
await MatrixClientPeg.get()._unstable_setStatusMessage("");
|
||||||
|
this.setState({message: ""});
|
||||||
|
};
|
||||||
|
|
||||||
|
_onSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
MatrixClientPeg.get()._unstable_setStatusMessage(this.state.message);
|
||||||
|
};
|
||||||
|
|
||||||
|
_onStatusChange = (e) => {
|
||||||
|
this.setState({message: e.target.value});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const formSubmitClasses = classNames({
|
||||||
|
"mx_StatusMessageContextMenu_submit": true,
|
||||||
|
"mx_StatusMessageContextMenu_submitFaded": !this.state.message, // no message == faded
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = <form className="mx_StatusMessageContextMenu_form" onSubmit={this._onSubmit} autoComplete="off">
|
||||||
|
<input type="text" key="message" placeholder={_t("Set a new status...")} autoFocus={true}
|
||||||
|
className="mx_StatusMessageContextMenu_message"
|
||||||
|
value={this.state.message} onChange={this._onStatusChange} maxLength="60" />
|
||||||
|
<AccessibleButton onClick={this._onSubmit} element="div" className={formSubmitClasses}>
|
||||||
|
<img src="img/icons-checkmark.svg" width="22" height="22" />
|
||||||
|
</AccessibleButton>
|
||||||
|
</form>;
|
||||||
|
|
||||||
|
const clearIcon = this.state.message ? "img/cancel-red.svg" : "img/cancel.svg";
|
||||||
|
const clearButton = <AccessibleButton onClick={this._onClearClick} disabled={!this.state.message}
|
||||||
|
className="mx_StatusMessageContextMenu_clear">
|
||||||
|
<img src={clearIcon} alt={_t('Clear status')} width="12" height="12"
|
||||||
|
className="mx_filterFlipColor mx_StatusMessageContextMenu_clearIcon" />
|
||||||
|
<span>{_t("Clear status")}</span>
|
||||||
|
</AccessibleButton>;
|
||||||
|
|
||||||
|
const menuClasses = classNames({
|
||||||
|
"mx_StatusMessageContextMenu": true,
|
||||||
|
"mx_StatusMessageContextMenu_hasStatus": this.state.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div className={menuClasses}>
|
||||||
|
{ form }
|
||||||
|
<hr />
|
||||||
|
{ clearButton }
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,8 +36,12 @@ export default class ChangelogDialog extends React.Component {
|
||||||
for (let i=0; i<REPOS.length; i++) {
|
for (let i=0; i<REPOS.length; i++) {
|
||||||
const oldVersion = version2[2*i];
|
const oldVersion = version2[2*i];
|
||||||
const newVersion = version[2*i];
|
const newVersion = version[2*i];
|
||||||
request(`https://api.github.com/repos/${REPOS[i]}/compare/${oldVersion}...${newVersion}`, (a, b, body) => {
|
const url = `https://api.github.com/repos/${REPOS[i]}/compare/${oldVersion}...${newVersion}`;
|
||||||
if (body == null) return;
|
request(url, (err, response, body) => {
|
||||||
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||||
|
this.setState({ [REPOS[i]]: response.statusText });
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState({[REPOS[i]]: JSON.parse(body).commits});
|
this.setState({[REPOS[i]]: JSON.parse(body).commits});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -58,13 +62,20 @@ export default class ChangelogDialog extends React.Component {
|
||||||
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
||||||
|
|
||||||
const logs = REPOS.map(repo => {
|
const logs = REPOS.map(repo => {
|
||||||
if (this.state[repo] == null) return <Spinner key={repo} />;
|
let content;
|
||||||
|
if (this.state[repo] == null) {
|
||||||
|
content = <Spinner key={repo} />;
|
||||||
|
} else if (typeof this.state[repo] === "string") {
|
||||||
|
content = _t("Unable to load commit detail: %(msg)s", {
|
||||||
|
msg: this.state[repo],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
content = this.state[repo].map(this._elementsForCommit);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div key={repo}>
|
<div key={repo}>
|
||||||
<h2>{repo}</h2>
|
<h2>{repo}</h2>
|
||||||
<ul>
|
<ul>{content}</ul>
|
||||||
{this.state[repo].map(this._elementsForCommit)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,7 +29,6 @@ var TintableSvg = React.createClass({
|
||||||
width: PropTypes.string.isRequired,
|
width: PropTypes.string.isRequired,
|
||||||
height: PropTypes.string.isRequired,
|
height: PropTypes.string.isRequired,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
forceColors: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
statics: {
|
statics: {
|
||||||
|
@ -51,12 +50,6 @@ var TintableSvg = React.createClass({
|
||||||
delete TintableSvg.mounts[this.id];
|
delete TintableSvg.mounts[this.id];
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
|
||||||
if (prevProps.forceColors !== this.props.forceColors) {
|
|
||||||
this.calcAndApplyFixups(this.refs.svgContainer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
tint: function() {
|
tint: function() {
|
||||||
// TODO: only bother running this if the global tint settings have changed
|
// TODO: only bother running this if the global tint settings have changed
|
||||||
// since we loaded!
|
// since we loaded!
|
||||||
|
@ -64,13 +57,8 @@ var TintableSvg = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad: function(event) {
|
onLoad: function(event) {
|
||||||
this.calcAndApplyFixups(event.target);
|
// console.log("TintableSvg.onLoad for " + this.props.src);
|
||||||
},
|
this.fixups = Tinter.calcSvgFixups([event.target]);
|
||||||
|
|
||||||
calcAndApplyFixups: function(target) {
|
|
||||||
if (!target) return;
|
|
||||||
// console.log("TintableSvg.calcAndApplyFixups for " + this.props.src);
|
|
||||||
this.fixups = Tinter.calcSvgFixups([target], this.props.forceColors);
|
|
||||||
Tinter.applySvgFixups(this.fixups);
|
Tinter.applySvgFixups(this.fixups);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -83,7 +71,6 @@ var TintableSvg = React.createClass({
|
||||||
height={this.props.height}
|
height={this.props.height}
|
||||||
onLoad={this.onLoad}
|
onLoad={this.onLoad}
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
ref="svgContainer"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -85,8 +85,8 @@ export default React.createClass({
|
||||||
_getDisplayedGroups(userGroups, relatedGroups) {
|
_getDisplayedGroups(userGroups, relatedGroups) {
|
||||||
let displayedGroups = userGroups || [];
|
let displayedGroups = userGroups || [];
|
||||||
if (relatedGroups && relatedGroups.length > 0) {
|
if (relatedGroups && relatedGroups.length > 0) {
|
||||||
displayedGroups = displayedGroups.filter((groupId) => {
|
displayedGroups = relatedGroups.filter((groupId) => {
|
||||||
return relatedGroups.includes(groupId);
|
return displayedGroups.includes(groupId);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
displayedGroups = [];
|
displayedGroups = [];
|
||||||
|
|
|
@ -70,6 +70,7 @@ const EntityTile = React.createClass({
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
suppressOnHover: PropTypes.bool,
|
suppressOnHover: PropTypes.bool,
|
||||||
showPresence: PropTypes.bool,
|
showPresence: PropTypes.bool,
|
||||||
|
subtextLabel: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
@ -129,6 +130,9 @@ const EntityTile = React.createClass({
|
||||||
presenceState={this.props.presenceState} />;
|
presenceState={this.props.presenceState} />;
|
||||||
nameClasses += ' mx_EntityTile_name_hover';
|
nameClasses += ' mx_EntityTile_name_hover';
|
||||||
}
|
}
|
||||||
|
if (this.props.subtextLabel) {
|
||||||
|
presenceLabel = <span className="mx_EntityTile_subtext">{this.props.subtextLabel}</span>;
|
||||||
|
}
|
||||||
nameEl = (
|
nameEl = (
|
||||||
<div className="mx_EntityTile_details">
|
<div className="mx_EntityTile_details">
|
||||||
<EmojiText element="div" className={nameClasses} dir="auto">
|
<EmojiText element="div" className={nameClasses} dir="auto">
|
||||||
|
@ -137,6 +141,15 @@ const EntityTile = React.createClass({
|
||||||
{presenceLabel}
|
{presenceLabel}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (this.props.subtextLabel) {
|
||||||
|
nameEl = (
|
||||||
|
<div className="mx_EntityTile_details">
|
||||||
|
<EmojiText element="div" className="mx_EntityTile_name" dir="auto">
|
||||||
|
{name}
|
||||||
|
</EmojiText>
|
||||||
|
<span className="mx_EntityTile_subtext">{this.props.subtextLabel}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
nameEl = (
|
nameEl = (
|
||||||
<EmojiText element="div" className="mx_EntityTile_name" dir="auto">{ name }</EmojiText>
|
<EmojiText element="div" className="mx_EntityTile_name" dir="auto">{ name }</EmojiText>
|
||||||
|
|
|
@ -42,6 +42,7 @@ import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import MultiInviter from "../../../utils/MultiInviter";
|
import MultiInviter from "../../../utils/MultiInviter";
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
module.exports = withMatrixClient(React.createClass({
|
module.exports = withMatrixClient(React.createClass({
|
||||||
displayName: 'MemberInfo',
|
displayName: 'MemberInfo',
|
||||||
|
@ -889,11 +890,16 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
let presenceState;
|
let presenceState;
|
||||||
let presenceLastActiveAgo;
|
let presenceLastActiveAgo;
|
||||||
let presenceCurrentlyActive;
|
let presenceCurrentlyActive;
|
||||||
|
let statusMessage;
|
||||||
|
|
||||||
if (this.props.member.user) {
|
if (this.props.member.user) {
|
||||||
presenceState = this.props.member.user.presence;
|
presenceState = this.props.member.user.presence;
|
||||||
presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
|
presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
|
||||||
presenceCurrentlyActive = this.props.member.user.currentlyActive;
|
presenceCurrentlyActive = this.props.member.user.currentlyActive;
|
||||||
|
|
||||||
|
if (SettingsStore.isFeatureEnabled("feature_custom_status")) {
|
||||||
|
statusMessage = this.props.member.user._unstable_statusMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const room = this.props.matrixClient.getRoom(this.props.member.roomId);
|
const room = this.props.matrixClient.getRoom(this.props.member.roomId);
|
||||||
|
@ -915,6 +921,11 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
presenceState={presenceState} />;
|
presenceState={presenceState} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let statusLabel = null;
|
||||||
|
if (statusMessage) {
|
||||||
|
statusLabel = <span className="mx_MemberInfo_statusMessage">{ statusMessage }</span>;
|
||||||
|
}
|
||||||
|
|
||||||
let roomMemberDetails = null;
|
let roomMemberDetails = null;
|
||||||
if (this.props.member.roomId) { // is in room
|
if (this.props.member.roomId) { // is in room
|
||||||
const PowerSelector = sdk.getComponent('elements.PowerSelector');
|
const PowerSelector = sdk.getComponent('elements.PowerSelector');
|
||||||
|
@ -931,6 +942,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_MemberInfo_profileField">
|
<div className="mx_MemberInfo_profileField">
|
||||||
{presenceLabel}
|
{presenceLabel}
|
||||||
|
{statusLabel}
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
@ -85,6 +87,11 @@ module.exports = React.createClass({
|
||||||
const active = -1;
|
const active = -1;
|
||||||
const presenceState = member.user ? member.user.presence : null;
|
const presenceState = member.user ? member.user.presence : null;
|
||||||
|
|
||||||
|
let statusMessage = null;
|
||||||
|
if (member.user && SettingsStore.isFeatureEnabled("feature_custom_status")) {
|
||||||
|
statusMessage = member.user._unstable_statusMessage;
|
||||||
|
}
|
||||||
|
|
||||||
const av = (
|
const av = (
|
||||||
<MemberAvatar member={member} width={36} height={36} />
|
<MemberAvatar member={member} width={36} height={36} />
|
||||||
);
|
);
|
||||||
|
@ -106,7 +113,9 @@ module.exports = React.createClass({
|
||||||
presenceLastTs={member.user ? member.user.lastPresenceTs : 0}
|
presenceLastTs={member.user ? member.user.lastPresenceTs : 0}
|
||||||
presenceCurrentlyActive={member.user ? member.user.currentlyActive : false}
|
presenceCurrentlyActive={member.user ? member.user.currentlyActive : false}
|
||||||
avatarJsx={av} title={this.getPowerLabel()} onClick={this.onClick}
|
avatarJsx={av} title={this.getPowerLabel()} onClick={this.onClick}
|
||||||
name={name} powerStatus={powerStatus} showPresence={this.props.showPresence} />
|
name={name} powerStatus={powerStatus} showPresence={this.props.showPresence}
|
||||||
|
subtextLabel={statusMessage}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -291,7 +291,7 @@ export default class MessageComposer extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const uploadInputStyle = {display: 'none'};
|
const uploadInputStyle = {display: 'none'};
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");
|
const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ export default class MessageComposer extends React.Component {
|
||||||
if (this.state.me) {
|
if (this.state.me) {
|
||||||
controls.push(
|
controls.push(
|
||||||
<div key="controls_avatar" className="mx_MessageComposer_avatar">
|
<div key="controls_avatar" className="mx_MessageComposer_avatar">
|
||||||
<MemberAvatar member={this.state.me} width={24} height={24} />
|
<MemberStatusMessageAvatar member={this.state.me} width={24} height={24} />
|
||||||
</div>,
|
</div>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -349,6 +349,34 @@ export default class MessageComposer extends React.Component {
|
||||||
const canSendMessages = !this.state.tombstone &&
|
const canSendMessages = !this.state.tombstone &&
|
||||||
this.props.room.maySendMessage();
|
this.props.room.maySendMessage();
|
||||||
|
|
||||||
|
// TODO: Remove temporary logging for riot-web#7838
|
||||||
|
// Note: we rip apart the power level event ourselves because we don't want to
|
||||||
|
// log too much data about it - just the bits we care about. Many of the variables
|
||||||
|
// logged here are to help figure out where in the stack the 'cannot post in room'
|
||||||
|
// warning is coming from. This means logging various numbers from the PL event to
|
||||||
|
// verify RoomState._maySendEventOfType is doing the right thing.
|
||||||
|
const room = this.props.room;
|
||||||
|
const plEvent = room.currentState.getStateEvents('m.room.power_levels', '');
|
||||||
|
let plEventString = "<no power level event>";
|
||||||
|
if (plEvent) {
|
||||||
|
const content = plEvent.getContent();
|
||||||
|
if (!content) {
|
||||||
|
plEventString = "<no event content>";
|
||||||
|
} else {
|
||||||
|
const stringifyFalsey = (v) => v === null ? '<null>' : (v === undefined ? '<undefined>' : v);
|
||||||
|
const actualUserPl = stringifyFalsey(content.users ? content.users[room.myUserId] : "<no users in content>");
|
||||||
|
const usersPl = stringifyFalsey(content.users_default);
|
||||||
|
const actualEventPl = stringifyFalsey(content.events ? content.events['m.room.message'] : "<no events in content>");
|
||||||
|
const eventPl = stringifyFalsey(content.events_default);
|
||||||
|
plEventString = `actualUserPl=${actualUserPl} defaultUserPl=${usersPl} actualEventPl=${actualEventPl} defaultEventPl=${eventPl}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
`[riot-web#7838] renderComposer() hasTombstone=${!!this.state.tombstone} maySendMessage=${room.maySendMessage()}` +
|
||||||
|
` myMembership=${room.getMyMembership()} maySendEvent=${room.currentState.maySendEvent('m.room.message', room.myUserId)}` +
|
||||||
|
` myUserId=${room.myUserId} roomId=${room.roomId} hasPlEvent=${!!plEvent} powerLevels='${plEventString}'`
|
||||||
|
);
|
||||||
|
|
||||||
if (canSendMessages) {
|
if (canSendMessages) {
|
||||||
// This also currently includes the call buttons. Really we should
|
// This also currently includes the call buttons. Really we should
|
||||||
// check separately for whether we can call, but this is slightly
|
// check separately for whether we can call, but this is slightly
|
||||||
|
@ -425,6 +453,8 @@ export default class MessageComposer extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: Remove temporary logging for riot-web#7838
|
||||||
|
console.log("[riot-web#7838] Falling back to showing cannot post in room error");
|
||||||
controls.push(
|
controls.push(
|
||||||
<div key="controls_error" className="mx_MessageComposer_noperm_error">
|
<div key="controls_error" className="mx_MessageComposer_noperm_error">
|
||||||
{ _t('You do not have permission to post to this room') }
|
{ _t('You do not have permission to post to this room') }
|
||||||
|
|
|
@ -86,6 +86,7 @@ module.exports = React.createClass({
|
||||||
incomingCallTag: null,
|
incomingCallTag: null,
|
||||||
incomingCall: null,
|
incomingCall: null,
|
||||||
selectedTags: [],
|
selectedTags: [],
|
||||||
|
hover: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -294,6 +295,17 @@ module.exports = React.createClass({
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onMouseEnter: function(ev) {
|
||||||
|
this.setState({hover: true});
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseLeave: function(ev) {
|
||||||
|
this.setState({hover: false});
|
||||||
|
|
||||||
|
// Refresh the room list just in case the user missed something.
|
||||||
|
this._delayedRefreshRoomList();
|
||||||
|
},
|
||||||
|
|
||||||
_delayedRefreshRoomList: new rate_limited_func(function() {
|
_delayedRefreshRoomList: new rate_limited_func(function() {
|
||||||
this.refreshRoomList();
|
this.refreshRoomList();
|
||||||
}, 500),
|
}, 500),
|
||||||
|
@ -346,6 +358,11 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshRoomList: function() {
|
refreshRoomList: function() {
|
||||||
|
if (this.state.hover) {
|
||||||
|
// Don't re-sort the list if we're hovering over the list
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: ideally we'd calculate this once at start, and then maintain
|
// TODO: ideally we'd calculate this once at start, and then maintain
|
||||||
// any changes to it incrementally, updating the appropriate sublists
|
// any changes to it incrementally, updating the appropriate sublists
|
||||||
// as needed.
|
// as needed.
|
||||||
|
@ -693,7 +710,8 @@ module.exports = React.createClass({
|
||||||
const subListComponents = this._mapSubListProps(subLists);
|
const subListComponents = this._mapSubListProps(subLists);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={this._collectResizeContainer} className="mx_RoomList">
|
<div ref={this._collectResizeContainer} className="mx_RoomList"
|
||||||
|
onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
{ subListComponents }
|
{ subListComponents }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,13 +19,76 @@ import PropTypes from "prop-types";
|
||||||
import sdk from "../../../index";
|
import sdk from "../../../index";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
|
|
||||||
export default class RoomRecoveryReminder extends React.PureComponent {
|
export default class RoomRecoveryReminder extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
showKeyBackupDialog = () => {
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
error: null,
|
||||||
|
unverifiedDevice: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this._loadBackupStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _loadBackupStatus() {
|
||||||
|
let backupSigStatus;
|
||||||
|
try {
|
||||||
|
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||||
|
backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Unable to fetch key backup status", e);
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
error: e,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let unverifiedDevice;
|
||||||
|
for (const sig of backupSigStatus.sigs) {
|
||||||
|
if (!sig.device.isVerified()) {
|
||||||
|
unverifiedDevice = sig.device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
unverifiedDevice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showSetupDialog = () => {
|
||||||
|
if (this.state.unverifiedDevice) {
|
||||||
|
// A key backup exists for this account, but the creating device is not
|
||||||
|
// verified, so we'll show the device verify dialog.
|
||||||
|
// TODO: Should change to a restore key backup flow that checks the recovery
|
||||||
|
// passphrase while at the same time also cross-signing the device as well in
|
||||||
|
// a single flow (for cases where a key backup exists but the backup creating
|
||||||
|
// device is unverified). Since we don't have that yet, we'll look for an
|
||||||
|
// unverified device and verify it. Note that this means we won't restore
|
||||||
|
// keys yet; instead we'll only trust the backup for sending our own new keys
|
||||||
|
// to it.
|
||||||
|
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
|
||||||
|
Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, {
|
||||||
|
userId: MatrixClientPeg.get().credentials.userId,
|
||||||
|
device: this.state.unverifiedDevice,
|
||||||
|
onFinished: this.props.onFinished,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default case assumes that a key backup doesn't exist for this account, so
|
||||||
|
// we'll show the create key backup flow.
|
||||||
Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
|
Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
|
||||||
import("../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog"),
|
import("../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog"),
|
||||||
{
|
{
|
||||||
|
@ -46,29 +109,51 @@ export default class RoomRecoveryReminder extends React.PureComponent {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
},
|
||||||
onSetup: () => {
|
onSetup: () => {
|
||||||
this.showKeyBackupDialog();
|
this.showSetupDialog();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSetupClick = () => {
|
onSetupClick = () => {
|
||||||
this.showKeyBackupDialog();
|
this.showSetupDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.state.loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
||||||
|
|
||||||
|
let body;
|
||||||
|
if (this.state.error) {
|
||||||
|
body = <div className="error">
|
||||||
|
{_t("Unable to load key backup status")}
|
||||||
|
</div>;
|
||||||
|
} else if (this.state.unverifiedDevice) {
|
||||||
|
// A key backup exists for this account, but the creating device is not
|
||||||
|
// verified.
|
||||||
|
body = _t(
|
||||||
|
"To view your secure message history and ensure you can view new " +
|
||||||
|
"messages on future devices, set up Secure Message Recovery.",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// The default case assumes that a key backup doesn't exist for this account.
|
||||||
|
// (This component doesn't currently check that itself.)
|
||||||
|
body = _t(
|
||||||
|
"If you log out or use another device, you'll lose your " +
|
||||||
|
"secure message history. To prevent this, set up Secure " +
|
||||||
|
"Message Recovery.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomRecoveryReminder">
|
<div className="mx_RoomRecoveryReminder">
|
||||||
<div className="mx_RoomRecoveryReminder_header">{_t(
|
<div className="mx_RoomRecoveryReminder_header">{_t(
|
||||||
"Secure Message Recovery",
|
"Secure Message Recovery",
|
||||||
)}</div>
|
)}</div>
|
||||||
<div className="mx_RoomRecoveryReminder_body">{_t(
|
<div className="mx_RoomRecoveryReminder_body">{body}</div>
|
||||||
"If you log out or use another device, you'll lose your " +
|
|
||||||
"secure message history. To prevent this, set up Secure " +
|
|
||||||
"Message Recovery.",
|
|
||||||
)}</div>
|
|
||||||
<div className="mx_RoomRecoveryReminder_buttons">
|
<div className="mx_RoomRecoveryReminder_buttons">
|
||||||
<AccessibleButton className="mx_RoomRecoveryReminder_button mx_RoomRecoveryReminder_secondary"
|
<AccessibleButton className="mx_RoomRecoveryReminder_button mx_RoomRecoveryReminder_secondary"
|
||||||
onClick={this.onDontAskAgainClick}>
|
onClick={this.onDontAskAgainClick}>
|
||||||
|
|
|
@ -30,6 +30,7 @@ import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import ActiveRoomObserver from '../../../ActiveRoomObserver';
|
import ActiveRoomObserver from '../../../ActiveRoomObserver';
|
||||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RoomTile',
|
displayName: 'RoomTile',
|
||||||
|
@ -251,6 +252,17 @@ module.exports = React.createClass({
|
||||||
const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
|
const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
|
||||||
const badges = notifBadges || mentionBadges;
|
const badges = notifBadges || mentionBadges;
|
||||||
|
|
||||||
|
const isJoined = this.props.room.getMyMembership() === "join";
|
||||||
|
const looksLikeDm = this.props.room.getInvitedAndJoinedMemberCount() === 2;
|
||||||
|
let subtext = null;
|
||||||
|
if (!isInvite && isJoined && looksLikeDm && SettingsStore.isFeatureEnabled("feature_custom_status")) {
|
||||||
|
const selfId = MatrixClientPeg.get().getUserId();
|
||||||
|
const otherMember = this.props.room.currentState.getMembersExcept([selfId])[0];
|
||||||
|
if (otherMember && otherMember.user && otherMember.user._unstable_statusMessage) {
|
||||||
|
subtext = otherMember.user._unstable_statusMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
'mx_RoomTile': true,
|
'mx_RoomTile': true,
|
||||||
'mx_RoomTile_selected': this.state.selected,
|
'mx_RoomTile_selected': this.state.selected,
|
||||||
|
@ -261,6 +273,7 @@ module.exports = React.createClass({
|
||||||
'mx_RoomTile_menuDisplayed': this.state.menuDisplayed,
|
'mx_RoomTile_menuDisplayed': this.state.menuDisplayed,
|
||||||
'mx_RoomTile_noBadges': !badges,
|
'mx_RoomTile_noBadges': !badges,
|
||||||
'mx_RoomTile_transparent': this.props.transparent,
|
'mx_RoomTile_transparent': this.props.transparent,
|
||||||
|
'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed,
|
||||||
});
|
});
|
||||||
|
|
||||||
const avatarClasses = classNames({
|
const avatarClasses = classNames({
|
||||||
|
@ -286,6 +299,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||||
let label;
|
let label;
|
||||||
|
let subtextLabel;
|
||||||
let tooltip;
|
let tooltip;
|
||||||
if (!this.props.collapsed) {
|
if (!this.props.collapsed) {
|
||||||
const nameClasses = classNames({
|
const nameClasses = classNames({
|
||||||
|
@ -294,6 +308,8 @@ module.exports = React.createClass({
|
||||||
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed,
|
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
subtextLabel = subtext ? <span className="mx_RoomTile_subtext">{ subtext }</span> : null;
|
||||||
|
|
||||||
if (this.state.selected) {
|
if (this.state.selected) {
|
||||||
const nameSelected = <EmojiText>{ name }</EmojiText>;
|
const nameSelected = <EmojiText>{ name }</EmojiText>;
|
||||||
|
|
||||||
|
@ -337,9 +353,14 @@ module.exports = React.createClass({
|
||||||
{ dmIndicator }
|
{ dmIndicator }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{ label }
|
<div className="mx_RoomTile_nameContainer">
|
||||||
{ contextMenuButton }
|
<div className="mx_RoomTile_labelContainer">
|
||||||
{ badge }
|
{ label }
|
||||||
|
{ subtextLabel }
|
||||||
|
</div>
|
||||||
|
{ contextMenuButton }
|
||||||
|
{ badge }
|
||||||
|
</div>
|
||||||
{ /* { incomingCallBox } */ }
|
{ /* { incomingCallBox } */ }
|
||||||
{ tooltip }
|
{ tooltip }
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
|
|
|
@ -250,11 +250,14 @@
|
||||||
"A word by itself is easy to guess": "A word by itself is easy to guess",
|
"A word by itself is easy to guess": "A word by itself is easy to guess",
|
||||||
"Names and surnames by themselves are easy to guess": "Names and surnames by themselves are easy to guess",
|
"Names and surnames by themselves are easy to guess": "Names and surnames by themselves are easy to guess",
|
||||||
"Common names and surnames are easy to guess": "Common names and surnames are easy to guess",
|
"Common names and surnames are easy to guess": "Common names and surnames are easy to guess",
|
||||||
|
"Straight rows of keys are easy to guess": "Straight rows of keys are easy to guess",
|
||||||
|
"Short keyboard patterns are easy to guess": "Short keyboard patterns are easy to guess",
|
||||||
"There was an error joining the room": "There was an error joining the room",
|
"There was an error joining the room": "There was an error joining the room",
|
||||||
"Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.",
|
"Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.",
|
||||||
"Please contact your homeserver administrator.": "Please contact your homeserver administrator.",
|
"Please contact your homeserver administrator.": "Please contact your homeserver administrator.",
|
||||||
"Failed to join room": "Failed to join room",
|
"Failed to join room": "Failed to join room",
|
||||||
"Message Pinning": "Message Pinning",
|
"Message Pinning": "Message Pinning",
|
||||||
|
"Custom user status messages": "Custom user status messages",
|
||||||
"Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view",
|
"Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view",
|
||||||
"Backup of encryption keys to server": "Backup of encryption keys to server",
|
"Backup of encryption keys to server": "Backup of encryption keys to server",
|
||||||
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
||||||
|
@ -563,8 +566,9 @@
|
||||||
"You are trying to access a room.": "You are trying to access a room.",
|
"You are trying to access a room.": "You are trying to access a room.",
|
||||||
"<a>Click here</a> to join the discussion!": "<a>Click here</a> to join the discussion!",
|
"<a>Click here</a> to join the discussion!": "<a>Click here</a> to join the discussion!",
|
||||||
"This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled",
|
"This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled",
|
||||||
"Secure Message Recovery": "Secure Message Recovery",
|
"To view your secure message history and ensure you can view new messages on future devices, set up Secure Message Recovery.": "To view your secure message history and ensure you can view new messages on future devices, set up Secure Message Recovery.",
|
||||||
"If you log out or use another device, you'll lose your secure message history. To prevent this, set up Secure Message Recovery.": "If you log out or use another device, you'll lose your secure message history. To prevent this, set up Secure Message Recovery.",
|
"If you log out or use another device, you'll lose your secure message history. To prevent this, set up Secure Message Recovery.": "If you log out or use another device, you'll lose your secure message history. To prevent this, set up Secure Message Recovery.",
|
||||||
|
"Secure Message Recovery": "Secure Message Recovery",
|
||||||
"Don't ask again": "Don't ask again",
|
"Don't ask again": "Don't ask again",
|
||||||
"Set up": "Set up",
|
"Set up": "Set up",
|
||||||
"To change the room's avatar, you must be a": "To change the room's avatar, you must be a",
|
"To change the room's avatar, you must be a": "To change the room's avatar, you must be a",
|
||||||
|
@ -888,6 +892,7 @@
|
||||||
"What GitHub issue are these logs for?": "What GitHub issue are these logs for?",
|
"What GitHub issue are these logs for?": "What GitHub issue are these logs for?",
|
||||||
"Notes:": "Notes:",
|
"Notes:": "Notes:",
|
||||||
"Send logs": "Send logs",
|
"Send logs": "Send logs",
|
||||||
|
"Unable to load commit detail: %(msg)s": "Unable to load commit detail: %(msg)s",
|
||||||
"Unavailable": "Unavailable",
|
"Unavailable": "Unavailable",
|
||||||
"Changelog": "Changelog",
|
"Changelog": "Changelog",
|
||||||
"Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one",
|
"Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one",
|
||||||
|
@ -1064,6 +1069,8 @@
|
||||||
"Forget": "Forget",
|
"Forget": "Forget",
|
||||||
"Low Priority": "Low Priority",
|
"Low Priority": "Low Priority",
|
||||||
"Direct Chat": "Direct Chat",
|
"Direct Chat": "Direct Chat",
|
||||||
|
"Set a new status...": "Set a new status...",
|
||||||
|
"Clear status": "Clear status",
|
||||||
"View Community": "View Community",
|
"View Community": "View Community",
|
||||||
"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.",
|
||||||
|
|
|
@ -83,6 +83,12 @@ export const SETTINGS = {
|
||||||
supportedLevels: LEVELS_FEATURE,
|
supportedLevels: LEVELS_FEATURE,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
"feature_custom_status": {
|
||||||
|
isFeature: true,
|
||||||
|
displayName: _td("Custom user status messages"),
|
||||||
|
supportedLevels: LEVELS_FEATURE,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
"feature_lazyloading": {
|
"feature_lazyloading": {
|
||||||
isFeature: true,
|
isFeature: true,
|
||||||
displayName: _td("Increase performance by only loading room members on first view"),
|
displayName: _td("Increase performance by only loading room members on first view"),
|
||||||
|
|
|
@ -52,6 +52,8 @@ _td("This is similar to a commonly used password");
|
||||||
_td("A word by itself is easy to guess");
|
_td("A word by itself is easy to guess");
|
||||||
_td("Names and surnames by themselves are easy to guess");
|
_td("Names and surnames by themselves are easy to guess");
|
||||||
_td("Common names and surnames are easy to guess");
|
_td("Common names and surnames are easy to guess");
|
||||||
|
_td("Straight rows of keys are easy to guess");
|
||||||
|
_td("Short keyboard patterns are easy to guess");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around zxcvbn password strength estimation
|
* Wrapper around zxcvbn password strength estimation
|
||||||
|
|
|
@ -54,7 +54,7 @@ describe('DecryptionFailureTracker', function() {
|
||||||
// Immediately track the newest failures
|
// Immediately track the newest failures
|
||||||
tracker.trackFailures();
|
tracker.trackFailures();
|
||||||
|
|
||||||
expect(count).toNotBe(0, 'should track a failure for an event that failed decryption');
|
expect(count).not.toBe(0, 'should track a failure for an event that failed decryption');
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -185,21 +185,21 @@ describe('GroupView', function() {
|
||||||
const avatar = ReactTestUtils.findRenderedComponentWithType(root, sdk.getComponent('avatars.GroupAvatar'));
|
const avatar = ReactTestUtils.findRenderedComponentWithType(root, sdk.getComponent('avatars.GroupAvatar'));
|
||||||
const img = ReactTestUtils.findRenderedDOMComponentWithTag(avatar, 'img');
|
const img = ReactTestUtils.findRenderedDOMComponentWithTag(avatar, 'img');
|
||||||
const avatarImgElement = ReactDOM.findDOMNode(img);
|
const avatarImgElement = ReactDOM.findDOMNode(img);
|
||||||
expect(avatarImgElement).toExist();
|
expect(avatarImgElement).toBeTruthy();
|
||||||
expect(avatarImgElement.src).toInclude(
|
expect(avatarImgElement.src).toContain(
|
||||||
'https://my.home.server/_matrix/media/v1/thumbnail/' +
|
'https://my.home.server/_matrix/media/v1/thumbnail/' +
|
||||||
'someavatarurl?width=48&height=48&method=crop',
|
'someavatarurl?width=48&height=48&method=crop',
|
||||||
);
|
);
|
||||||
|
|
||||||
const name = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_name');
|
const name = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_name');
|
||||||
const nameElement = ReactDOM.findDOMNode(name);
|
const nameElement = ReactDOM.findDOMNode(name);
|
||||||
expect(nameElement).toExist();
|
expect(nameElement).toBeTruthy();
|
||||||
expect(nameElement.innerText).toInclude('The name of a community');
|
expect(nameElement.innerText).toContain('The name of a community');
|
||||||
expect(nameElement.innerText).toInclude(groupId);
|
expect(nameElement.innerText).toContain(groupId);
|
||||||
|
|
||||||
const shortDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_shortDesc');
|
const shortDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_shortDesc');
|
||||||
const shortDescElement = ReactDOM.findDOMNode(shortDesc);
|
const shortDescElement = ReactDOM.findDOMNode(shortDesc);
|
||||||
expect(shortDescElement).toExist();
|
expect(shortDescElement).toBeTruthy();
|
||||||
expect(shortDescElement.innerText).toBe('This is a community');
|
expect(shortDescElement.innerText).toBe('This is a community');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ describe('GroupView', function() {
|
||||||
|
|
||||||
const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
|
const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
|
||||||
const longDescElement = ReactDOM.findDOMNode(longDesc);
|
const longDescElement = ReactDOM.findDOMNode(longDesc);
|
||||||
expect(longDescElement).toExist();
|
expect(longDescElement).toBeTruthy();
|
||||||
expect(longDescElement.innerText).toBe('This is a LONG description.');
|
expect(longDescElement.innerText).toBe('This is a LONG description.');
|
||||||
expect(longDescElement.innerHTML).toBe('<div dir="auto">This is a <b>LONG</b> description.</div>');
|
expect(longDescElement.innerHTML).toBe('<div dir="auto">This is a <b>LONG</b> description.</div>');
|
||||||
});
|
});
|
||||||
|
@ -239,7 +239,7 @@ describe('GroupView', function() {
|
||||||
const placeholder = ReactTestUtils
|
const placeholder = ReactTestUtils
|
||||||
.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc_placeholder');
|
.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc_placeholder');
|
||||||
const placeholderElement = ReactDOM.findDOMNode(placeholder);
|
const placeholderElement = ReactDOM.findDOMNode(placeholder);
|
||||||
expect(placeholderElement).toExist();
|
expect(placeholderElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
httpBackend
|
httpBackend
|
||||||
|
@ -258,15 +258,15 @@ describe('GroupView', function() {
|
||||||
const prom = waitForUpdate(groupView, 4).then(() => {
|
const prom = waitForUpdate(groupView, 4).then(() => {
|
||||||
const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
|
const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
|
||||||
const longDescElement = ReactDOM.findDOMNode(longDesc);
|
const longDescElement = ReactDOM.findDOMNode(longDesc);
|
||||||
expect(longDescElement).toExist();
|
expect(longDescElement).toBeTruthy();
|
||||||
|
|
||||||
expect(longDescElement.innerHTML).toInclude('<h1>This is a more complicated group page</h1>');
|
expect(longDescElement.innerHTML).toContain('<h1>This is a more complicated group page</h1>');
|
||||||
expect(longDescElement.innerHTML).toInclude('<p>With paragraphs</p>');
|
expect(longDescElement.innerHTML).toContain('<p>With paragraphs</p>');
|
||||||
expect(longDescElement.innerHTML).toInclude('<ul>');
|
expect(longDescElement.innerHTML).toContain('<ul>');
|
||||||
expect(longDescElement.innerHTML).toInclude('<li>And lists!</li>');
|
expect(longDescElement.innerHTML).toContain('<li>And lists!</li>');
|
||||||
|
|
||||||
const imgSrc = "https://my.home.server/_matrix/media/v1/thumbnail/someimageurl?width=800&height=600";
|
const imgSrc = "https://my.home.server/_matrix/media/v1/thumbnail/someimageurl?width=800&height=600";
|
||||||
expect(longDescElement.innerHTML).toInclude('<img src="' + imgSrc + '">');
|
expect(longDescElement.innerHTML).toContain('<img src="' + imgSrc + '">');
|
||||||
});
|
});
|
||||||
|
|
||||||
httpBackend
|
httpBackend
|
||||||
|
@ -285,11 +285,11 @@ describe('GroupView', function() {
|
||||||
const prom = waitForUpdate(groupView, 4).then(() => {
|
const prom = waitForUpdate(groupView, 4).then(() => {
|
||||||
const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
|
const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
|
||||||
const longDescElement = ReactDOM.findDOMNode(longDesc);
|
const longDescElement = ReactDOM.findDOMNode(longDesc);
|
||||||
expect(longDescElement).toExist();
|
expect(longDescElement).toBeTruthy();
|
||||||
|
|
||||||
// If this fails, the URL could be in an img `src`, which is what we care about but
|
// If this fails, the URL could be in an img `src`, which is what we care about but
|
||||||
// there's no harm in keeping this simple and checking the entire HTML string.
|
// there's no harm in keeping this simple and checking the entire HTML string.
|
||||||
expect(longDescElement.innerHTML).toExclude('evilimageurl');
|
expect(longDescElement.innerHTML).not.toContain('evilimageurl');
|
||||||
});
|
});
|
||||||
|
|
||||||
httpBackend
|
httpBackend
|
||||||
|
@ -308,7 +308,7 @@ describe('GroupView', function() {
|
||||||
const prom = waitForUpdate(groupView, 4).then(() => {
|
const prom = waitForUpdate(groupView, 4).then(() => {
|
||||||
const roomDetailList = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_RoomDetailList');
|
const roomDetailList = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_RoomDetailList');
|
||||||
const roomDetailListElement = ReactDOM.findDOMNode(roomDetailList);
|
const roomDetailListElement = ReactDOM.findDOMNode(roomDetailList);
|
||||||
expect(roomDetailListElement).toExist();
|
expect(roomDetailListElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
|
httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
|
||||||
|
@ -325,7 +325,7 @@ describe('GroupView', function() {
|
||||||
const prom = waitForUpdate(groupView, 4).then(() => {
|
const prom = waitForUpdate(groupView, 4).then(() => {
|
||||||
const roomDetailList = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_RoomDetailList');
|
const roomDetailList = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_RoomDetailList');
|
||||||
const roomDetailListElement = ReactDOM.findDOMNode(roomDetailList);
|
const roomDetailListElement = ReactDOM.findDOMNode(roomDetailList);
|
||||||
expect(roomDetailListElement).toExist();
|
expect(roomDetailListElement).toBeTruthy();
|
||||||
|
|
||||||
const roomDetailListRoomName = ReactTestUtils.findRenderedDOMComponentWithClass(
|
const roomDetailListRoomName = ReactTestUtils.findRenderedDOMComponentWithClass(
|
||||||
root,
|
root,
|
||||||
|
@ -333,7 +333,7 @@ describe('GroupView', function() {
|
||||||
);
|
);
|
||||||
const roomDetailListRoomNameElement = ReactDOM.findDOMNode(roomDetailListRoomName);
|
const roomDetailListRoomNameElement = ReactDOM.findDOMNode(roomDetailListRoomName);
|
||||||
|
|
||||||
expect(roomDetailListRoomNameElement).toExist();
|
expect(roomDetailListRoomNameElement).toBeTruthy();
|
||||||
expect(roomDetailListRoomNameElement.innerText).toEqual('Some room name');
|
expect(roomDetailListRoomNameElement.innerText).toEqual('Some room name');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -364,7 +364,7 @@ describe('GroupView', function() {
|
||||||
const prom = waitForUpdate(groupView, 3).then(() => {
|
const prom = waitForUpdate(groupView, 3).then(() => {
|
||||||
const shortDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_shortDesc');
|
const shortDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_shortDesc');
|
||||||
const shortDescElement = ReactDOM.findDOMNode(shortDesc);
|
const shortDescElement = ReactDOM.findDOMNode(shortDesc);
|
||||||
expect(shortDescElement).toExist();
|
expect(shortDescElement).toBeTruthy();
|
||||||
expect(shortDescElement.innerText).toBe('This is a community');
|
expect(shortDescElement.innerText).toBe('This is a community');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const jest = require('jest-mock');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
const ReactTestUtils = require('react-addons-test-utils');
|
const ReactTestUtils = require('react-addons-test-utils');
|
||||||
|
@ -87,8 +88,8 @@ describe('Registration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT track a referral following successful registration of a non-team member', function(done) {
|
it('should NOT track a referral following successful registration of a non-team member', function(done) {
|
||||||
const onLoggedIn = expect.createSpy().andCall(function(creds, teamToken) {
|
const onLoggedIn = jest.fn(function(creds, teamToken) {
|
||||||
expect(teamToken).toNotExist();
|
expect(teamToken).toBeFalsy();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ describe('InteractiveAuthDialog', function() {
|
||||||
submitNode = node;
|
submitNode = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(passwordNode).toExist();
|
expect(passwordNode).toBeTruthy();
|
||||||
expect(submitNode).toExist();
|
expect(submitNode).toBeTruthy();
|
||||||
|
|
||||||
// submit should be disabled
|
// submit should be disabled
|
||||||
expect(submitNode.disabled).toBe(true);
|
expect(submitNode.disabled).toBe(true);
|
||||||
|
|
|
@ -114,7 +114,7 @@ describe("GroupMemberList", function() {
|
||||||
|
|
||||||
const memberList = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList_joined");
|
const memberList = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList_joined");
|
||||||
const memberListElement = ReactDOM.findDOMNode(memberList);
|
const memberListElement = ReactDOM.findDOMNode(memberList);
|
||||||
expect(memberListElement).toExist();
|
expect(memberListElement).toBeTruthy();
|
||||||
expect(memberListElement.innerText).toBe("Test");
|
expect(memberListElement.innerText).toBe("Test");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ describe("GroupMemberList", function() {
|
||||||
|
|
||||||
const memberList = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList_joined");
|
const memberList = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList_joined");
|
||||||
const memberListElement = ReactDOM.findDOMNode(memberList);
|
const memberListElement = ReactDOM.findDOMNode(memberList);
|
||||||
expect(memberListElement).toExist();
|
expect(memberListElement).toBeTruthy();
|
||||||
expect(memberListElement.innerText).toBe("Failed to load group members");
|
expect(memberListElement.innerText).toBe("Failed to load group members");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const jest = require('jest-mock');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require("react-dom");
|
const ReactDOM = require("react-dom");
|
||||||
const ReactTestUtils = require('react-addons-test-utils');
|
const ReactTestUtils = require('react-addons-test-utils');
|
||||||
|
@ -55,14 +56,14 @@ function doInputEmail(inputEmail, onTeamSelected) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectTeamSelectedFromEmailInput(inputEmail, expectedTeam) {
|
function expectTeamSelectedFromEmailInput(inputEmail, expectedTeam) {
|
||||||
const onTeamSelected = expect.createSpy();
|
const onTeamSelected = jest.fn();
|
||||||
doInputEmail(inputEmail, onTeamSelected);
|
doInputEmail(inputEmail, onTeamSelected);
|
||||||
|
|
||||||
expect(onTeamSelected).toHaveBeenCalledWith(expectedTeam);
|
expect(onTeamSelected).toHaveBeenCalledWith(expectedTeam);
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectSupportFromEmailInput(inputEmail, isSupportShown) {
|
function expectSupportFromEmailInput(inputEmail, isSupportShown) {
|
||||||
const onTeamSelected = expect.createSpy();
|
const onTeamSelected = jest.fn();
|
||||||
const res = doInputEmail(inputEmail, onTeamSelected);
|
const res = doInputEmail(inputEmail, onTeamSelected);
|
||||||
|
|
||||||
expect(res.state.showSupportEmail).toBe(isSupportShown);
|
expect(res.state.showSupportEmail).toBe(isSupportShown);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactTestUtils from 'react-addons-test-utils';
|
import ReactTestUtils from 'react-addons-test-utils';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import expect, {createSpy} from 'expect';
|
import expect from 'expect';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import * as testUtils from '../../../test-utils';
|
import * as testUtils from '../../../test-utils';
|
||||||
|
|
|
@ -69,7 +69,7 @@ describe('RoomList', () => {
|
||||||
ReactTestUtils.findRenderedComponentWithType(root, RoomList);
|
ReactTestUtils.findRenderedComponentWithType(root, RoomList);
|
||||||
|
|
||||||
movingRoom = createRoom({name: 'Moving room'});
|
movingRoom = createRoom({name: 'Moving room'});
|
||||||
expect(movingRoom.roomId).toNotBe(null);
|
expect(movingRoom.roomId).not.toBe(null);
|
||||||
|
|
||||||
// Mock joined member
|
// Mock joined member
|
||||||
myMember = new RoomMember(movingRoomId, myUserId);
|
myMember = new RoomMember(movingRoomId, myUserId);
|
||||||
|
@ -139,7 +139,7 @@ describe('RoomList', () => {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(expectedRoomTile).toExist();
|
expect(expectedRoomTile).toBeTruthy();
|
||||||
expect(expectedRoomTile.props.room).toBe(room);
|
expect(expectedRoomTile.props.room).toBe(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import expect, {createSpy} from 'expect';
|
import expect from 'expect';
|
||||||
|
import jest from 'jest-mock';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import * as testUtils from '../../../test-utils';
|
import * as testUtils from '../../../test-utils';
|
||||||
import sdk from 'matrix-react-sdk';
|
import sdk from 'matrix-react-sdk';
|
||||||
|
@ -18,12 +19,12 @@ describe('RoomSettings', () => {
|
||||||
|
|
||||||
function expectSentStateEvent(roomId, eventType, expectedEventContent) {
|
function expectSentStateEvent(roomId, eventType, expectedEventContent) {
|
||||||
let found = false;
|
let found = false;
|
||||||
for (const call of client.sendStateEvent.calls) {
|
for (const call of client.sendStateEvent.mock.calls) {
|
||||||
const [
|
const [
|
||||||
actualRoomId,
|
actualRoomId,
|
||||||
actualEventType,
|
actualEventType,
|
||||||
actualEventContent,
|
actualEventContent,
|
||||||
] = call.arguments.slice(0, 3);
|
] = call.slice(0, 3);
|
||||||
|
|
||||||
if (roomId === actualRoomId && actualEventType === eventType) {
|
if (roomId === actualRoomId && actualEventType === eventType) {
|
||||||
expect(actualEventContent).toEqual(expectedEventContent);
|
expect(actualEventContent).toEqual(expectedEventContent);
|
||||||
|
@ -40,20 +41,20 @@ describe('RoomSettings', () => {
|
||||||
client = MatrixClientPeg.get();
|
client = MatrixClientPeg.get();
|
||||||
client.credentials = {userId: '@me:domain.com'};
|
client.credentials = {userId: '@me:domain.com'};
|
||||||
|
|
||||||
client.setRoomName = createSpy().andReturn(Promise.resolve());
|
client.setRoomName = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
client.setRoomTopic = createSpy().andReturn(Promise.resolve());
|
client.setRoomTopic = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
client.setRoomDirectoryVisibility = createSpy().andReturn(Promise.resolve());
|
client.setRoomDirectoryVisibility = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
|
|
||||||
// Covers any room state event (e.g. name, avatar, topic)
|
// Covers any room state event (e.g. name, avatar, topic)
|
||||||
client.sendStateEvent = createSpy().andReturn(Promise.resolve());
|
client.sendStateEvent = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
|
|
||||||
// Covers room tagging
|
// Covers room tagging
|
||||||
client.setRoomTag = createSpy().andReturn(Promise.resolve());
|
client.setRoomTag = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
client.deleteRoomTag = createSpy().andReturn(Promise.resolve());
|
client.deleteRoomTag = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
|
|
||||||
// Covers any setting in the SettingsStore
|
// Covers any setting in the SettingsStore
|
||||||
// (including local client settings not stored via matrix)
|
// (including local client settings not stored via matrix)
|
||||||
SettingsStore.setValue = createSpy().andReturn(Promise.resolve());
|
SettingsStore.setValue = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
|
|
||||||
parentDiv = document.createElement('div');
|
parentDiv = document.createElement('div');
|
||||||
document.body.appendChild(parentDiv);
|
document.body.appendChild(parentDiv);
|
||||||
|
@ -83,9 +84,9 @@ describe('RoomSettings', () => {
|
||||||
|
|
||||||
it('should not set when no setting is changed', (done) => {
|
it('should not set when no setting is changed', (done) => {
|
||||||
roomSettings.save().then(() => {
|
roomSettings.save().then(() => {
|
||||||
expect(client.sendStateEvent).toNotHaveBeenCalled();
|
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||||
expect(client.setRoomTag).toNotHaveBeenCalled();
|
expect(client.setRoomTag).not.toHaveBeenCalled();
|
||||||
expect(client.deleteRoomTag).toNotHaveBeenCalled();
|
expect(client.deleteRoomTag).not.toHaveBeenCalled();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -93,7 +94,7 @@ describe('RoomSettings', () => {
|
||||||
// XXX: Apparently we do call SettingsStore.setValue
|
// XXX: Apparently we do call SettingsStore.setValue
|
||||||
xit('should not settings via the SettingsStore when no setting is changed', (done) => {
|
xit('should not settings via the SettingsStore when no setting is changed', (done) => {
|
||||||
roomSettings.save().then(() => {
|
roomSettings.save().then(() => {
|
||||||
expect(SettingsStore.setValue).toNotHaveBeenCalled();
|
expect(SettingsStore.setValue).not.toHaveBeenCalled();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -103,7 +104,7 @@ describe('RoomSettings', () => {
|
||||||
roomSettings.setName(name);
|
roomSettings.setName(name);
|
||||||
|
|
||||||
roomSettings.save().then(() => {
|
roomSettings.save().then(() => {
|
||||||
expect(client.setRoomName.calls[0].arguments.slice(0, 2))
|
expect(client.setRoomName.mock.calls[0].slice(0, 2))
|
||||||
.toEqual(['!DdJkzRliezrwpNebLk:matrix.org', name]);
|
.toEqual(['!DdJkzRliezrwpNebLk:matrix.org', name]);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
@ -115,7 +116,7 @@ describe('RoomSettings', () => {
|
||||||
roomSettings.setTopic(topic);
|
roomSettings.setTopic(topic);
|
||||||
|
|
||||||
roomSettings.save().then(() => {
|
roomSettings.save().then(() => {
|
||||||
expect(client.setRoomTopic.calls[0].arguments.slice(0, 2))
|
expect(client.setRoomTopic.mock.calls[0].slice(0, 2))
|
||||||
.toEqual(['!DdJkzRliezrwpNebLk:matrix.org', topic]);
|
.toEqual(['!DdJkzRliezrwpNebLk:matrix.org', topic]);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -39,7 +39,7 @@ describe('matrix-to', function() {
|
||||||
it('should pick no candidate servers when the room is not found', function() {
|
it('should pick no candidate servers when the room is not found', function() {
|
||||||
peg.get().getRoom = () => null;
|
peg.get().getRoom = () => null;
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(3);
|
expect(pickedServers.length).toBe(3);
|
||||||
expect(pickedServers[0]).toBe("pl_95");
|
expect(pickedServers[0]).toBe("pl_95");
|
||||||
// we don't check the 2nd and 3rd servers because that is done by the next test
|
// we don't check the 2nd and 3rd servers because that is done by the next test
|
||||||
|
@ -112,7 +112,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(3);
|
expect(pickedServers.length).toBe(3);
|
||||||
expect(pickedServers[0]).toBe("first");
|
expect(pickedServers[0]).toBe("first");
|
||||||
expect(pickedServers[1]).toBe("second");
|
expect(pickedServers[1]).toBe("second");
|
||||||
|
@ -143,7 +143,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(3);
|
expect(pickedServers.length).toBe(3);
|
||||||
expect(pickedServers[0]).toBe("first");
|
expect(pickedServers[0]).toBe("first");
|
||||||
expect(pickedServers[1]).toBe("second");
|
expect(pickedServers[1]).toBe("second");
|
||||||
|
@ -178,7 +178,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(3);
|
expect(pickedServers.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(1);
|
expect(pickedServers.length).toBe(1);
|
||||||
expect(pickedServers[0]).toBe("example.org:8448");
|
expect(pickedServers[0]).toBe("example.org:8448");
|
||||||
});
|
});
|
||||||
|
@ -292,7 +292,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(0);
|
expect(pickedServers.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -358,7 +358,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(1);
|
expect(pickedServers.length).toBe(1);
|
||||||
expect(pickedServers[0]).toEqual("evilcorp.com");
|
expect(pickedServers[0]).toEqual("evilcorp.com");
|
||||||
});
|
});
|
||||||
|
@ -392,7 +392,7 @@ describe('matrix-to', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
const pickedServers = pickServerCandidates("!somewhere:example.org");
|
||||||
expect(pickedServers).toExist();
|
expect(pickedServers).toBeTruthy();
|
||||||
expect(pickedServers.length).toBe(1);
|
expect(pickedServers.length).toBe(1);
|
||||||
expect(pickedServers[0]).toEqual("evilcorp.com");
|
expect(pickedServers[0]).toEqual("evilcorp.com");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue