From 3d507e98409af91c9b8cadec511f6e5d253bdbed Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Fri, 21 Apr 2017 00:05:52 +0200 Subject: [PATCH 01/34] (Room)?Avatar: Request 96x96 avatars on high DPI screens --- src/Avatar.js | 9 +++++---- src/components/views/avatars/RoomAvatar.js | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Avatar.js b/src/Avatar.js index 76f5e55ff0..cb5e6965e3 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -22,8 +22,8 @@ module.exports = { avatarUrlForMember: function(member, width, height, resizeMethod) { var url = member.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - width, - height, + window.devicePixelRatio > 1.2 ? 96 : width, + window.devicePixelRatio > 1.2 ? 96 : height, resizeMethod, false, false @@ -40,7 +40,9 @@ module.exports = { avatarUrlForUser: function(user, width, height, resizeMethod) { var url = ContentRepo.getHttpUriForMxc( MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl, - width, height, resizeMethod + window.devicePixelRatio > 1.2 ? 96 : width, + window.devicePixelRatio > 1.2 ? 96 : height, + resizeMethod ); if (!url || url.length === 0) { return null; @@ -57,4 +59,3 @@ module.exports = { return 'img/' + images[total % images.length] + '.png'; } }; - diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js index bfa7575b0c..7ed7bfa9fa 100644 --- a/src/components/views/avatars/RoomAvatar.js +++ b/src/components/views/avatars/RoomAvatar.js @@ -59,7 +59,9 @@ module.exports = React.createClass({ ContentRepo.getHttpUriForMxc( MatrixClientPeg.get().getHomeserverUrl(), props.oobData.avatarUrl, - props.width, props.height, props.resizeMethod + window.devicePixelRatio > 1.2 ? 96 : props.width, + window.devicePixelRatio > 1.2 ? 96 : props.height, + props.resizeMethod ), // highest priority this.getRoomAvatarUrl(props), this.getOneToOneAvatar(props), @@ -74,7 +76,9 @@ module.exports = React.createClass({ return props.room.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - props.width, props.height, props.resizeMethod, + window.devicePixelRatio > 1.2 ? 96 : props.width, + window.devicePixelRatio > 1.2 ? 96 : props.height, + props.resizeMethod, false ); }, @@ -103,14 +107,18 @@ module.exports = React.createClass({ } return theOtherGuy.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - props.width, props.height, props.resizeMethod, + window.devicePixelRatio > 1.2 ? 96 : props.width, + window.devicePixelRatio > 1.2 ? 96 : props.height, + props.resizeMethod, false ); } else if (userIds.length == 1) { return mlist[userIds[0]].getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - props.width, props.height, props.resizeMethod, - false + window.devicePixelRatio > 1.2 ? 96 : props.width, + window.devicePixelRatio > 1.2 ? 96 : props.height, + props.resizeMethod, + false ); } else { return null; From 0590ce7faf9680d9d720a43de786545e7da5e7e6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 23 Apr 2017 06:06:23 +0100 Subject: [PATCH 02/34] Conform damn you (mostly) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 53 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index 92770877b7..617135a2c8 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -15,11 +15,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -var MatrixClientPeg = require("./MatrixClientPeg"); -var PlatformPeg = require("./PlatformPeg"); -var TextForEvent = require('./TextForEvent'); -var Avatar = require('./Avatar'); -var dis = require("./dispatcher"); +import MatrixClientPeg from './MatrixClientPeg'; +import PlatformPeg from './PlatformPeg'; +import TextForEvent from './TextForEvent'; +import Avatar from './Avatar'; +import dis from './dispatcher'; /* * Dispatches: @@ -29,7 +29,7 @@ var dis = require("./dispatcher"); * } */ -var Notifier = { +const Notifier = { notifsByRoom: {}, notificationMessageForEvent: function(ev) { @@ -48,16 +48,16 @@ var Notifier = { return; } - var msg = this.notificationMessageForEvent(ev); + let msg = this.notificationMessageForEvent(ev); if (!msg) return; - var title; - if (!ev.sender || room.name == ev.sender.name) { + let title; + if (!ev.sender || room.name === ev.sender.name) { title = room.name; // notificationMessageForEvent includes sender, // but we already have the sender here if (ev.getContent().body) msg = ev.getContent().body; - } else if (ev.getType() == 'm.room.member') { + } else if (ev.getType() === 'm.room.member') { // context is all in the message here, we don't need // to display sender info title = room.name; @@ -68,7 +68,7 @@ var Notifier = { if (ev.getContent().body) msg = ev.getContent().body; } - var avatarUrl = ev.sender ? Avatar.avatarUrlForMember( + const avatarUrl = ev.sender ? Avatar.avatarUrlForMember( ev.sender, 40, 40, 'crop' ) : null; @@ -83,7 +83,7 @@ var Notifier = { }, _playAudioNotification: function(ev, room) { - var e = document.getElementById("messageAudio"); + const e = document.getElementById("messageAudio"); if (e) { e.load(); e.play(); @@ -95,7 +95,7 @@ var Notifier = { this.boundOnSyncStateChange = this.onSyncStateChange.bind(this); this.boundOnRoomReceipt = this.onRoomReceipt.bind(this); MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline); - MatrixClientPeg.get().on("Room.receipt", this.boundOnRoomReceipt); + MatrixClientPeg.get().on('Room.receipt', this.boundOnRoomReceipt); MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange); this.toolbarHidden = false; this.isSyncing = false; @@ -104,7 +104,7 @@ var Notifier = { stop: function() { if (MatrixClientPeg.get() && this.boundOnRoomTimeline) { MatrixClientPeg.get().removeListener('Room.timeline', this.boundOnRoomTimeline); - MatrixClientPeg.get().removeListener("Room.receipt", this.boundOnRoomReceipt); + MatrixClientPeg.get().removeListener('Room.receipt', this.boundOnRoomReceipt); MatrixClientPeg.get().removeListener('sync', this.boundOnSyncStateChange); } this.isSyncing = false; @@ -121,7 +121,7 @@ var Notifier = { // make sure that we persist the current setting audio_enabled setting // before changing anything if (global.localStorage) { - if(global.localStorage.getItem('audio_notifications_enabled') == null) { + if (global.localStorage.getItem('audio_notifications_enabled') === null) { this.setAudioEnabled(this.isEnabled()); } } @@ -141,7 +141,7 @@ var Notifier = { if (callback) callback(); dis.dispatch({ action: "notifier_enabled", - value: true + value: true, }); }); // clear the notifications_hidden flag, so that if notifications are @@ -152,7 +152,7 @@ var Notifier = { global.localStorage.setItem('notifications_enabled', 'false'); dis.dispatch({ action: "notifier_enabled", - value: false + value: false, }); } }, @@ -165,7 +165,7 @@ var Notifier = { if (!global.localStorage) return true; - var enabled = global.localStorage.getItem('notifications_enabled'); + const enabled = global.localStorage.getItem('notifications_enabled'); if (enabled === null) return true; return enabled === 'true'; }, @@ -173,12 +173,12 @@ var Notifier = { setAudioEnabled: function(enable) { if (!global.localStorage) return; global.localStorage.setItem('audio_notifications_enabled', - enable ? 'true' : 'false'); + enable ? 'true' : 'false'); }, isAudioEnabled: function(enable) { if (!global.localStorage) return true; - var enabled = global.localStorage.getItem( + const enabled = global.localStorage.getItem( 'audio_notifications_enabled'); // default to true if the popups are enabled if (enabled === null) return this.isEnabled(); @@ -192,7 +192,7 @@ var Notifier = { // this is nothing to do with notifier_enabled dis.dispatch({ action: "notifier_enabled", - value: this.isEnabled() + value: this.isEnabled(), }); // update the info to localStorage for persistent settings @@ -215,8 +215,7 @@ var Notifier = { onSyncStateChange: function(state) { if (state === "SYNCING") { this.isSyncing = true; - } - else if (state === "STOPPED" || state === "ERROR") { + } else if (state === "STOPPED" || state === "ERROR") { this.isSyncing = false; } }, @@ -225,10 +224,10 @@ var Notifier = { if (toStartOfTimeline) return; if (!room) return; if (!this.isSyncing) return; // don't alert for any messages initially - if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) return; + if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return; if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return; - var actions = MatrixClientPeg.get().getPushActionsForEvent(ev); + const actions = MatrixClientPeg.get().getPushActionsForEvent(ev); if (actions && actions.notify) { if (this.isEnabled()) { this._displayPopupNotification(ev, room); @@ -240,7 +239,7 @@ var Notifier = { }, onRoomReceipt: function(ev, room) { - if (room.getUnreadNotificationCount() == 0) { + if (room.getUnreadNotificationCount() === 0) { // ideally we would clear each notification when it was read, // but we have no way, given a read receipt, to know whether // the receipt comes before or after an event, so we can't @@ -255,7 +254,7 @@ var Notifier = { } delete this.notifsByRoom[room.roomId]; } - } + }, }; if (!global.mxNotifier) { From 5e8b43f3edc70cd8d73a920c9a231c0f681c43fb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 23 Apr 2017 06:16:25 +0100 Subject: [PATCH 03/34] if we're not granted, show an ErrorDialog with some text which needs changing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Notifier.js b/src/Notifier.js index 617135a2c8..fed2760732 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -20,6 +20,8 @@ import PlatformPeg from './PlatformPeg'; import TextForEvent from './TextForEvent'; import Avatar from './Avatar'; import dis from './dispatcher'; +import sdk from './index'; +import Modal from './Modal'; /* * Dispatches: @@ -131,6 +133,14 @@ const Notifier = { plaf.requestNotificationPermission().done((result) => { if (result !== 'granted') { // The permission request was dismissed or denied + const description = result === 'denied' + ? 'Your browser is not permitting this app to send you notifications.' + : 'It seems you didn\'t accept notifications when your browser asked'; + const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); + Modal.createDialog(ErrorDialog, { + title: 'Unable to enable Notifications', + description, + }); return; } From 74e92d6c235e629802184c86d0323587dec9f82f Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 24 Apr 2017 15:44:45 +0100 Subject: [PATCH 04/34] Remove DM-guessing code --- src/components/views/rooms/RoomList.js | 53 +++----------------------- 1 file changed, 6 insertions(+), 47 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 3810f7d4d6..5839b66d1c 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -97,7 +97,7 @@ module.exports = React.createClass({ if (this.props.selectedRoom) { constantTimeDispatcher.dispatch( "RoomTile.select", this.props.selectedRoom, {} - ); + ); } constantTimeDispatcher.dispatch( "RoomTile.select", nextProps.selectedRoom, { selected: true } @@ -265,7 +265,7 @@ module.exports = React.createClass({ }, onRoomStateMember: function(ev, state, member) { - if (ev.getStateKey() === MatrixClientPeg.get().credentials.userId && + if (ev.getStateKey() === MatrixClientPeg.get().credentials.userId && ev.getPrevContent() && ev.getPrevContent().membership === "invite") { this._delayedRefreshRoomList(); @@ -290,7 +290,7 @@ module.exports = React.createClass({ this._delayedRefreshRoomList(); } else if (ev.getType() == 'm.push_rules') { - this._delayedRefreshRoomList(); + this._delayedRefreshRoomList(); } }, @@ -318,7 +318,7 @@ module.exports = React.createClass({ // as needed. // Alternatively we'd do something magical with Immutable.js or similar. this.setState(this.getRoomLists()); - + // this._lastRefreshRoomListTs = Date.now(); }, @@ -341,7 +341,7 @@ module.exports = React.createClass({ MatrixClientPeg.get().getRooms().forEach(function(room) { const me = room.getMember(MatrixClientPeg.get().credentials.userId); if (!me) return; - + // console.log("room = " + room.name + ", me.membership = " + me.membership + // ", sender = " + me.events.member.getSender() + // ", target = " + me.events.member.getStateKey() + @@ -391,51 +391,10 @@ module.exports = React.createClass({ } }); - if (s.lists["im.vector.fake.direct"].length == 0 && - MatrixClientPeg.get().getAccountData('m.direct') === undefined && - !MatrixClientPeg.get().isGuest()) - { - // scan through the 'recents' list for any rooms which look like DM rooms - // and make them DM rooms - const oldRecents = s.lists["im.vector.fake.recent"]; - s.lists["im.vector.fake.recent"] = []; - - for (const room of oldRecents) { - const me = room.getMember(MatrixClientPeg.get().credentials.userId); - - if (me && Rooms.looksLikeDirectMessageRoom(room, me)) { - self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); - s.lists["im.vector.fake.direct"].push(room); - } else { - self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); - s.lists["im.vector.fake.recent"].push(room); - } - } - - // save these new guessed DM rooms into the account data - const newMDirectEvent = {}; - for (const room of s.lists["im.vector.fake.direct"]) { - const me = room.getMember(MatrixClientPeg.get().credentials.userId); - const otherPerson = Rooms.getOnlyOtherMember(room, me); - if (!otherPerson) continue; - - const roomList = newMDirectEvent[otherPerson.userId] || []; - roomList.push(room.roomId); - newMDirectEvent[otherPerson.userId] = roomList; - } - - console.warn("Resetting room DM state to be " + JSON.stringify(newMDirectEvent)); - - // if this fails, fine, we'll just do the same thing next time we get the room lists - MatrixClientPeg.get().setAccountData('m.direct', newMDirectEvent).done(); - } - - //console.log("calculated new roomLists; im.vector.fake.recent = " + s.lists["im.vector.fake.recent"]); - // we actually apply the sorting to this when receiving the prop in RoomSubLists. // we'll need this when we get to iterating through lists programatically - e.g. ctrl-shift-up/down -/* +/* this.listOrder = [ "im.vector.fake.invite", "m.favourite", From 8291b36b3efa555713d90bc5c496187ab4d2eeed Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 27 Apr 2017 10:16:24 +0100 Subject: [PATCH 05/34] Use document.onkeydown instead of onkeypress --- src/UserActivity.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UserActivity.js b/src/UserActivity.js index e7338e17e9..1ae272f5df 100644 --- a/src/UserActivity.js +++ b/src/UserActivity.js @@ -32,7 +32,7 @@ class UserActivity { start() { document.onmousedown = this._onUserActivity.bind(this); document.onmousemove = this._onUserActivity.bind(this); - document.onkeypress = this._onUserActivity.bind(this); + document.onkeydown = this._onUserActivity.bind(this); // can't use document.scroll here because that's only the document // itself being scrolled. Need to use addEventListener's useCapture. // also this needs to be the wheel event, not scroll, as scroll is @@ -50,7 +50,7 @@ class UserActivity { stop() { document.onmousedown = undefined; document.onmousemove = undefined; - document.onkeypress = undefined; + document.onkeydown = undefined; window.removeEventListener('wheel', this._onUserActivity.bind(this), { passive: true, capture: true }); } From 39de37b547bde50e726ee9a22863bde1585ebe28 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Thu, 27 Apr 2017 12:38:03 +0200 Subject: [PATCH 06/34] *Avatar: Calculate thumbnail dimensions based on devicePixelRatio Multiply the dimension by the device pixel ratio and then round it to get the width/height of the thumbnail to use in the request. --- src/Avatar.js | 8 ++++---- src/components/views/avatars/RoomAvatar.js | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Avatar.js b/src/Avatar.js index cb5e6965e3..c0127d49af 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -22,8 +22,8 @@ module.exports = { avatarUrlForMember: function(member, width, height, resizeMethod) { var url = member.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - window.devicePixelRatio > 1.2 ? 96 : width, - window.devicePixelRatio > 1.2 ? 96 : height, + Math.floor(width * window.devicePixelRatio), + Math.floor(height * window.devicePixelRatio), resizeMethod, false, false @@ -40,8 +40,8 @@ module.exports = { avatarUrlForUser: function(user, width, height, resizeMethod) { var url = ContentRepo.getHttpUriForMxc( MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl, - window.devicePixelRatio > 1.2 ? 96 : width, - window.devicePixelRatio > 1.2 ? 96 : height, + Math.floor(width * window.devicePixelRatio), + Math.floor(height * window.devicePixelRatio), resizeMethod ); if (!url || url.length === 0) { diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js index 7ed7bfa9fa..8041fd5cd7 100644 --- a/src/components/views/avatars/RoomAvatar.js +++ b/src/components/views/avatars/RoomAvatar.js @@ -59,8 +59,8 @@ module.exports = React.createClass({ ContentRepo.getHttpUriForMxc( MatrixClientPeg.get().getHomeserverUrl(), props.oobData.avatarUrl, - window.devicePixelRatio > 1.2 ? 96 : props.width, - window.devicePixelRatio > 1.2 ? 96 : props.height, + Math.floor(props.width * window.devicePixelRatio), + Math.floor(props.height * window.devicePixelRatio), props.resizeMethod ), // highest priority this.getRoomAvatarUrl(props), @@ -76,8 +76,8 @@ module.exports = React.createClass({ return props.room.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - window.devicePixelRatio > 1.2 ? 96 : props.width, - window.devicePixelRatio > 1.2 ? 96 : props.height, + Math.floor(props.width * window.devicePixelRatio), + Math.floor(props.height * window.devicePixelRatio), props.resizeMethod, false ); @@ -107,16 +107,16 @@ module.exports = React.createClass({ } return theOtherGuy.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - window.devicePixelRatio > 1.2 ? 96 : props.width, - window.devicePixelRatio > 1.2 ? 96 : props.height, + Math.floor(props.width * window.devicePixelRatio), + Math.floor(props.height * window.devicePixelRatio), props.resizeMethod, false ); } else if (userIds.length == 1) { return mlist[userIds[0]].getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), - window.devicePixelRatio > 1.2 ? 96 : props.width, - window.devicePixelRatio > 1.2 ? 96 : props.height, + Math.floor(props.width * window.devicePixelRatio), + Math.floor(props.height * window.devicePixelRatio), props.resizeMethod, false ); From 8f7359fce1060fb6c63b11e3ef89fa50abfcfaf1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 27 Apr 2017 14:03:54 +0100 Subject: [PATCH 07/34] Only show jumpToReadMarker bar when RM !== RR If RM !== RR, use the pos. of the RM to determine whether it is visible, as before. --- src/components/structures/RoomView.js | 8 +------- src/components/structures/TimelinePanel.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 8a355a8f6d..a0c36374b6 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1276,13 +1276,7 @@ module.exports = React.createClass({ return; } - var pos = this.refs.messagePanel.getReadMarkerPosition(); - - // we want to show the bar if the read-marker is off the top of the - // screen. - // If pos is null, the event might not be paginated, so show the unread bar! - var showBar = pos < 0 || pos === null; - + const showBar = this.refs.messagePanel.canJumpToReadMarker(); if (this.state.showTopUnreadMessagesBar != showBar) { this.setState({showTopUnreadMessagesBar: showBar}, this.onChildResize); diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index f07bad0052..066ab37107 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -766,6 +766,16 @@ var TimelinePanel = React.createClass({ return null; }, + canJumpToReadMarker: function() { + // Only show jump bar if RR !== RM. If they are the same, there are only fully + // read messages and unread messages. We already have a badge count and the bottom + // bar to jump to "live" when we have unread messages. + // We want to show the bar if the read-marker is off the top of the screen. + // Also, if pos === null, the event might not be paginated - show the unread bar + const pos = this.getReadMarkerPosition(); + return this.state.readMarkerEventId !== this._getCurrentReadReceipt() && (pos < 0 || pos === null); + }, + /** * called by the parent component when PageUp/Down/etc is pressed. * From 2012513c0e0cb560089302d74707a4cb69068b0e Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 27 Apr 2017 14:16:50 +0100 Subject: [PATCH 08/34] Allow MELS to have its own RM This inserts a RM if any of the events in a MELS if the RM is tracking an event within it. Fixes https://github.com/vector-im/riot-web/issues/3749 --- src/components/structures/MessagePanel.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 87f444d607..246f351841 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -282,15 +282,16 @@ module.exports = React.createClass({ var isMembershipChange = (e) => e.getType() === 'm.room.member'; for (i = 0; i < this.props.events.length; i++) { - var mxEv = this.props.events[i]; - var wantTile = true; - var eventId = mxEv.getId(); + let mxEv = this.props.events[i]; + let wantTile = true; + let eventId = mxEv.getId(); + let readMarkerInMels = false; if (!EventTile.haveTileForEvent(mxEv)) { wantTile = false; } - var last = (i == lastShownEventIndex); + let last = (i == lastShownEventIndex); // Wrap consecutive member events in a ListSummary, ignore if redacted if (isMembershipChange(mxEv) && @@ -332,6 +333,9 @@ module.exports = React.createClass({ let eventTiles = summarisedEvents.map( (e) => { + if (e.getId() === this.props.readMarkerEventId) { + readMarkerInMels = true; + } // In order to prevent DateSeparators from appearing in the expanded form // of MemberEventListSummary, render each member event as if the previous // one was itself. This way, the timestamp of the previous event === the @@ -356,6 +360,11 @@ module.exports = React.createClass({ {eventTiles} ); + + if (readMarkerInMels) { + ret.push(this._getReadMarkerTile(visible)); + } + continue; } From 50831796bbf454abb56c209ae3ea1786fdfb9175 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 27 Apr 2017 16:49:23 +0100 Subject: [PATCH 09/34] Remove RHS when viewing RoomDirectory --- src/components/structures/LoggedInView.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 9f01b0082b..c4eeb03d5f 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -221,10 +221,8 @@ export default React.createClass({ case PageTypes.RoomDirectory: page_element = ; - if (!this.props.collapse_rhs) right_panel = ; break; case PageTypes.HomePage: From 6313193aa80ae2dd311d13dfc85b9808b953a434 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 27 Apr 2017 16:52:40 +0100 Subject: [PATCH 10/34] Null check readMarkerEventId, update comment --- src/components/structures/TimelinePanel.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 066ab37107..f72d35c41c 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -767,13 +767,16 @@ var TimelinePanel = React.createClass({ }, canJumpToReadMarker: function() { - // Only show jump bar if RR !== RM. If they are the same, there are only fully + // 1. Do not show jump bar if neither the RM nor the RR are set. + // 2. Only show jump bar if RR !== RM. If they are the same, there are only fully // read messages and unread messages. We already have a badge count and the bottom // bar to jump to "live" when we have unread messages. - // We want to show the bar if the read-marker is off the top of the screen. - // Also, if pos === null, the event might not be paginated - show the unread bar + // 3. We want to show the bar if the read-marker is off the top of the screen. + // 4. Also, if pos === null, the event might not be paginated - show the unread bar const pos = this.getReadMarkerPosition(); - return this.state.readMarkerEventId !== this._getCurrentReadReceipt() && (pos < 0 || pos === null); + return this.state.readMarkerEventId !== null && // 1. + this.state.readMarkerEventId !== this._getCurrentReadReceipt() && // 2. + (pos < 0 || pos === null); // 3., 4. }, /** From 0b06e979cb1bd0cd3ea3174379782cb6d41f5d77 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 27 Apr 2017 17:57:13 +0100 Subject: [PATCH 11/34] Add optional tintable SVG icon for SimpleRoomHeader --- src/components/views/rooms/SimpleRoomHeader.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/SimpleRoomHeader.js b/src/components/views/rooms/SimpleRoomHeader.js index 40995d2a72..1559f7ef85 100644 --- a/src/components/views/rooms/SimpleRoomHeader.js +++ b/src/components/views/rooms/SimpleRoomHeader.js @@ -19,6 +19,7 @@ limitations under the License. import React from 'react'; import dis from '../../../dispatcher'; import AccessibleButton from '../elements/AccessibleButton'; +import sdk from '../../../index'; // cancel button which is shared between room header and simple room header export function CancelButton(props) { @@ -43,8 +44,8 @@ export default React.createClass({ title: React.PropTypes.string, onCancelClick: React.PropTypes.func, - // is the RightPanel collapsed? - collapsedRhs: React.PropTypes.bool, + // `src` to a TintableSvg. Optional. + icon: React.PropTypes.string, }, onShowRhsClick: function(ev) { @@ -53,9 +54,17 @@ export default React.createClass({ render: function() { let cancelButton; + let icon; if (this.props.onCancelClick) { cancelButton = ; } + if (this.props.icon) { + const TintableSvg = sdk.getComponent('elements.TintableSvg'); + icon = ; + } let showRhsButton; /* // don't bother cluttering things up with this for now. @@ -73,6 +82,7 @@ export default React.createClass({
+ { icon } { this.props.title } { showRhsButton } { cancelButton } From 88abe54ffe1a1e55ad0f43910e61a40fb711e204 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 27 Apr 2017 17:58:02 +0100 Subject: [PATCH 12/34] Give the SRH its prop back --- src/components/views/rooms/SimpleRoomHeader.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/views/rooms/SimpleRoomHeader.js b/src/components/views/rooms/SimpleRoomHeader.js index 1559f7ef85..a6f342af86 100644 --- a/src/components/views/rooms/SimpleRoomHeader.js +++ b/src/components/views/rooms/SimpleRoomHeader.js @@ -44,6 +44,9 @@ export default React.createClass({ title: React.PropTypes.string, onCancelClick: React.PropTypes.func, + // is the RightPanel collapsed? + collapsedRhs: React.PropTypes.bool, + // `src` to a TintableSvg. Optional. icon: React.PropTypes.string, }, From 19482d751d82c162321d99c5bf95ac8fe950768b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 28 Apr 2017 00:52:31 +0100 Subject: [PATCH 13/34] comment out spammy CTD logging --- src/ConstantTimeDispatcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ConstantTimeDispatcher.js b/src/ConstantTimeDispatcher.js index 265ee11fd4..6c2c3266aa 100644 --- a/src/ConstantTimeDispatcher.js +++ b/src/ConstantTimeDispatcher.js @@ -47,7 +47,7 @@ class ConstantTimeDispatcher { dispatch(type, arg, params) { if (!this.listeners[type] || !this.listeners[type][arg]) { - console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")"); + //console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")"); return; } this.listeners[type][arg].forEach(listener=>{ From c9c72036f38e500a88f5b9e0b7b57808a583f0ab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@googlemail.com> Date: Fri, 28 Apr 2017 01:00:10 +0100 Subject: [PATCH 14/34] Change wording Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index fed2760732..f68b7e562c 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -134,8 +134,10 @@ const Notifier = { if (result !== 'granted') { // The permission request was dismissed or denied const description = result === 'denied' - ? 'Your browser is not permitting this app to send you notifications.' - : 'It seems you didn\'t accept notifications when your browser asked'; + ? 'Riot does not have permission to send you notifications' + + ' - please check your browser settings' + : 'Riot was not given permission to send notifications' + + '- please try again'; const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createDialog(ErrorDialog, { title: 'Unable to enable Notifications', From 47827e0b81c8db6977d2a628451daf9db348c5b5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@googlemail.com> Date: Fri, 28 Apr 2017 01:00:50 +0100 Subject: [PATCH 15/34] un-eat the space Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Notifier.js b/src/Notifier.js index f68b7e562c..6473ab4d9c 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -137,7 +137,7 @@ const Notifier = { ? 'Riot does not have permission to send you notifications' + ' - please check your browser settings' : 'Riot was not given permission to send notifications' - + '- please try again'; + + ' - please try again'; const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createDialog(ErrorDialog, { title: 'Unable to enable Notifications', From 86a5ff42e952c52218f68937b83ac179fe290b83 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 29 Apr 2017 14:22:06 +0100 Subject: [PATCH 16/34] Change max-len 90->120 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 6cd0e1015e..74790a2964 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -64,7 +64,7 @@ module.exports = { // to JSX. ignorePattern: '^\\s*<', ignoreComments: true, - code: 90, + code: 120, }], "valid-jsdoc": ["warn"], "new-cap": ["warn"], From c8fb18dc932bdfe6e64bce328b515a52d6c32607 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@googlemail.com> Date: Sun, 30 Apr 2017 13:00:47 +0100 Subject: [PATCH 17/34] Pin filesize ver to fix break upstream https://travis-ci.org/vector-im/riot-web/builds/227340622 https://github.com/avoidwork/filesize.js/issues/87 3.5.7 and 3.5.8 ver released <24h ago and broke stuff for us --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c96a74f5b..95a82bbb73 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "draft-js-export-markdown": "^0.2.0", "emojione": "2.2.3", "file-saver": "^1.3.3", - "filesize": "^3.1.2", + "filesize": "3.5.6", "flux": "^2.0.3", "fuse.js": "^2.2.0", "glob": "^5.0.14", From 3f25928380b36834e0cbe249938581ede9dc3c6e Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 2 May 2017 16:34:39 +0100 Subject: [PATCH 18/34] Fix jumping to an unread event when in MELS This adds the `data-contained-scroll-tokens` API to elements in `ScrollPanel` which allows arbitrary containers of elements with scroll tokens to declare their contained scroll tokens. When jumping to a scroll token inside a container, the `ScrollPanel` will act as if it is scrolling to the container itself, not the child. MELS has been modified such that it exposes the scroll tokens of all events that exist within it.This means "Jump to unread message" will work if the unread event is within a MELS (which is any member event, because even individual member events surrounded by other events are put inside a MELS). --- src/components/structures/MessagePanel.js | 1 - src/components/structures/ScrollPanel.js | 5 +++++ src/components/views/elements/MemberEventListSummary.js | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 246f351841..74aec61511 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -354,7 +354,6 @@ module.exports = React.createClass({ {eventTiles} diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index d43e22e2f1..da3b303d89 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -551,6 +551,11 @@ module.exports = React.createClass({ var messages = this.refs.itemlist.children; for (var i = messages.length-1; i >= 0; --i) { var m = messages[i]; + if (m.dataset.containedScrollTokens && + m.dataset.containedScrollTokens.indexOf(scrollToken) !== -1) { + node = m; + break; + } if (!m.dataset.scrollToken) continue; if (m.dataset.scrollToken == scrollToken) { node = m; diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 63bd2a7c39..a24e19577d 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -369,6 +369,7 @@ module.exports = React.createClass({ render: function() { const eventsToRender = this.props.events; + const eventIds = eventsToRender.map(e => e.getId()); const fewEvents = eventsToRender.length < this.props.threshold; const expanded = this.state.expanded || fewEvents; @@ -379,7 +380,7 @@ module.exports = React.createClass({ if (fewEvents) { return ( -
+
{expandedEvents}
); @@ -437,7 +438,7 @@ module.exports = React.createClass({ ); return ( -
+
{toggleButton} {summaryContainer} {expanded ?
 
: null} From fe83a99ab709121d2259b25d1a735babb2a022ee Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 2 May 2017 17:36:59 +0100 Subject: [PATCH 19/34] Update ScrollPanel docs --- src/components/structures/ScrollPanel.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index da3b303d89..7fa320d784 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -50,6 +50,10 @@ if (DEBUG_SCROLL) { * serialise the scroll state, and returned as the 'trackedScrollToken' * attribute by getScrollState(). * + * Child elements that contain elements that have scroll tokens must declare the + * contained scroll tokens using 'data-contained-scroll-tokens`. When scrolling + * to a contained scroll token, the ScrollPanel will scroll to the container. + * * Some notes about the implementation: * * The saved 'scrollState' can exist in one of two states: From 4febc63aeecee7613681749b3aa435cbd25ea17f Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 2 May 2017 17:41:09 +0100 Subject: [PATCH 20/34] Add comment to _scrollToToken --- src/components/structures/ScrollPanel.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 7fa320d784..3f36fac89b 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -555,6 +555,9 @@ module.exports = React.createClass({ var messages = this.refs.itemlist.children; for (var i = messages.length-1; i >= 0; --i) { var m = messages[i]; + // 'data-contained-scroll-tokens' has been set, indicating that a child + // element contains elements that each have a token. Check this array of + // tokens for `scrollToken`. if (m.dataset.containedScrollTokens && m.dataset.containedScrollTokens.indexOf(scrollToken) !== -1) { node = m; From af137f8867dce643232c8edfa92294b8f87b3b89 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 2 May 2017 18:30:46 +0100 Subject: [PATCH 21/34] Validate phone number on login To prevent confusion when accidently inputting mxid or email. Fixes https://github.com/vector-im/riot-web/issues/3637 --- src/components/structures/login/Login.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index 315a0ea242..a3635177e2 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -23,6 +23,9 @@ import url from 'url'; import sdk from '../../../index'; import Login from '../../../Login'; +// For validating phone numbers without country codes +const PHONE_NUMBER_REGEX = /^[0-9\(\)\-\s]*$/; + /** * A wire component which glues together login UI components and Login logic */ @@ -125,7 +128,16 @@ module.exports = React.createClass({ }, onPhoneNumberChanged: function(phoneNumber) { - this.setState({ phoneNumber: phoneNumber }); + // Validate the phone number entered + if (!PHONE_NUMBER_REGEX.test(phoneNumber)) { + this.setState({ errorText: 'The phone number entered looks invalid' }); + return; + } + + this.setState({ + phoneNumber: phoneNumber, + errorText: null, + }); }, onServerConfigChange: function(config) { From 76e98d42679e5a1167ebad419ec66abfd70f7a3c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 2 May 2017 21:12:58 +0100 Subject: [PATCH 22/34] improve version hyperlinking removed redundant v prefix (key already says version) links to most applicable version/tag tag-commit -> commit commit1-commit2-commit3 -> commit1 (v)x.y.z -> tag commit -> commit Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index ba5d5780b4..88e6829514 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -32,13 +32,22 @@ import AccessibleButton from '../views/elements/AccessibleButton'; // if this looks like a release, use the 'version' from package.json; else use // the git sha. Prepend version with v, to look like riot-web version -const REACT_SDK_VERSION = 'dist' in package_json ? `v${package_json.version}` : package_json.gitHead || ''; +const REACT_SDK_VERSION = 'dist' in package_json ? package_json.version : package_json.gitHead || ''; // Simple method to help prettify GH Release Tags and Commit Hashes. -const GHVersionUrl = function(repo, token) { - const uriTail = (token.startsWith('v') && token.includes('.')) ? `releases/tag/${token}` : `commit/${token}`; - return `https://github.com/${repo}/${uriTail}`; -} +const semVerRegex = /^v?(\d+\.\d+\.\d+)(?:-(?:\d+-g)?(.+))?|$/i; +const gHVersionLabel = function(repo, token) { + const match = token.match(semVerRegex); + let url; // assume commit hash + if (match && match[1]) { // basic semVer string possibly with commit hash + url = (match.length > 1 && match[2]) + ? `https://github.com/${repo}/commit/${match[2]}` + : `https://github.com/${repo}/releases/tag/v${match[1]}`; + } else { + url = `https://github.com/${repo}/commit/${token.split('-')[0]}`; + } + return {token}; +}; // Enumerate some simple 'flip a bit' UI settings (if any). // 'id' gives the key name in the im.vector.web.settings account data event @@ -911,7 +920,7 @@ module.exports = React.createClass({ // we are using a version old version of olm. We assume the former. let olmVersionString = ""; if (olmVersion !== undefined) { - olmVersionString = `v${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`; + olmVersionString = `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`; } return ( @@ -995,11 +1004,11 @@ module.exports = React.createClass({
matrix-react-sdk version: {(REACT_SDK_VERSION !== '') - ? {REACT_SDK_VERSION} + ? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION) : REACT_SDK_VERSION }
riot-web version: {(this.state.vectorVersion !== null) - ? {this.state.vectorVersion} + ? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion) : 'unknown' }
olm version: {olmVersionString}
From 74b2c86f93db0692311750c2a1d38339a44eb72a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 2 May 2017 21:17:12 +0100 Subject: [PATCH 23/34] tidy up UserSettings Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 177 +++++++++++----------- 1 file changed, 88 insertions(+), 89 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 88e6829514..4b63c8d888 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -14,25 +14,25 @@ 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. */ -var React = require('react'); -var ReactDOM = require('react-dom'); -var sdk = require('../../index'); -var MatrixClientPeg = require("../../MatrixClientPeg"); -var PlatformPeg = require("../../PlatformPeg"); -var Modal = require('../../Modal'); -var dis = require("../../dispatcher"); -var q = require('q'); -var package_json = require('../../../package.json'); -var UserSettingsStore = require('../../UserSettingsStore'); -var GeminiScrollbar = require('react-gemini-scrollbar'); -var Email = require('../../email'); -var AddThreepid = require('../../AddThreepid'); -var SdkConfig = require('../../SdkConfig'); +const React = require('react'); +const ReactDOM = require('react-dom'); +const sdk = require('../../index'); +const MatrixClientPeg = require("../../MatrixClientPeg"); +const PlatformPeg = require("../../PlatformPeg"); +const Modal = require('../../Modal'); +const dis = require("../../dispatcher"); +const q = require('q'); +const packageJson = require('../../../package.json'); +const UserSettingsStore = require('../../UserSettingsStore'); +const GeminiScrollbar = require('react-gemini-scrollbar'); +const Email = require('../../email'); +const AddThreepid = require('../../AddThreepid'); +const SdkConfig = require('../../SdkConfig'); import AccessibleButton from '../views/elements/AccessibleButton'; // if this looks like a release, use the 'version' from package.json; else use // the git sha. Prepend version with v, to look like riot-web version -const REACT_SDK_VERSION = 'dist' in package_json ? package_json.version : package_json.gitHead || ''; +const REACT_SDK_VERSION = 'dist' in packageJson ? packageJson.version : packageJson.gitHead || ''; // Simple method to help prettify GH Release Tags and Commit Hashes. const semVerRegex = /^v?(\d+\.\d+\.\d+)(?:-(?:\d+-g)?(.+))?|$/i; @@ -59,7 +59,7 @@ const SETTINGS_LABELS = [ }, { id: 'hideReadReceipts', - label: 'Hide read receipts' + label: 'Hide read receipts', }, { id: 'dontSendTypingNotifications', @@ -115,7 +115,7 @@ const THEMES = [ id: 'theme', label: 'Dark theme', value: 'dark', - } + }, ]; @@ -189,7 +189,7 @@ module.exports = React.createClass({ }); this._refreshFromServer(); - var syncedSettings = UserSettingsStore.getSyncedSettings(); + const syncedSettings = UserSettingsStore.getSyncedSettings(); if (!syncedSettings.theme) { syncedSettings.theme = 'light'; } @@ -211,16 +211,16 @@ module.exports = React.createClass({ middleOpacity: 1.0, }); dis.unregister(this.dispatcherRef); - let cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.get(); if (cli) { cli.removeListener("RoomMember.membership", this._onInviteStateChange); } }, _refreshFromServer: function() { - var self = this; + const self = this; q.all([ - UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids() + UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(), ]).done(function(resps) { self.setState({ avatarUrl: resps[0].avatar_url, @@ -228,7 +228,7 @@ module.exports = React.createClass({ phase: "UserSettings.DISPLAY", }); }, function(error) { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to load user settings: " + error); Modal.createDialog(ErrorDialog, { title: "Can't load user settings", @@ -245,7 +245,7 @@ module.exports = React.createClass({ onAvatarPickerClick: function(ev) { if (MatrixClientPeg.get().isGuest()) { - var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog"); + const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog"); Modal.createDialog(NeedToRegisterDialog, { title: "Please Register", description: "Guests can't set avatars. Please register.", @@ -259,8 +259,8 @@ module.exports = React.createClass({ }, onAvatarSelected: function(ev) { - var self = this; - var changeAvatar = this.refs.changeAvatar; + const self = this; + const changeAvatar = this.refs.changeAvatar; if (!changeAvatar) { console.error("No ChangeAvatar found to upload image to!"); return; @@ -269,9 +269,9 @@ module.exports = React.createClass({ // dunno if the avatar changed, re-check it. self._refreshFromServer(); }, function(err) { - var errMsg = (typeof err === "string") ? err : (err.error || ""); + // const errMsg = (typeof err === "string") ? err : (err.error || ""); console.error("Failed to set avatar: " + err); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to set avatar", description: ((err && err.message) ? err.message : "Operation failed"), @@ -280,7 +280,7 @@ module.exports = React.createClass({ }, onLogoutClicked: function(ev) { - var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); Modal.createDialog(QuestionDialog, { title: "Sign out?", description: @@ -295,7 +295,7 @@ module.exports = React.createClass({ + , ], onFinished: (confirmed) => { if (confirmed) { @@ -309,34 +309,33 @@ module.exports = React.createClass({ }, onPasswordChangeError: function(err) { - var errMsg = err.error || ""; + let errMsg = err.error || ""; if (err.httpStatus === 403) { errMsg = "Failed to change password. Is your password correct?"; - } - else if (err.httpStatus) { + } else if (err.httpStatus) { errMsg += ` (HTTP status ${err.httpStatus})`; } - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to change password: " + errMsg); Modal.createDialog(ErrorDialog, { title: "Error", - description: errMsg + description: errMsg, }); }, onPasswordChanged: function() { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Success", description: `Your password was successfully changed. You will not receive push notifications on other devices until you - log back in to them.` + log back in to them.`, }); }, onUpgradeClicked: function() { dis.dispatch({ - action: "start_upgrade_registration" + action: "start_upgrade_registration", }); }, @@ -350,11 +349,11 @@ module.exports = React.createClass({ }, _addEmail: function() { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - var email_address = this.refs.add_email_input.value; - if (!Email.looksValid(email_address)) { + const emailAddress = this.refs.add_email_input.value; + if (!Email.looksValid(emailAddress)) { Modal.createDialog(ErrorDialog, { title: "Invalid Email Address", description: "This doesn't appear to be a valid email address", @@ -364,7 +363,7 @@ module.exports = React.createClass({ this._addThreepid = new AddThreepid(); // we always bind emails when registering, so let's do the // same here. - this._addThreepid.addEmailAddress(email_address, true).done(() => { + this._addThreepid.addEmailAddress(emailAddress, true).done(() => { Modal.createDialog(QuestionDialog, { title: "Verification Pending", description: "Please check your email and click on the link it contains. Once this is done, click continue.", @@ -373,7 +372,7 @@ module.exports = React.createClass({ }); }, (err) => { this.setState({email_add_pending: false}); - console.error("Unable to add email address " + email_address + " " + err); + console.error("Unable to add email address " + emailAddress + " " + err); Modal.createDialog(ErrorDialog, { title: "Unable to add email address", description: ((err && err.message) ? err.message : "Operation failed"), @@ -427,9 +426,9 @@ module.exports = React.createClass({ this.setState({email_add_pending: false}); }, (err) => { this.setState({email_add_pending: false}); - if (err.errcode == 'M_THREEPID_AUTH_FAILED') { - var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - var message = "Unable to verify email address. "; + if (err.errcode === 'M_THREEPID_AUTH_FAILED') { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + let message = "Unable to verify email address. "; message += "Please check your email and click on the link it contains. Once this is done, click continue."; Modal.createDialog(QuestionDialog, { title: "Verification Pending", @@ -438,7 +437,7 @@ module.exports = React.createClass({ onFinished: this.onEmailDialogFinished, }); } else { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Unable to verify email address: " + err); Modal.createDialog(ErrorDialog, { title: "Unable to verify email address", @@ -478,17 +477,17 @@ module.exports = React.createClass({ _onRejectAllInvitesClicked: function(rooms, ev) { this.setState({ - rejectingInvites: true + rejectingInvites: true, }); // reject the invites - let promises = rooms.map((room) => { + const promises = rooms.map((room) => { return MatrixClientPeg.get().leave(room.roomId); }); // purposefully drop errors to the floor: we'll just have a non-zero number on the UI // after trying to reject all the invites. q.allSettled(promises).then(() => { this.setState({ - rejectingInvites: false + rejectingInvites: false, }); }).done(); }, @@ -501,7 +500,7 @@ module.exports = React.createClass({ }, "e2e-export"); }, { matrixClient: MatrixClientPeg.get(), - } + }, ); }, @@ -513,7 +512,7 @@ module.exports = React.createClass({ }, "e2e-export"); }, { matrixClient: MatrixClientPeg.get(), - } + }, ); }, @@ -539,7 +538,7 @@ module.exports = React.createClass({ }, _renderUserInterfaceSettings: function() { - var client = MatrixClientPeg.get(); + // const client = MatrixClientPeg.get(); return (
@@ -558,7 +557,7 @@ module.exports = React.createClass({ UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) } + onChange={ (e) => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) } />