From f34855573ef203182994eac6d9ecaa027225dd48 Mon Sep 17 00:00:00 2001
From: Bruno Windels
Date: Thu, 7 Feb 2019 16:24:26 +0000
Subject: [PATCH 01/46] replace ratelimitedfunc with lodash impl
---
src/ratelimitedfunc.js | 58 ++++++++++++------------------------------
1 file changed, 16 insertions(+), 42 deletions(-)
diff --git a/src/ratelimitedfunc.js b/src/ratelimitedfunc.js
index 20f6db79b8..1f15f11d91 100644
--- a/src/ratelimitedfunc.js
+++ b/src/ratelimitedfunc.js
@@ -20,54 +20,28 @@ limitations under the License.
* to update the interface once for all of them.
*
* Note that the function must not take arguments, since the args
- * could be different for each invocarion of the function.
+ * could be different for each invocation of the function.
*
* The returned function has a 'cancelPendingCall' property which can be called
* on unmount or similar to cancel any pending update.
*/
-module.exports = function(f, minIntervalMs) {
- this.lastCall = 0;
- this.scheduledCall = undefined;
- const self = this;
- const wrapper = function() {
- const now = Date.now();
+import { throttle } from "lodash";
- if (self.lastCall < now - minIntervalMs) {
- f.apply(this);
- // get the time again now the function has finished, so if it
- // took longer than the delay time to execute, it doesn't
- // immediately become eligible to run again.
- self.lastCall = Date.now();
- } else if (self.scheduledCall === undefined) {
- self.scheduledCall = setTimeout(
- () => {
- self.scheduledCall = undefined;
- f.apply(this);
- // get time again as per above
- self.lastCall = Date.now();
- },
- (self.lastCall + minIntervalMs) - now,
- );
- }
+export default function ratelimitedfunc(fn, time) {
+ const throttledFn = throttle(fn, time, {
+ leading: true,
+ trailing: true,
+ });
+ const _bind = throttledFn.bind;
+ throttledFn.bind = function() {
+ const boundFn = _bind.apply(throttledFn, arguments);
+ boundFn.cancelPendingCall = throttledFn.cancelPendingCall;
+ return boundFn;
};
- // add the cancelPendingCall property
- wrapper.cancelPendingCall = function() {
- if (self.scheduledCall) {
- clearTimeout(self.scheduledCall);
- self.scheduledCall = undefined;
- }
+ throttledFn.cancelPendingCall = function() {
+ throttledFn.cancel();
};
-
- // make sure that cancelPendingCall is copied when react rebinds the
- // wrapper
- const _bind = wrapper.bind;
- wrapper.bind = function() {
- const rebound = _bind.apply(this, arguments);
- rebound.cancelPendingCall = wrapper.cancelPendingCall;
- return rebound;
- };
-
- return wrapper;
-};
+ return throttledFn;
+}
From d2dd1bae135830f144f962a97fa246cc3ca3b260 Mon Sep 17 00:00:00 2001
From: Bruno Windels
Date: Thu, 7 Feb 2019 16:31:57 +0000
Subject: [PATCH 02/46] run search after you've stopped typing for 200ms
instead of every 500ms
---
src/components/structures/SearchBox.js | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.js
index fbcd9a7279..2f777c1163 100644
--- a/src/components/structures/SearchBox.js
+++ b/src/components/structures/SearchBox.js
@@ -21,7 +21,7 @@ import { _t } from '../../languageHandler';
import { KeyCode } from '../../Keyboard';
import sdk from '../../index';
import dis from '../../dispatcher';
-import rate_limited_func from '../../ratelimitedfunc';
+import { debounce } from 'lodash';
import AccessibleButton from '../../components/views/elements/AccessibleButton';
module.exports = React.createClass({
@@ -67,12 +67,9 @@ module.exports = React.createClass({
this.onSearch();
},
- onSearch: new rate_limited_func(
- function() {
- this.props.onSearch(this.refs.search.value);
- },
- 500,
- ),
+ onSearch: debounce(function() {
+ this.props.onSearch(this.refs.search.value);
+ }, 200, {trailing: true}),
_onKeyDown: function(ev) {
switch (ev.keyCode) {
From 60950b258aedf3ff4d874adb27d9037f40d1f71b Mon Sep 17 00:00:00 2001
From: Travis Ralston
Date: Fri, 8 Feb 2019 15:23:14 -0700
Subject: [PATCH 03/46] Scale up settings UI to be easier to read
Part of https://github.com/vector-im/riot-web/issues/8207
---
res/css/structures/_TabbedView.scss | 19 +++++++++----------
res/css/views/dialogs/_SettingsDialog.scss | 6 +++---
res/css/views/settings/tabs/_SettingsTab.scss | 8 ++++----
3 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss
index fb4df53d52..6e435b8e75 100644
--- a/res/css/structures/_TabbedView.scss
+++ b/res/css/structures/_TabbedView.scss
@@ -28,8 +28,8 @@ limitations under the License.
}
.mx_TabbedView_tabLabels {
- width: 150px;
- max-width: 150px;
+ width: 170px;
+ max-width: 170px;
color: $tab-label-fg-color;
position: fixed;
}
@@ -39,9 +39,8 @@ limitations under the License.
cursor: pointer;
display: block;
border-radius: 3px;
- font-size: 12px;
- font-weight: 600;
- min-height: 20px; // use min-height instead of height to allow the label to overflow a bit
+ font-size: 14px;
+ min-height: 24px; // use min-height instead of height to allow the label to overflow a bit
margin-bottom: 6px;
position: relative;
}
@@ -55,8 +54,8 @@ limitations under the License.
margin-left: 6px;
margin-right: 9px;
margin-top: 1px;
- width: 14px;
- height: 14px;
+ width: 16px;
+ height: 16px;
display: inline-block;
}
@@ -64,9 +63,9 @@ limitations under the License.
display: inline-block;
background-color: $tab-label-icon-bg-color;
mask-repeat: no-repeat;
- mask-size: 14px;
+ mask-size: 16px;
width: 14px;
- height: 18px;
+ height: 22px;
mask-position: center;
content: '';
vertical-align: middle;
@@ -81,7 +80,7 @@ limitations under the License.
}
.mx_TabbedView_tabPanel {
- margin-left: 220px; // 150px sidebar + 70px padding
+ margin-left: 240px; // 170px sidebar + 70px padding
flex-grow: 1;
display: flex;
flex-direction: column;
diff --git a/res/css/views/dialogs/_SettingsDialog.scss b/res/css/views/dialogs/_SettingsDialog.scss
index 4a9708f6d1..abf0048cfd 100644
--- a/res/css/views/dialogs/_SettingsDialog.scss
+++ b/res/css/views/dialogs/_SettingsDialog.scss
@@ -16,8 +16,8 @@ limitations under the License.
.mx_SettingsDialog {
.mx_Dialog {
- max-width: 900px;
- width: 80%;
+ max-width: 1000px;
+ width: 90%;
height: 80%;
border-radius: 4px;
padding-top: 0;
@@ -30,7 +30,7 @@ limitations under the License.
.mx_TabbedView .mx_SettingsTab {
box-sizing: border-box;
- min-width: 550px;
+ min-width: 580px;
padding-right: 130px;
// Put some padding on the bottom to avoid the settings tab from
diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss
index a899aec0fa..626c0e32eb 100644
--- a/res/css/views/settings/tabs/_SettingsTab.scss
+++ b/res/css/views/settings/tabs/_SettingsTab.scss
@@ -21,7 +21,7 @@ limitations under the License.
}
.mx_SettingsTab_subheading {
- font-size: 14px;
+ font-size: 16px;
display: block;
font-family: $font-family;
font-weight: 600;
@@ -32,7 +32,7 @@ limitations under the License.
.mx_SettingsTab_subsectionText {
color: $settings-subsection-fg-color;
- font-size: 12px;
+ font-size: 14px;
padding-bottom: 12px;
display: block;
margin: 0 100px 0 0; // Align with the rest of the view
@@ -45,9 +45,9 @@ limitations under the License.
}
.mx_SettingsTab_section .mx_SettingsFlag .mx_SettingsFlag_label {
- vertical-align: bottom;
+ vertical-align: middle;
display: inline-block;
- font-size: 12px;
+ font-size: 14px;
color: $primary-fg-color;
max-width: calc(100% - 48px); // Force word wrap instead of colliding with the switch
}
From 1cf0a6a0490f53864d63ec6c0a3a87f2f2f7f7ce Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 11 Feb 2019 12:08:10 +0000
Subject: [PATCH 04/46] Add legacy verification button on wait
Add a way to bail out of interactive verification from the screen
where you're waiting for the other person to accept.
---
.../views/dialogs/DeviceVerifyDialog.js | 15 +++++++++++++++
src/i18n/strings/en_EN.json | 1 +
2 files changed, 16 insertions(+)
diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js
index 47ee73c61c..269facd107 100644
--- a/src/components/views/dialogs/DeviceVerifyDialog.js
+++ b/src/components/views/dialogs/DeviceVerifyDialog.js
@@ -60,6 +60,11 @@ export default class DeviceVerifyDialog extends React.Component {
}
_onSwitchToLegacyClick = () => {
+ if (this._verifier) {
+ this._verifier.removeListener('show_sas', this._onVerifierShowSas);
+ this._verifier.cancel('User cancel');
+ this._verifier = null;
+ }
this.setState({mode: MODE_LEGACY});
}
@@ -184,11 +189,21 @@ export default class DeviceVerifyDialog extends React.Component {
_renderSasVerificationPhaseWaitAccept() {
const Spinner = sdk.getComponent("views.elements.Spinner");
+ const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
return (
{_t("Waiting for partner to accept...")}
+
{_t(
+ "Nothing appearing? Not all clients support interactive verification yet. " +
+ ".",
+ {}, {button: sub =>
+ {sub}
+ }
+ )}
);
}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index f48e306641..3cc6e71436 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1061,6 +1061,7 @@
"Verify by comparing a short text string.": "Verify by comparing a short text string.",
"Begin Verifying": "Begin Verifying",
"Waiting for partner to accept...": "Waiting for partner to accept...",
+ "Nothing appearing? Not all clients support interactive verification yet. .": "Nothing appearing? Not all clients support interactive verification yet. .",
"Waiting for %(userId)s to confirm...": "Waiting for %(userId)s to confirm...",
"Use two-way text verification": "Use two-way text verification",
"To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:",
From 97aa0b52fad7a0fdbd63bd183bf4c107545b3834 Mon Sep 17 00:00:00 2001
From: "J. Ryan Stinnett"
Date: Mon, 11 Feb 2019 12:12:47 +0000
Subject: [PATCH 05/46] Avoid room directory error without a client
---
src/components/structures/RoomDirectory.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index 5309b02041..e13eab8eb3 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -78,6 +78,11 @@ module.exports = React.createClass({
this.protocols = null;
this.setState({protocolsLoading: true});
+ if (!MatrixClientPeg.get()) {
+ // We may not have a client yet when invoked from welcome page
+ this.setState({protocolsLoading: false});
+ return;
+ }
MatrixClientPeg.get().getThirdpartyProtocols().done((response) => {
this.protocols = response;
this.setState({protocolsLoading: false});
From ba597c65eba04d06b48a41a41f148e619b2cc400 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 11 Feb 2019 13:09:50 +0000
Subject: [PATCH 06/46] lint
---
src/components/views/dialogs/DeviceVerifyDialog.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js
index 269facd107..c901942fcd 100644
--- a/src/components/views/dialogs/DeviceVerifyDialog.js
+++ b/src/components/views/dialogs/DeviceVerifyDialog.js
@@ -202,7 +202,7 @@ export default class DeviceVerifyDialog extends React.Component {
onClick={this._onSwitchToLegacyClick}
>
{sub}
- }
+ },
)}
);
From 119806bdbd8494724206ca3112c4f3410f6c80cf Mon Sep 17 00:00:00 2001
From: Will Hunt
Date: Mon, 11 Feb 2019 13:31:06 +0000
Subject: [PATCH 07/46] !important shouldn't have a space
I wonder if this is making SCSS choke.
---
res/css/views/rooms/_RoomTile.scss | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss
index 220790784e..2024a503ae 100644
--- a/res/css/views/rooms/_RoomTile.scss
+++ b/res/css/views/rooms/_RoomTile.scss
@@ -154,7 +154,7 @@ limitations under the License.
}
.mx_RoomTile_unread, .mx_RoomTile_highlight {
- font-weight: 700 ! important;
+ font-weight: 700 !important;
.mx_RoomTile_name {
color: $roomtile-selected-color;
@@ -176,7 +176,7 @@ limitations under the License.
}
.mx_RoomTile:focus {
- filter: none ! important;
+ filter: none !important;
background-color: $roomtile-focused-bg-color;
}
From 477d5ac0dcc612202fba4b04d10da290c479a468 Mon Sep 17 00:00:00 2001
From: "J. Ryan Stinnett"
Date: Mon, 11 Feb 2019 13:31:27 +0000
Subject: [PATCH 08/46] View welcome behind modals when not showing logged in
view
---
src/components/structures/MatrixChat.js | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 1453007b18..7c03aaec57 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -574,11 +574,8 @@ export default React.createClass({
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}, 'mx_SettingsDialog');
- // View the home page if we need something to look at
- if (!this.state.currentGroupId && !this.state.currentRoomId) {
- this._setPage(PageTypes.HomePage);
- this.notifyNewScreen('home');
- }
+ // View the welcome or home page if we need something to look at
+ this._viewSomethingBehindModal();
break;
}
case 'view_create_room':
@@ -595,11 +592,8 @@ export default React.createClass({
config: this.props.config,
}, 'mx_RoomDirectory_dialogWrapper');
- // View the home page if we need something to look at
- if (!this.state.currentGroupId && !this.state.currentRoomId) {
- this._setPage(PageTypes.HomePage);
- this.notifyNewScreen('home');
- }
+ // View the welcome or home page if we need something to look at
+ this._viewSomethingBehindModal();
}
break;
case 'view_my_groups':
@@ -887,6 +881,16 @@ export default React.createClass({
this.notifyNewScreen('group/' + groupId);
},
+ _viewSomethingBehindModal() {
+ if (this.state.view !== VIEWS.LOGGED_IN) {
+ this._viewWelcome();
+ return;
+ }
+ if (!this.state.currentGroupId && !this.state.currentRoomId) {
+ this._viewHome();
+ }
+ },
+
_viewWelcome() {
this.setStateForNewView({
view: VIEWS.WELCOME,
From d66dbf9be79feed80295080f3088852138235316 Mon Sep 17 00:00:00 2001
From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
Date: Mon, 11 Feb 2019 13:38:47 +0000
Subject: [PATCH 09/46] Change taking a community off the left-left panel less
scary
With the current wording, I half-think clicking this button might remove me from the community!
IMO 'Hide' sounds more like it's just going to disappear from the panel, but I can add it back at some point.
---
src/components/views/context_menus/TagTileContextMenu.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/context_menus/TagTileContextMenu.js b/src/components/views/context_menus/TagTileContextMenu.js
index 8b868e7b11..c0203a3ac8 100644
--- a/src/components/views/context_menus/TagTileContextMenu.js
+++ b/src/components/views/context_menus/TagTileContextMenu.js
@@ -68,7 +68,7 @@ export default class TagTileContextMenu extends React.Component {
- { _t('Remove') }
+ { _t('Hide') }
;
}
From 4101d4e9de0e4735106930bc7b8a0366901c56fd Mon Sep 17 00:00:00 2001
From: "J. Ryan Stinnett"
Date: Mon, 11 Feb 2019 13:49:18 +0000
Subject: [PATCH 10/46] Always change to LOGGED_IN view to show a room
---
src/components/structures/MatrixChat.js | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 7c03aaec57..4bb4e34033 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -819,6 +819,7 @@ export default React.createClass({
this.focusComposer = true;
const newState = {
+ view: VIEWS.LOGGED_IN,
currentRoomId: roomInfo.room_id || null,
page_type: PageTypes.RoomView,
thirdPartyInvite: roomInfo.third_party_invite,
@@ -1556,11 +1557,7 @@ export default React.createClass({
payload.room_id = roomString;
}
- // we can't view a room unless we're logged in
- // (a guest account is fine)
- if (this.state.view === VIEWS.LOGGED_IN) {
- dis.dispatch(payload);
- }
+ dis.dispatch(payload);
} else if (screen.indexOf('user/') == 0) {
const userId = screen.substring(5);
From 9d3ba2b3d9a9fcc0f8ee1fc222c9da9d26ce3d25 Mon Sep 17 00:00:00 2001
From: "J. Ryan Stinnett"
Date: Mon, 11 Feb 2019 14:33:19 +0000
Subject: [PATCH 11/46] Guard against invalid room object on join
---
src/components/structures/RoomView.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 50fa18e075..f75393c6db 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -877,13 +877,12 @@ module.exports = React.createClass({
// If the user is a ROU, allow them to transition to a PWLU
if (cli && cli.isGuest()) {
// Join this room once the user has registered and logged in
- const signUrl = this.props.thirdPartyInvite ?
- this.props.thirdPartyInvite.inviteSignUrl : undefined;
+ // (If we failed to peek, we may not have a valid room object.)
dis.dispatch({
action: 'do_after_sync_prepared',
deferred_action: {
action: 'view_room',
- room_id: this.state.room.roomId,
+ room_id: this.state.room ? this.state.room.roomId : this.state.roomId,
},
});
From b6aa72da55e2db5d0532dd8f11edd277430d0863 Mon Sep 17 00:00:00 2001
From: Bruno Windels
Date: Mon, 11 Feb 2019 15:40:17 +0100
Subject: [PATCH 12/46] update RoomTile notificationCount through props so
updates are consistent
---
src/components/structures/RoomSubList.js | 1 +
src/components/views/rooms/RoomTile.js | 11 +----------
2 files changed, 2 insertions(+), 10 deletions(-)
diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js
index 4644d19f61..ca2be85b35 100644
--- a/src/components/structures/RoomSubList.js
+++ b/src/components/structures/RoomSubList.js
@@ -145,6 +145,7 @@ const RoomSubList = React.createClass({
collapsed={this.props.collapsed || false}
unread={Unread.doesRoomHaveUnreadMessages(room)}
highlight={room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite}
+ notificationCount={room.getUnreadNotificationCount()}
isInvite={this.props.isInvite}
refreshSubList={this._updateSubListCount}
incomingCall={null}
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js
index ed214812b5..f9e9d64b9e 100644
--- a/src/components/views/rooms/RoomTile.js
+++ b/src/components/views/rooms/RoomTile.js
@@ -108,13 +108,6 @@ module.exports = React.createClass({
return statusUser._unstable_statusMessage;
},
- onRoomTimeline: function(ev, room) {
- if (room !== this.props.room) return;
- this.setState({
- notificationCount: this.props.room.getUnreadNotificationCount(),
- });
- },
-
onRoomName: function(room) {
if (room !== this.props.room) return;
this.setState({
@@ -159,7 +152,6 @@ module.exports = React.createClass({
componentWillMount: function() {
MatrixClientPeg.get().on("accountData", this.onAccountData);
- MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().on("Room.name", this.onRoomName);
ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange);
this.dispatcherRef = dis.register(this.onAction);
@@ -179,7 +171,6 @@ module.exports = React.createClass({
const cli = MatrixClientPeg.get();
if (cli) {
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
- MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
}
ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange);
@@ -306,7 +297,7 @@ module.exports = React.createClass({
render: function() {
const isInvite = this.props.room.getMyMembership() === "invite";
- const notificationCount = this.state.notificationCount;
+ const notificationCount = this.props.notificationCount;
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
From d069f4a89a4057d5107d1b2452068426d51eefc6 Mon Sep 17 00:00:00 2001
From: Bruno Windels
Date: Mon, 11 Feb 2019 15:41:32 +0100
Subject: [PATCH 13/46] not the cause of the bug, but this seems wrong
---
src/Unread.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Unread.js b/src/Unread.js
index 55e60f2a9a..9514ec821b 100644
--- a/src/Unread.js
+++ b/src/Unread.js
@@ -32,7 +32,7 @@ module.exports = {
return false;
} else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
return false;
- } else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
+ } else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
return false;
}
const EventTile = sdk.getComponent('rooms.EventTile');
From fbc0bbfb6f7e80639c9d5288df632ead62938244 Mon Sep 17 00:00:00 2001
From: Bruno Windels
Date: Mon, 11 Feb 2019 16:17:15 +0100
Subject: [PATCH 14/46] clear hover flag after 1000ms of not moving the mouse
as a fix for #8184
---
src/components/views/rooms/RoomList.js | 30 +++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 56eb4b713d..1d207835bc 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -17,6 +17,7 @@ limitations under the License.
'use strict';
import SettingsStore from "../../../settings/SettingsStore";
+import Timer from "../../../utils/Timer";
const React = require("react");
const ReactDOM = require("react-dom");
@@ -41,6 +42,7 @@ import {Resizer} from '../../../resizer';
import {Layout, Distributor} from '../../../resizer/distributors/roomsublist2';
const HIDE_CONFERENCE_CHANS = true;
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
+const HOVER_MOVE_TIMEOUT = 1000;
function labelForTagName(tagName) {
if (tagName.startsWith('u.')) return tagName.slice(2);
@@ -73,6 +75,7 @@ module.exports = React.createClass({
getInitialState: function() {
+ this._hoverClearTimer = null;
this._subListRefs = {
// key => RoomSubList ref
};
@@ -357,11 +360,32 @@ module.exports = React.createClass({
this.forceUpdate();
},
- onMouseEnter: function(ev) {
- this.setState({hover: true});
+ onMouseMove: async function(ev) {
+ if (!this._hoverClearTimer) {
+ this.setState({hover: true});
+ this._hoverClearTimer = new Timer(HOVER_MOVE_TIMEOUT);
+ this._hoverClearTimer.start();
+ let finished = true;
+ try {
+ await this._hoverClearTimer.finished();
+ } catch (err) {
+ finished = false;
+ }
+ this._hoverClearTimer = null;
+ if (finished) {
+ this.setState({hover: false});
+ this._delayedRefreshRoomList();
+ }
+ } else {
+ this._hoverClearTimer.restart();
+ }
},
onMouseLeave: function(ev) {
+ if (this._hoverClearTimer) {
+ this._hoverClearTimer.abort();
+ this._hoverClearTimer = null;
+ }
this.setState({hover: false});
// Refresh the room list just in case the user missed something.
@@ -774,7 +798,7 @@ module.exports = React.createClass({
return (
);
From ab6535b1353b18f7c03bc141e74aed8dd956fe3d Mon Sep 17 00:00:00 2001
From: Bruno Windels
Date: Mon, 11 Feb 2019 16:27:29 +0100
Subject: [PATCH 15/46] lint
---
src/components/views/rooms/RoomList.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 1d207835bc..227dd318ed 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -98,7 +98,7 @@ module.exports = React.createClass({
// update overflow indicators
this._checkSubListsOverflow();
// don't store height for collapsed sublists
- if(!this.collapsedState[key]) {
+ if (!this.collapsedState[key]) {
this.subListSizes[key] = size;
window.localStorage.setItem("mx_roomlist_sizes",
JSON.stringify(this.subListSizes));
From 99ae63c0218c44afc5bb75e363b70ec8f50b108c Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 11 Feb 2019 15:57:34 +0000
Subject: [PATCH 16/46] Add display name / avatar to incoming sas dialog
Fetch the other user's profile & display it on an incoming verification
request
---
res/css/_components.scss | 1 +
res/css/views/dialogs/_IncomingSasDialog.scss | 24 +++++++++
.../views/dialogs/IncomingSasDialog.js | 50 ++++++++++++++++++-
3 files changed, 74 insertions(+), 1 deletion(-)
create mode 100644 res/css/views/dialogs/_IncomingSasDialog.scss
diff --git a/res/css/_components.scss b/res/css/_components.scss
index 57a34023c0..80add0b1ad 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -57,6 +57,7 @@
@import "./views/dialogs/_DevtoolsDialog.scss";
@import "./views/dialogs/_EncryptedEventDialog.scss";
@import "./views/dialogs/_GroupAddressPicker.scss";
+@import "./views/dialogs/_IncomingSasDialog.scss";
@import "./views/dialogs/_RestoreKeyBackupDialog.scss";
@import "./views/dialogs/_RoomSettingsDialog.scss";
@import "./views/dialogs/_RoomUpgradeDialog.scss";
diff --git a/res/css/views/dialogs/_IncomingSasDialog.scss b/res/css/views/dialogs/_IncomingSasDialog.scss
new file mode 100644
index 0000000000..3a9d645a98
--- /dev/null
+++ b/res/css/views/dialogs/_IncomingSasDialog.scss
@@ -0,0 +1,24 @@
+/*
+Copyright 2019 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_IncomingSasDialog_opponentProfile_image {
+ position: relative;
+}
+
+.mx_IncomingSasDialog_opponentProfile h2 {
+ display: inline-block;
+ margin-left: 10px;
+}
diff --git a/src/components/views/dialogs/IncomingSasDialog.js b/src/components/views/dialogs/IncomingSasDialog.js
index 2a76e8a904..da2211c10f 100644
--- a/src/components/views/dialogs/IncomingSasDialog.js
+++ b/src/components/views/dialogs/IncomingSasDialog.js
@@ -16,6 +16,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
+import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
@@ -37,9 +38,12 @@ export default class IncomingSasDialog extends React.Component {
this.state = {
phase: PHASE_START,
sasVerified: false,
+ opponentProfile: null,
+ opponentProfileError: null,
};
this.props.verifier.on('show_sas', this._onVerifierShowSas);
this.props.verifier.on('cancel', this._onVerifierCancel);
+ this._fetchOpponentProfile();
}
componentWillUnmount() {
@@ -49,6 +53,21 @@ export default class IncomingSasDialog extends React.Component {
this.props.verifier.removeListener('show_sas', this._onVerifierShowSas);
}
+ async _fetchOpponentProfile() {
+ try {
+ const prof = await MatrixClientPeg.get().getProfileInfo(
+ this.props.verifier.userId,
+ );
+ this.setState({
+ opponentProfile: prof,
+ });
+ } catch (e) {
+ this.setState({
+ opponentProfileError: e,
+ });
+ }
+ }
+
_onFinished = () => {
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
}
@@ -93,10 +112,39 @@ export default class IncomingSasDialog extends React.Component {
_renderPhaseStart() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
+ const Spinner = sdk.getComponent("views.elements.Spinner");
+ const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
+
+ let profile;
+ if (this.state.opponentProfile) {
+ profile =
+
+
{this.state.opponentProfile.displayname}
+
;
+ } else if (this.state.opponentProfileError) {
+ profile =
+
+
{this.props.verifier.userId}
+
;
+ } else {
+ profile = ;
+ }
return (
-
{this.props.verifier.userId}
+ {profile}
{_t(
"Verify this user to mark them as trusted. " +
"Trusting users gives you extra peace of mind when using " +
From 6aef9fcc75ec7a1a6b6439c9563b3b361cefb254 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 11 Feb 2019 16:41:06 +0000
Subject: [PATCH 17/46] Restore backup on new recovery method dialog
Rather than verifying
---
.../keybackup/NewRecoveryMethodDialog.js | 37 +------------------
1 file changed, 2 insertions(+), 35 deletions(-)
diff --git a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js
index db86178b5a..28281af771 100644
--- a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js
+++ b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js
@@ -39,36 +39,8 @@ export default class NewRecoveryMethodDialog extends React.PureComponent {
}
onSetupClick = async () => {
- // 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. 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.
- let backupSigStatus;
- try {
- backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(this.props.newVersionInfo);
- } catch (e) {
- console.log("Unable to fetch key backup status", e);
- return;
- }
-
- let unverifiedDevice;
- for (const sig of backupSigStatus.sigs) {
- if (!sig.device.isVerified()) {
- unverifiedDevice = sig.device;
- break;
- }
- }
- if (!unverifiedDevice) {
- console.log("Unable to find a device to verify.");
- return;
- }
-
- const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
- Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, {
- userId: MatrixClientPeg.get().credentials.userId,
- device: unverifiedDevice,
+ const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
+ Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, {
onFinished: this.props.onFinished,
});
}
@@ -111,11 +83,6 @@ export default class NewRecoveryMethodDialog extends React.PureComponent {
} else {
content =
{newMethodDetected}
-
{_t(
- "Setting up Secure Messages on this device " +
- "will re-encrypt this device's message history with " +
- "the new recovery method.",
- )}
{hackWarning}
Date: Tue, 12 Feb 2019 08:59:38 +0000
Subject: [PATCH 18/46] Explain roomId workaround
---
src/components/structures/RoomView.js | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index f75393c6db..b233662898 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -283,6 +283,15 @@ module.exports = React.createClass({
}
},
+ _getRoomId() {
+ // According to `_onRoomViewStoreUpdate`, `state.roomId` can be null
+ // if we have a room alias we haven't resolved yet. To work around this,
+ // first we'll try the room object if it's there, and then fallback to
+ // the bare room ID. (We may want to update `state.roomId` after
+ // resolving aliases, so we could always trust it.)
+ return this.state.room ? this.state.room.roomId : this.state.roomId;
+ },
+
_onWidgetEchoStoreUpdate: function() {
this.setState({
showApps: this._shouldShowApps(this.state.room),
@@ -882,7 +891,7 @@ module.exports = React.createClass({
action: 'do_after_sync_prepared',
deferred_action: {
action: 'view_room',
- room_id: this.state.room ? this.state.room.roomId : this.state.roomId,
+ room_id: this._getRoomId(),
},
});
From aaea40a93d7f684b3c4c1593c5ab99730b766285 Mon Sep 17 00:00:00 2001
From: Bruno Windels
Date: Tue, 12 Feb 2019 11:04:25 +0100
Subject: [PATCH 19/46] add breadcrumbs component
---
res/css/_components.scss | 1 +
res/css/views/rooms/_RoomBreadcrumbs.scss | 47 +++++++++++
src/components/structures/LeftPanel.js | 2 +
src/components/views/rooms/RoomBreadcrumbs.js | 84 +++++++++++++++++++
4 files changed, 134 insertions(+)
create mode 100644 res/css/views/rooms/_RoomBreadcrumbs.scss
create mode 100644 src/components/views/rooms/RoomBreadcrumbs.js
diff --git a/res/css/_components.scss b/res/css/_components.scss
index 57a34023c0..62c6186152 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -126,6 +126,7 @@
@import "./views/rooms/_PinnedEventsPanel.scss";
@import "./views/rooms/_PresenceLabel.scss";
@import "./views/rooms/_ReplyPreview.scss";
+@import "./views/rooms/_RoomBreadcrumbs.scss";
@import "./views/rooms/_RoomDropTarget.scss";
@import "./views/rooms/_RoomHeader.scss";
@import "./views/rooms/_RoomList.scss";
diff --git a/res/css/views/rooms/_RoomBreadcrumbs.scss b/res/css/views/rooms/_RoomBreadcrumbs.scss
new file mode 100644
index 0000000000..41149fc0b2
--- /dev/null
+++ b/res/css/views/rooms/_RoomBreadcrumbs.scss
@@ -0,0 +1,47 @@
+/*
+Copyright 2015, 2016 OpenMarket 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_RoomBreadcrumbs {
+ overflow-x: auto;
+ position: relative;
+ height: 32px;
+ margin: 8px;
+ margin-bottom: 0;
+
+ > div {
+ display: flex;
+ flex-direction: row;
+ position: absolute;
+ right: 0;
+ top: 0;
+ height: 32px;
+
+ > * {
+ margin-right: 4px;
+ }
+ }
+
+ &::after {
+ content: "";
+ position: absolute;
+ width: 15px;
+ top: 0;
+ left: 0;
+ height: 100%;
+ background: linear-gradient(to left, rgba(242,245,248,0), rgba(242,245,248,1));
+ }
+}
+
diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js
index bd49f8acd4..49956265f6 100644
--- a/src/components/structures/LeftPanel.js
+++ b/src/components/structures/LeftPanel.js
@@ -182,6 +182,7 @@ const LeftPanel = React.createClass({
render: function() {
const RoomList = sdk.getComponent('rooms.RoomList');
+ const RoomBreadcrumbs = sdk.getComponent('rooms.RoomBreadcrumbs');
const TagPanel = sdk.getComponent('structures.TagPanel');
const CustomRoomTagPanel = sdk.getComponent('structures.CustomRoomTagPanel');
const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton');
@@ -221,6 +222,7 @@ const LeftPanel = React.createClass({
{ tagPanelContainer }