From 488fa3745b567cdff57041b1f81352c484aef0ec Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 9 May 2017 10:03:40 +0100 Subject: [PATCH 01/69] Fix RM not updating if RR event unpaginated If the RR event has been unpaginated, the logic in `sendReadReceipt` will now fallback on the event ID of the RM which in theory is always =< RR event ID stream-wise. --- src/components/structures/ScrollPanel.js | 2 +- src/components/structures/TimelinePanel.js | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index a652bcc827..22eb2240ed 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -575,7 +575,7 @@ module.exports = React.createClass({ var boundingRect = node.getBoundingClientRect(); var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom; - debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" + + console.log("ScrollPanel: scrolling to token '" + scrollToken + "'+" + pixelOffset + " (delta: "+scrollDelta+")"); if(scrollDelta != 0) { diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 7c89694a29..d85282b5dc 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -510,8 +510,10 @@ var TimelinePanel = React.createClass({ var currentReadUpToEventId = this._getCurrentReadReceipt(true); var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); + currentReadUpToEventIndex = currentReadUpToEventIndex || + this._indexForEventId(this.state.readMarkerEventId); // We want to avoid sending out read receipts when we are looking at - // events in the past which are before the latest RR. + // events in the past which are before the latest RR/RM. // // For now, let's apply a heuristic: if (a) the event corresponding to // the latest RR (either from the server, or sent by ourselves) doesn't @@ -523,6 +525,7 @@ var TimelinePanel = React.createClass({ // RRs) - but that is a bit of a niche case. It will sort itself out when // the user eventually hits the live timeline. // + console.log(currentReadUpToEventId, currentReadUpToEventIndex, this._timelineWindow.canPaginate(EventTimeline.FORWARDS)); if (currentReadUpToEventId && currentReadUpToEventIndex === null && this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) { return; @@ -544,6 +547,11 @@ var TimelinePanel = React.createClass({ this.last_rr_sent_event_id = lastReadEvent.getId(); this.last_rm_sent_event_id = this.state.readMarkerEventId; + console.log('TimelinePanel: Sending Read Markers for ', + this.props.timelineSet.room.roomId, + 'rm', this.state.readMarkerEventId, + 'rr', lastReadEvent.getId(), + ); MatrixClientPeg.get().setRoomReadMarkers( this.props.timelineSet.room.roomId, this.state.readMarkerEventId, From ac25fd6d87214e962897667d7abdd8b766cef2b9 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 9 May 2017 10:16:37 +0100 Subject: [PATCH 02/69] Remove log --- src/components/structures/TimelinePanel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index d85282b5dc..ba233780ec 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -525,7 +525,6 @@ var TimelinePanel = React.createClass({ // RRs) - but that is a bit of a niche case. It will sort itself out when // the user eventually hits the live timeline. // - console.log(currentReadUpToEventId, currentReadUpToEventIndex, this._timelineWindow.canPaginate(EventTimeline.FORWARDS)); if (currentReadUpToEventId && currentReadUpToEventIndex === null && this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) { return; From ca79d9bb6eed092b7b6ca7c7f89ae345624f4047 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 9 May 2017 17:36:19 +0100 Subject: [PATCH 03/69] Separate predicates for RM/RR Instead of modifying the condition for updating the RR, separate the RM and RR conditions and use an OR to decide when to set both. Make some logs only log when DEBUG. --- src/components/structures/ScrollPanel.js | 2 +- src/components/structures/TimelinePanel.js | 27 ++++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 22eb2240ed..a652bcc827 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -575,7 +575,7 @@ module.exports = React.createClass({ var boundingRect = node.getBoundingClientRect(); var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom; - console.log("ScrollPanel: scrolling to token '" + scrollToken + "'+" + + debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" + pixelOffset + " (delta: "+scrollDelta+")"); if(scrollDelta != 0) { diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index ba233780ec..973c619904 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -504,16 +504,15 @@ var TimelinePanel = React.createClass({ // very possible have logged out within that timeframe, so check // we still have a client. const cli = MatrixClientPeg.get(); - // if no client or client is guest don't send RR + // if no client or client is guest don't send RR or RM if (!cli || cli.isGuest()) return; + let shouldSendRR = true; + var currentReadUpToEventId = this._getCurrentReadReceipt(true); var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); - - currentReadUpToEventIndex = currentReadUpToEventIndex || - this._indexForEventId(this.state.readMarkerEventId); // We want to avoid sending out read receipts when we are looking at - // events in the past which are before the latest RR/RM. + // events in the past which are before the latest RR. // // For now, let's apply a heuristic: if (a) the event corresponding to // the latest RR (either from the server, or sent by ourselves) doesn't @@ -527,26 +526,30 @@ var TimelinePanel = React.createClass({ // if (currentReadUpToEventId && currentReadUpToEventIndex === null && this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) { - return; + shouldSendRR = false; } var lastReadEventIndex = this._getLastDisplayedEventIndex({ ignoreOwn: true }); - if (lastReadEventIndex === null) return; + if (lastReadEventIndex === null) { + shouldSendRR = false; + } var lastReadEvent = this.state.events[lastReadEventIndex]; + shouldSendRR = shouldSendRR && + (lastReadEventIndex > currentReadUpToEventIndex && + this.last_rr_sent_event_id != lastReadEvent.getId()); + + const shouldSendRM = this.last_rm_sent_event_id != this.state.readMarkerEventId; // we also remember the last read receipt we sent to avoid spamming the // same one at the server repeatedly - if ((lastReadEventIndex > currentReadUpToEventIndex && - this.last_rr_sent_event_id != lastReadEvent.getId()) || - this.last_rm_sent_event_id != this.state.readMarkerEventId) { - + if (shouldSendRR || shouldSendRM) { this.last_rr_sent_event_id = lastReadEvent.getId(); this.last_rm_sent_event_id = this.state.readMarkerEventId; - console.log('TimelinePanel: Sending Read Markers for ', + debuglog('TimelinePanel: Sending Read Markers for ', this.props.timelineSet.room.roomId, 'rm', this.state.readMarkerEventId, 'rr', lastReadEvent.getId(), From 7f766d89c32feba745c3d3000c64e94be5431226 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 10 May 2017 14:42:06 +0100 Subject: [PATCH 04/69] Rename variables, more comments --- src/components/structures/TimelinePanel.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 973c619904..874c6b1ac0 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -507,7 +507,7 @@ var TimelinePanel = React.createClass({ // if no client or client is guest don't send RR or RM if (!cli || cli.isGuest()) return; - let shouldSendRR = true; + let shouldSendReadReceipt = true; var currentReadUpToEventId = this._getCurrentReadReceipt(true); var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); @@ -526,26 +526,30 @@ var TimelinePanel = React.createClass({ // if (currentReadUpToEventId && currentReadUpToEventIndex === null && this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) { - shouldSendRR = false; + shouldSendReadReceipt = false; } var lastReadEventIndex = this._getLastDisplayedEventIndex({ ignoreOwn: true }); if (lastReadEventIndex === null) { - shouldSendRR = false; + shouldSendReadReceipt = false; } var lastReadEvent = this.state.events[lastReadEventIndex]; - shouldSendRR = shouldSendRR && - (lastReadEventIndex > currentReadUpToEventIndex && - this.last_rr_sent_event_id != lastReadEvent.getId()); + shouldSendReadReceipt = shouldSendReadReceipt && + // Only send a RR if the last read Event is ahead in the timeline relative to + // the current RR event. + lastReadEventIndex > currentReadUpToEventIndex && + // Only send a RR if the last RR set != the one we would send + this.last_rr_sent_event_id != lastReadEvent.getId(); - const shouldSendRM = this.last_rm_sent_event_id != this.state.readMarkerEventId; + // Only send a RM if the last RM sent != the one we would send + const shouldSendReadMarker = this.last_rm_sent_event_id != this.state.readMarkerEventId; // we also remember the last read receipt we sent to avoid spamming the // same one at the server repeatedly - if (shouldSendRR || shouldSendRM) { + if (shouldSendReadReceipt || shouldSendReadMarker) { this.last_rr_sent_event_id = lastReadEvent.getId(); this.last_rm_sent_event_id = this.state.readMarkerEventId; From 30e183a7f1e0c5378d837f3ab45dfc8abbe6ade0 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 10 May 2017 14:48:01 +0100 Subject: [PATCH 05/69] Only send RR if we should --- src/components/structures/TimelinePanel.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 874c6b1ac0..26bf9dd400 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -550,23 +550,27 @@ var TimelinePanel = React.createClass({ // we also remember the last read receipt we sent to avoid spamming the // same one at the server repeatedly if (shouldSendReadReceipt || shouldSendReadMarker) { - this.last_rr_sent_event_id = lastReadEvent.getId(); + if (shouldSendReadReceipt) { + this.last_rr_sent_event_id = lastReadEvent.getId(); + } else { + lastReadEvent = null; + } this.last_rm_sent_event_id = this.state.readMarkerEventId; debuglog('TimelinePanel: Sending Read Markers for ', this.props.timelineSet.room.roomId, 'rm', this.state.readMarkerEventId, - 'rr', lastReadEvent.getId(), + lastReadEvent ? 'rr ' + lastReadEvent.getId() : '', ); MatrixClientPeg.get().setRoomReadMarkers( this.props.timelineSet.room.roomId, this.state.readMarkerEventId, - lastReadEvent + lastReadEvent, // Could be null, in which case no RR is sent ).catch((e) => { // /read_markers API is not implemented on this HS, fallback to just RR - if (e.errcode === 'M_UNRECOGNIZED') { + if (e.errcode === 'M_UNRECOGNIZED' && lastReadEvent) { return MatrixClientPeg.get().sendReadReceipt( - lastReadEvent + lastReadEvent, ).catch(() => { this.last_rr_sent_event_id = undefined; }); From fe8ea4ffe7dcc5844c52a3baf96563838cac8a90 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 10 May 2017 14:51:47 +0100 Subject: [PATCH 06/69] Rename vars, linting --- src/components/structures/TimelinePanel.js | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 26bf9dd400..8a6c80980e 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -177,8 +177,8 @@ var TimelinePanel = React.createClass({ componentWillMount: function() { debuglog("TimelinePanel: mounting"); - this.last_rr_sent_event_id = undefined; - this.last_rm_sent_event_id = undefined; + this.lastRRSentSentId = undefined; + this.lastRMSentEventId = undefined; this.dispatcherRef = dis.register(this.onAction); MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); @@ -509,8 +509,8 @@ var TimelinePanel = React.createClass({ let shouldSendReadReceipt = true; - var currentReadUpToEventId = this._getCurrentReadReceipt(true); - var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); + const currentReadUpToEventId = this._getCurrentReadReceipt(true); + const currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); // We want to avoid sending out read receipts when we are looking at // events in the past which are before the latest RR. // @@ -529,33 +529,33 @@ var TimelinePanel = React.createClass({ shouldSendReadReceipt = false; } - var lastReadEventIndex = this._getLastDisplayedEventIndex({ - ignoreOwn: true + const lastReadEventIndex = this._getLastDisplayedEventIndex({ + ignoreOwn: true, }); if (lastReadEventIndex === null) { shouldSendReadReceipt = false; } - - var lastReadEvent = this.state.events[lastReadEventIndex]; + let lastReadEvent = this.state.events[lastReadEventIndex]; shouldSendReadReceipt = shouldSendReadReceipt && // Only send a RR if the last read Event is ahead in the timeline relative to // the current RR event. lastReadEventIndex > currentReadUpToEventIndex && // Only send a RR if the last RR set != the one we would send - this.last_rr_sent_event_id != lastReadEvent.getId(); + this.lastRRSentSentId != lastReadEvent.getId(); // Only send a RM if the last RM sent != the one we would send - const shouldSendReadMarker = this.last_rm_sent_event_id != this.state.readMarkerEventId; + const shouldSendReadMarker = + this.lastRMSentEventId != this.state.readMarkerEventId; // we also remember the last read receipt we sent to avoid spamming the // same one at the server repeatedly if (shouldSendReadReceipt || shouldSendReadMarker) { if (shouldSendReadReceipt) { - this.last_rr_sent_event_id = lastReadEvent.getId(); + this.lastRRSentSentId = lastReadEvent.getId(); } else { lastReadEvent = null; } - this.last_rm_sent_event_id = this.state.readMarkerEventId; + this.lastRMSentEventId = this.state.readMarkerEventId; debuglog('TimelinePanel: Sending Read Markers for ', this.props.timelineSet.room.roomId, @@ -572,12 +572,12 @@ var TimelinePanel = React.createClass({ return MatrixClientPeg.get().sendReadReceipt( lastReadEvent, ).catch(() => { - this.last_rr_sent_event_id = undefined; + this.lastRRSentSentId = undefined; }); } // it failed, so allow retries next time the user is active - this.last_rr_sent_event_id = undefined; - this.last_rm_sent_event_id = undefined; + this.lastRRSentSentId = undefined; + this.lastRMSentEventId = undefined; }); // do a quick-reset of our unreadNotificationCount to avoid having From 856ef58d4678e4b243f4df94bdc20bb3b6e70efb Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 10 May 2017 14:55:58 +0100 Subject: [PATCH 07/69] fix commen --- src/components/structures/TimelinePanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 8a6c80980e..08b94fbdbf 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -537,7 +537,7 @@ var TimelinePanel = React.createClass({ } let lastReadEvent = this.state.events[lastReadEventIndex]; shouldSendReadReceipt = shouldSendReadReceipt && - // Only send a RR if the last read Event is ahead in the timeline relative to + // Only send a RR if the last read event is ahead in the timeline relative to // the current RR event. lastReadEventIndex > currentReadUpToEventIndex && // Only send a RR if the last RR set != the one we would send From 3815ad6cd031a9cd14081a18ba6c6c0448fcbc6c Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 11 May 2017 09:20:34 +0100 Subject: [PATCH 08/69] Sent -> Event --- src/components/structures/TimelinePanel.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 08b94fbdbf..5a5abc6257 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -177,7 +177,7 @@ var TimelinePanel = React.createClass({ componentWillMount: function() { debuglog("TimelinePanel: mounting"); - this.lastRRSentSentId = undefined; + this.lastRRSentEventId = undefined; this.lastRMSentEventId = undefined; this.dispatcherRef = dis.register(this.onAction); @@ -541,7 +541,7 @@ var TimelinePanel = React.createClass({ // the current RR event. lastReadEventIndex > currentReadUpToEventIndex && // Only send a RR if the last RR set != the one we would send - this.lastRRSentSentId != lastReadEvent.getId(); + this.lastRRSentEventId != lastReadEvent.getId(); // Only send a RM if the last RM sent != the one we would send const shouldSendReadMarker = @@ -551,7 +551,7 @@ var TimelinePanel = React.createClass({ // same one at the server repeatedly if (shouldSendReadReceipt || shouldSendReadMarker) { if (shouldSendReadReceipt) { - this.lastRRSentSentId = lastReadEvent.getId(); + this.lastRRSentEventId = lastReadEvent.getId(); } else { lastReadEvent = null; } @@ -572,11 +572,11 @@ var TimelinePanel = React.createClass({ return MatrixClientPeg.get().sendReadReceipt( lastReadEvent, ).catch(() => { - this.lastRRSentSentId = undefined; + this.lastRRSentEventId = undefined; }); } // it failed, so allow retries next time the user is active - this.lastRRSentSentId = undefined; + this.lastRRSentEventId = undefined; this.lastRMSentEventId = undefined; }); From 852e1eb72016d71c22b817515547c1d448bdc67a Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 11 May 2017 09:31:59 +0100 Subject: [PATCH 09/69] Rename some variables `ReadUpTo` -> `RR` `ReadReceipt` -> `RR` `ReadMarker` -> `RM` --- src/components/structures/TimelinePanel.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 5a5abc6257..de43bd1c19 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -507,10 +507,10 @@ var TimelinePanel = React.createClass({ // if no client or client is guest don't send RR or RM if (!cli || cli.isGuest()) return; - let shouldSendReadReceipt = true; + let shouldSendRR = true; - const currentReadUpToEventId = this._getCurrentReadReceipt(true); - const currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); + const currentRREventId = this._getCurrentReadReceipt(true); + const currentRREventIndex = this._indexForEventId(currentRREventId); // We want to avoid sending out read receipts when we are looking at // events in the past which are before the latest RR. // @@ -524,33 +524,33 @@ var TimelinePanel = React.createClass({ // RRs) - but that is a bit of a niche case. It will sort itself out when // the user eventually hits the live timeline. // - if (currentReadUpToEventId && currentReadUpToEventIndex === null && + if (currentRREventId && currentRREventIndex === null && this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) { - shouldSendReadReceipt = false; + shouldSendRR = false; } const lastReadEventIndex = this._getLastDisplayedEventIndex({ ignoreOwn: true, }); if (lastReadEventIndex === null) { - shouldSendReadReceipt = false; + shouldSendRR = false; } let lastReadEvent = this.state.events[lastReadEventIndex]; - shouldSendReadReceipt = shouldSendReadReceipt && + shouldSendRR = shouldSendRR && // Only send a RR if the last read event is ahead in the timeline relative to // the current RR event. - lastReadEventIndex > currentReadUpToEventIndex && + lastReadEventIndex > currentRREventIndex && // Only send a RR if the last RR set != the one we would send this.lastRRSentEventId != lastReadEvent.getId(); // Only send a RM if the last RM sent != the one we would send - const shouldSendReadMarker = + const shouldSendRM = this.lastRMSentEventId != this.state.readMarkerEventId; // we also remember the last read receipt we sent to avoid spamming the // same one at the server repeatedly - if (shouldSendReadReceipt || shouldSendReadMarker) { - if (shouldSendReadReceipt) { + if (shouldSendRR || shouldSendRM) { + if (shouldSendRR) { this.lastRRSentEventId = lastReadEvent.getId(); } else { lastReadEvent = null; From f55b27f43281e860ca92b37635a8207907b9134d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 11 May 2017 17:32:23 +0100 Subject: [PATCH 10/69] looks like it is passed and accessed like a string so unless I'm going insane, it should be a string. fixes ``` rageshake.js:61 Warning: Failed prop type: The prop `onClick` is marked as required in `AccessibleButton`, but its value is `undefined`. in AccessibleButton (created by RoomHeader) in RoomHeader (created by RoomView) in div (created by RoomView) in RoomView (created by LoggedInView) in main (created by LoggedInView) in div (created by LoggedInView) in div (created by LoggedInView) in LoggedInView (created by MatrixChat) in MatrixChat ``` Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/RoomPreviewBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 43c3b05295..5b7ec9347b 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -47,7 +47,7 @@ module.exports = React.createClass({ // The alias that was used to access this room, if appropriate // If given, this will be how the room is referred to (eg. // in error messages). - roomAlias: React.PropTypes.object, + roomAlias: React.PropTypes.string, }, getDefaultProps: function() { From 5e4467adced6ac7cd7eb8b20464efbeb0d8830db Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 11 May 2017 17:35:06 +0100 Subject: [PATCH 11/69] hide settings/search appropriately pass inRoom prop to RoomHeader (defaults to false) remove default onSettingsClick, handle if it is passed EVERYWHERE if onSettingsClick is passes, show that button show search button only if we are in the room, seems to fail otherwise this seems to handle all cases I could throw at it. Give it your best Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 1 + src/components/views/rooms/RoomHeader.js | 30 +++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 1b3ed6e80d..af0c595ea9 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1772,6 +1772,7 @@ module.exports = React.createClass({ oobData={this.props.oobData} editing={this.state.editingRoomSettings} saving={this.state.uploadingRoomSettings} + inRoom={myMember && myMember.membership === 'join'} collapsedRhs={ this.props.collapsedRhs } onSearchClick={this.onSearchClick} onSettingsClick={this.onSettingsClick} diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 94f2691f2c..5a7da47cbf 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -39,6 +39,7 @@ module.exports = React.createClass({ oobData: React.PropTypes.object, editing: React.PropTypes.bool, saving: React.PropTypes.bool, + inRoom: React.PropTypes.bool, collapsedRhs: React.PropTypes.bool, onSettingsClick: React.PropTypes.func, onSaveClick: React.PropTypes.func, @@ -49,7 +50,7 @@ module.exports = React.createClass({ getDefaultProps: function() { return { editing: false, - onSettingsClick: function() {}, + inRoom: false, onSaveClick: function() {}, }; }, @@ -225,12 +226,17 @@ module.exports = React.createClass({ roomName = this.props.room.name; } + const innerName = + {roomName}; + + if (this.props.onSettingsClick) { + name =
{ innerName }{ searchStatus }
; + } else { + name =
{ innerName }{ searchStatus }
; + } - name = -
- {roomName} - { searchStatus } -
; } if (can_set_room_topic) { @@ -299,6 +305,14 @@ module.exports = React.createClass({ ; } + let search_button; + if (this.props.onSearchClick && this.props.inRoom) { + search_button = + + + ; + } + var rightPanel_buttons; if (this.props.collapsedRhs) { rightPanel_buttons = @@ -313,9 +327,7 @@ module.exports = React.createClass({
{ settings_button } { forget_button } - - - + { search_button } { rightPanel_buttons }
; } From 69d860e98207df27eaaefdb1a53f9e82918f94bc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 12 May 2017 21:06:36 +0100 Subject: [PATCH 12/69] revert name overengineering, undefined onClick should be fine on div Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/RoomHeader.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 5a7da47cbf..c10228c334 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -226,17 +226,11 @@ module.exports = React.createClass({ roomName = this.props.room.name; } - const innerName = - {roomName}; - - if (this.props.onSettingsClick) { - name =
{ innerName }{ searchStatus }
; - } else { - name =
{ innerName }{ searchStatus }
; - } - + name = +
+ {roomName} + { searchStatus } +
; } if (can_set_room_topic) { From 822f2f10f28a724654d3cc574ecc4672d7f243fa Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 12 May 2017 21:16:55 +0100 Subject: [PATCH 13/69] conform to Luke's comment https://github.com/matrix-org/matrix-react-sdk/pull/880#discussion_r116257726 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/RoomHeader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index c10228c334..e9bb10c60c 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -228,7 +228,7 @@ module.exports = React.createClass({ name =
- {roomName} + { roomName } { searchStatus }
; } From 60b13d76a505fbc99940e72f425f21d7984bef1b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 12 May 2017 21:20:56 +0100 Subject: [PATCH 14/69] conform to Luke's other comment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/RoomHeader.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index e9bb10c60c..4cf20454a0 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; var React = require('react'); +var classNames = require('classnames'); var sdk = require('../../../index'); var MatrixClientPeg = require('../../../MatrixClientPeg'); var Modal = require("../../../Modal"); @@ -226,9 +227,10 @@ module.exports = React.createClass({ roomName = this.props.room.name; } + const emojiTextClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint }); name =
- { roomName } + { roomName } { searchStatus }
; } From 29568feb9519c2068e852037894451573aee645a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 12 May 2017 22:38:57 +0100 Subject: [PATCH 15/69] show error if we can't set a filter this way it still works for a room we've been in before Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/FilePanel.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js index fc4cbd9423..778a0cd6f3 100644 --- a/src/components/structures/FilePanel.js +++ b/src/components/structures/FilePanel.js @@ -59,6 +59,8 @@ var FilePanel = React.createClass({ var client = MatrixClientPeg.get(); var room = client.getRoom(roomId); + this.noRoom = !room; + if (room) { var filter = new Matrix.Filter(client.credentials.userId); filter.setDefinition( @@ -82,13 +84,18 @@ var FilePanel = React.createClass({ console.error("Failed to get or create file panel filter", error); } ); - } - else { + } else { console.error("Failed to add filtered timelineSet for FilePanel as no room!"); } }, render: function() { + if (this.noRoom) { + return
+
You must join the room to see its files
+
; + } + // wrap a TimelinePanel with the jump-to-event bits turned off. var TimelinePanel = sdk.getComponent("structures.TimelinePanel"); var Loader = sdk.getComponent("elements.Spinner"); From 6ec799a028dfff7af27e471df9c13f438ae4e7d0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 13 May 2017 15:04:20 +0100 Subject: [PATCH 16/69] I broke UserSettings for webpack-dev-server where version file doesn't exist, version starts as null then gets set to undefined by the promise this wasn't handled and now undefined is understood to be unknown rather than null also picked up on a small casing error threePids vs threepids, most things using the latter apart from the init Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 46dce8bd2e..1740f066d6 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -36,7 +36,7 @@ const REACT_SDK_VERSION = 'dist' in packageJson ? packageJson.version : packageJ // Simple method to help prettify GH Release Tags and Commit Hashes. const semVerRegex = /^v?(\d+\.\d+\.\d+(?:-rc.+)?)(?:-(?:\d+-g)?([0-9a-fA-F]+))?(?:-dirty)?$/i; -const gHVersionLabel = function(repo, token) { +const gHVersionLabel = function(repo, token='') { const match = token.match(semVerRegex); let url; if (match && match[1]) { // basic semVer string possibly with commit hash @@ -151,10 +151,10 @@ module.exports = React.createClass({ getInitialState: function() { return { avatarUrl: null, - threePids: [], + threepids: [], phase: "UserSettings.LOADING", // LOADING, DISPLAY email_add_pending: false, - vectorVersion: null, + vectorVersion: undefined, rejectingInvites: false, }; }, @@ -1004,7 +1004,7 @@ module.exports = React.createClass({ ? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION) : REACT_SDK_VERSION }
- riot-web version: {(this.state.vectorVersion !== null) + riot-web version: {(this.state.vectorVersion !== undefined) ? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion) : 'unknown' }
From d7c88a9813087db5df9360eac1c9f57eafa67246 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 13 May 2017 15:20:31 +0100 Subject: [PATCH 17/69] only removed `/me `, remove anyway to fix vector-im/riot-web#3733 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/MessageComposerInput.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 8efd2fa579..e2fcc19f67 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -542,9 +542,9 @@ export default class MessageComposerInput extends React.Component { let sendTextFn = this.client.sendTextMessage; if (contentText.startsWith('/me')) { - contentText = contentText.replace('/me ', ''); + contentText = contentText.substring(4); // bit of a hack, but the alternative would be quite complicated - if (contentHTML) contentHTML = contentHTML.replace('/me ', ''); + if (contentHTML) contentHTML = contentHTML.replace(/\/me ?/, ''); sendHtmlFn = this.client.sendHtmlEmote; sendTextFn = this.client.sendEmoteMessage; } From f3274426db369814b5c819aee6b46141fedb5584 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 00:25:12 +0100 Subject: [PATCH 18/69] revert f999aa9 and support full date formats when desired --- src/DateUtils.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/DateUtils.js b/src/DateUtils.js index 07bab4ae7b..c58c09d4de 100644 --- a/src/DateUtils.js +++ b/src/DateUtils.js @@ -19,13 +19,14 @@ limitations under the License. var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; +function pad(n) { + return (n < 10 ? '0' : '') + n; +} + module.exports = { formatDate: function(date) { // date.toLocaleTimeString is completely system dependent. // just go 24h for now - function pad(n) { - return (n < 10 ? '0' : '') + n; - } var now = new Date(); if (date.toDateString() === now.toDateString()) { @@ -34,19 +35,20 @@ module.exports = { else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) { return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); } - else /* if (now.getFullYear() === date.getFullYear()) */ { + else if (now.getFullYear() === date.getFullYear()) { return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); } - /* else { - return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); + return this.formatFullDate(date); } - */ + }, + + formatFullDate: function(date) { + return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); }, formatTime: function(date) { - //return pad(date.getHours()) + ':' + pad(date.getMinutes()); - return ('00' + date.getHours()).slice(-2) + ':' + ('00' + date.getMinutes()).slice(-2); + return pad(date.getHours()) + ':' + pad(date.getMinutes()); } }; From c0cead15465ee6c7bf0091d58b4c131491c240f1 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 01:32:37 +0100 Subject: [PATCH 19/69] workaround for https://github.com/vector-im/riot-web/issues/3633. unsure our vector url match could ever return undefined, but apparently it is... --- src/HtmlUtils.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index a31601790f..4acb314c2f 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -148,17 +148,18 @@ var sanitizeHtmlParams = { attribs.href = m[1]; delete attribs.target; } - - m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN); - if (m) { - var entity = m[1]; - if (entity[0] === '@') { - attribs.href = '#/user/' + entity; + else { + m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN); + if (m) { + var entity = m[1]; + if (entity[0] === '@') { + attribs.href = '#/user/' + entity; + } + else if (entity[0] === '#' || entity[0] === '!') { + attribs.href = '#/room/' + entity; + } + delete attribs.target; } - else if (entity[0] === '#' || entity[0] === '!') { - attribs.href = '#/room/' + entity; - } - delete attribs.target; } } attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/ From c5f2b69e48de2606ba1d420ee40086fb4445f3d6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 01:37:24 +0100 Subject: [PATCH 20/69] add alt attributes to e2e icons on msgs; fixes https://github.com/vector-im/riot-web/issues/3786 --- src/components/views/rooms/EventTile.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index d1486d22c9..e2e203f680 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -492,22 +492,22 @@ module.exports = WithMatrixClient(React.createClass({ var e2e; // cosmetic padlocks: if ((e2eEnabled && this.props.eventSendStatus) || this.props.mxEvent.getType() === 'm.room.encryption') { - e2e = ; + e2e = Encrypted by verified device; } // real padlocks else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) { if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') { - e2e = ; + e2e = Undecryptable; } else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) { - e2e = ; + e2e = Encrypted by verified device; } else { - e2e = ; + e2e = Encrypted by unverified device; } } else if (e2eEnabled) { - e2e = ; + e2e = Unencrypted message; } const timestamp = this.props.mxEvent.getTs() ? : null; From 48864b0880f5c98dc46b043902833b5295d19b78 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 01:39:57 +0100 Subject: [PATCH 21/69] fix visibility of topbar close on dark theme, fixing https://github.com/vector-im/riot-web/issues/3783 --- src/components/views/rooms/TopUnreadMessagesBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js index 72b489a406..74c689c395 100644 --- a/src/components/views/rooms/TopUnreadMessagesBar.js +++ b/src/components/views/rooms/TopUnreadMessagesBar.js @@ -38,7 +38,7 @@ module.exports = React.createClass({ title="Scroll to unread messages"/> Jump to first unread message. - Close From 82092dc2d81401b24af4dc60dd0b1bcec5f05a27 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 02:14:50 +0100 Subject: [PATCH 22/69] onClick MELS avatars = expand MELS; fixes https://github.com/vector-im/riot-web/issues/3899 --- src/components/views/elements/MemberEventListSummary.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index ae8678894d..dcf1810468 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -269,7 +269,7 @@ module.exports = React.createClass({ ); }); return ( - + {avatars} ); From 5c0c49e1f60a45f0f866ab19474d94cb1a446b0d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 02:18:18 +0100 Subject: [PATCH 23/69] ignore voip answer/hangup in unread events - fixes https://github.com/vector-im/riot-web/issues/3827 --- src/Unread.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Unread.js b/src/Unread.js index d7490c8632..67166dc24f 100644 --- a/src/Unread.js +++ b/src/Unread.js @@ -25,7 +25,9 @@ module.exports = { eventTriggersUnreadCount: function(ev) { if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) { return false; - } else if (ev.getType() == "m.room.member") { + } else if (ev.getType() == 'm.room.member') { + 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') { return false; From 6879f7ee6f8f2f1e943b1609dee12800b735d4e0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 02:43:23 +0100 Subject: [PATCH 24/69] add presence to MemberInfo, fixes https://github.com/vector-im/riot-web/issues/3720 --- src/components/views/rooms/MemberInfo.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 1a9a8d5e0f..839405c922 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -717,8 +717,16 @@ module.exports = WithMatrixClient(React.createClass({ const memberName = this.props.member.name; + if (this.props.member.user) { + var presenceState = this.props.member.user.presence; + var presenceLastActiveAgo = this.props.member.user.lastActiveAgo; + var presenceLastTs = this.props.member.user.lastPresenceTs; + var presenceCurrentlyActive = this.props.member.user.currentlyActive; + } + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var PowerSelector = sdk.getComponent('elements.PowerSelector'); + var PresenceLabel = sdk.getComponent('rooms.PresenceLabel'); const EmojiText = sdk.getComponent('elements.EmojiText'); return (
@@ -736,6 +744,11 @@ module.exports = WithMatrixClient(React.createClass({
Level:
+
+ +
{ adminTools } From 486301cffb55aee0a7cfb3df1df3ff5a8ad3c228 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 02:55:07 +0100 Subject: [PATCH 25/69] remove dodgy heuristics for hiding dates on RRs and use DateUtils instead. reverts https://github.com/matrix-org/matrix-react-sdk/pull/586/commits/5d99d68a64ec0c81b42c5a8e6959376dab798feb fixes https://github.com/vector-im/riot-web/issues/3523 --- src/components/views/rooms/EventTile.js | 11 ----------- src/components/views/rooms/ReadReceiptMarker.js | 17 ++++------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index e2e203f680..44c4051995 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -295,16 +295,6 @@ module.exports = WithMatrixClient(React.createClass({ const receiptOffset = 15; let left = 0; - // It's possible that the receipt was sent several days AFTER the event. - // If it is, we want to display the complete date along with the HH:MM:SS, - // rather than just HH:MM:SS. - let dayAfterEvent = new Date(this.props.mxEvent.getTs()); - dayAfterEvent.setDate(dayAfterEvent.getDate() + 1); - dayAfterEvent.setHours(0); - dayAfterEvent.setMinutes(0); - dayAfterEvent.setSeconds(0); - let dayAfterEventTime = dayAfterEvent.getTime(); - var receipts = this.props.readReceipts || []; for (var i = 0; i < receipts.length; ++i) { var receipt = receipts[i]; @@ -340,7 +330,6 @@ module.exports = WithMatrixClient(React.createClass({ suppressAnimation={this._suppressReadReceiptAnimation} onClick={this.toggleAllReadAvatars} timestamp={receipt.ts} - showFullTimestamp={receipt.ts >= dayAfterEventTime} /> ); } diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index 230efbd1ea..0911d50c3e 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -24,6 +24,8 @@ var sdk = require('../../../index'); var Velociraptor = require('../../../Velociraptor'); require('../../../VelocityBounce'); +import DateUtils from '../../../DateUtils'; + var bounce = false; try { if (global.localStorage) { @@ -63,9 +65,6 @@ module.exports = React.createClass({ // Timestamp when the receipt was read timestamp: React.PropTypes.number, - - // True to show the full date/time rather than just the time - showFullTimestamp: React.PropTypes.bool, }, getDefaultProps: function() { @@ -170,16 +169,8 @@ module.exports = React.createClass({ let title; if (this.props.timestamp) { - const prefix = "Seen by " + this.props.member.userId + " at "; - let ts = new Date(this.props.timestamp); - if (this.props.showFullTimestamp) { - // "15/12/2016, 7:05:45 PM (@alice:matrix.org)" - title = prefix + ts.toLocaleString(); - } - else { - // "7:05:45 PM (@alice:matrix.org)" - title = prefix + ts.toLocaleTimeString(); - } + title = "Seen by " + this.props.member.userId + " at " + + DateUtils.formatDate(new Date(this.props.timestamp)); } return ( From 317e24852dc7eaed7cc2a9a055a1139bab934801 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 May 2017 03:03:17 +0100 Subject: [PATCH 26/69] explicitly label email & phone add sections; fixes https://github.com/vector-im/riot-web/issues/3531 --- src/components/structures/UserSettings.js | 1 + src/components/views/settings/AddPhoneNumber.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 1740f066d6..2c1f17ee3e 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -855,6 +855,7 @@ module.exports = React.createClass({ addEmailSection = (
+
+
From 83cb1e6e29a639dc65e45254fdbd706d2d06c347 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 15 May 2017 10:15:35 +0100 Subject: [PATCH 27/69] tell guests they can't use filepanel until they register Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/FilePanel.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js index 778a0cd6f3..e7438afc4d 100644 --- a/src/components/structures/FilePanel.js +++ b/src/components/structures/FilePanel.js @@ -90,7 +90,11 @@ var FilePanel = React.createClass({ }, render: function() { - if (this.noRoom) { + if (MatrixClientPeg.get().isGuest()) { + return
+
You must register to use this functionality
+
; + } else if (this.noRoom) { return
You must join the room to see its files
; From 15201d86aa3b5246242293f0fd4bd9346073aad0 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 15 May 2017 10:16:47 +0100 Subject: [PATCH 28/69] Prevent reskindex -w from running when file names have not changed --- scripts/reskindex.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/reskindex.js b/scripts/reskindex.js index 1db22f9e10..9516614fa5 100755 --- a/scripts/reskindex.js +++ b/scripts/reskindex.js @@ -8,8 +8,15 @@ var chokidar = require('chokidar'); var componentIndex = path.join('src', 'component-index.js'); var componentsDir = path.join('src', 'components'); var componentGlob = '**/*.js'; +var prevFiles = []; function reskindex() { + var files = glob.sync(componentGlob, {cwd: componentsDir}).sort(); + if (!filesHaveChanged(files, prevFiles)) { + return; + } + prevFiles = files; + var header = args.h || args.header; var packageJson = JSON.parse(fs.readFileSync('./package.json')); @@ -37,7 +44,6 @@ function reskindex() { strm.write("module.exports.components = {};\n"); } - var files = glob.sync(componentGlob, {cwd: componentsDir}).sort(); for (var i = 0; i < files.length; ++i) { var file = files[i].replace('.js', ''); @@ -54,6 +60,20 @@ function reskindex() { console.log('Reskindex: completed'); } +// Expects both arrays of file names to be sorted +function filesHaveChanged(files, prevFiles) { + if (files.length !== prevFiles.length) { + return true; + } + // Check for name changes + for (var i = 0; i < files.length; i++) { + if (prevFiles[i] !== files[i]) { + return true; + } + } + return false; +} + // -w indicates watch mode where any FS events will trigger reskindex if (!args.w) { reskindex(); From 8715b5233c63f9bedfc92f81dea2e4c36d90f715 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 15 May 2017 10:31:17 +0100 Subject: [PATCH 29/69] link to #/register Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/FilePanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js index e7438afc4d..d83b6b5564 100644 --- a/src/components/structures/FilePanel.js +++ b/src/components/structures/FilePanel.js @@ -92,7 +92,7 @@ var FilePanel = React.createClass({ render: function() { if (MatrixClientPeg.get().isGuest()) { return
-
You must register to use this functionality
+
You must register to use this functionality
; } else if (this.noRoom) { return
From 6bd7af29176d952f171591b1be147f4711802b69 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 14:00:09 +0100 Subject: [PATCH 30/69] Revert "Merge pull request #867 from matrix-org/t3chguy/BaseDialog-patch1" This reverts commit 3549ff254325cdba689b425307218e678c89a2c8, reversing changes made to 1db677141ea281ac2fba361006597768110f77a5. --- src/components/views/dialogs/BaseDialog.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js index 02460148b3..279dedbd43 100644 --- a/src/components/views/dialogs/BaseDialog.js +++ b/src/components/views/dialogs/BaseDialog.js @@ -57,25 +57,20 @@ export default React.createClass({ } }, - // Don't let key{down,press} events escape the modal. Consume them all. - _eatKeyEvent: function(e) { - e.stopPropagation(); - }, - // Must be when the key is released (and not pressed) otherwise componentWillUnmount // will focus another element which will receive future key events _onKeyUp: function(e) { if (e.keyCode === KeyCode.ESCAPE) { + e.stopPropagation(); e.preventDefault(); this.props.onFinished(); } else if (e.keyCode === KeyCode.ENTER) { if (this.props.onEnterPressed) { + e.stopPropagation(); e.preventDefault(); this.props.onEnterPressed(e); } } - // Consume all keyup events while Modal is open - e.stopPropagation(); }, _onCancelClick: function(e) { @@ -86,11 +81,7 @@ export default React.createClass({ const TintableSvg = sdk.getComponent("elements.TintableSvg"); return ( -
+
From f8d1a6d24042925692827f6ee100416a34fa516f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 14:26:46 +0100 Subject: [PATCH 31/69] Revert "Fix 'start chat' button" This reverts commit c841eb641b85995bddb235d3db2c779daffe97a1. --- src/components/views/elements/StartChatButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js index 747f75d1b3..02d5677a7c 100644 --- a/src/components/views/elements/StartChatButton.js +++ b/src/components/views/elements/StartChatButton.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; const StartChatButton = function(props) { const ActionButton = sdk.getComponent('elements.ActionButton'); return ( - Date: Tue, 16 May 2017 14:30:02 +0100 Subject: [PATCH 32/69] Revert "Fix Create Room button" This reverts commit 9cae667c063e67b32e60b89e7256d714a056559b. --- src/components/views/elements/CreateRoomButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js index 73c984a860..e7e526d36b 100644 --- a/src/components/views/elements/CreateRoomButton.js +++ b/src/components/views/elements/CreateRoomButton.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; const CreateRoomButton = function(props) { const ActionButton = sdk.getComponent('elements.ActionButton'); return ( - Date: Tue, 16 May 2017 14:49:55 +0100 Subject: [PATCH 33/69] Revert "Merge pull request #859 from matrix-org/dbkr/left_panel_for_newbies_2" This reverts commit 3366d3bbae203ce28ff85f8d7b80a6e3077a02a6, reversing changes made to ceb71a4ef65607cfcf5c50452839edb1aef40593. --- package.json | 1 - src/components/views/elements/ActionButton.js | 80 ------------ .../views/elements/CreateRoomButton.js | 38 ------ src/components/views/elements/HomeButton.js | 38 ------ .../views/elements/RoomDirectoryButton.js | 38 ------ .../views/elements/SettingsButton.js | 38 ------ .../views/elements/StartChatButton.js | 38 ------ src/components/views/rooms/RoomList.js | 121 ++++-------------- 8 files changed, 24 insertions(+), 368 deletions(-) delete mode 100644 src/components/views/elements/ActionButton.js delete mode 100644 src/components/views/elements/CreateRoomButton.js delete mode 100644 src/components/views/elements/HomeButton.js delete mode 100644 src/components/views/elements/RoomDirectoryButton.js delete mode 100644 src/components/views/elements/SettingsButton.js delete mode 100644 src/components/views/elements/StartChatButton.js diff --git a/package.json b/package.json index 21add8ccb7..3e1fa2d8f3 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "lodash": "^4.13.1", "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", "optimist": "^0.6.1", - "prop-types": "^15.5.8", "q": "^1.4.1", "react": "^15.4.0", "react-addons-css-transition-group": "15.3.2", diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js deleted file mode 100644 index 267388daf6..0000000000 --- a/src/components/views/elements/ActionButton.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2017 Vector Creations 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 AccessibleButton from './AccessibleButton'; -import dis from '../../../dispatcher'; -import sdk from '../../../index'; - -export default React.createClass({ - displayName: 'RoleButton', - - propTypes: { - size: PropTypes.string, - tooltip: PropTypes.bool, - action: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - iconPath: PropTypes.string.isRequired, - }, - - getDefaultProps: function() { - return { - size: "25", - tooltip: false, - }; - }, - - getInitialState: function() { - return { - showTooltip: false, - }; - }, - - _onClick: function(ev) { - ev.stopPropagation(); - dis.dispatch({action: this.props.action}); - }, - - _onMouseEnter: function() { - if (this.props.tooltip) this.setState({showTooltip: true}); - }, - - _onMouseLeave: function() { - this.setState({showTooltip: false}); - }, - - render: function() { - const TintableSvg = sdk.getComponent("elements.TintableSvg"); - - let tooltip; - if (this.state.showTooltip) { - const RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); - tooltip = ; - } - - return ( - - - {tooltip} - - ); - } -}); diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js deleted file mode 100644 index e7e526d36b..0000000000 --- a/src/components/views/elements/CreateRoomButton.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2017 Vector Creations 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 sdk from '../../../index'; -import PropTypes from 'prop-types'; - -const CreateRoomButton = function(props) { - const ActionButton = sdk.getComponent('elements.ActionButton'); - return ( - - ); -}; - -CreateRoomButton.propTypes = { - size: PropTypes.string, - tooltip: PropTypes.bool, -}; - -export default CreateRoomButton; diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js deleted file mode 100644 index 5c446f24c9..0000000000 --- a/src/components/views/elements/HomeButton.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2017 Vector Creations 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 sdk from '../../../index'; -import PropTypes from 'prop-types'; - -const HomeButton = function(props) { - const ActionButton = sdk.getComponent('elements.ActionButton'); - return ( - - ); -}; - -HomeButton.propTypes = { - size: PropTypes.string, - tooltip: PropTypes.bool, -}; - -export default HomeButton; diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js deleted file mode 100644 index 5e68776a15..0000000000 --- a/src/components/views/elements/RoomDirectoryButton.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2017 Vector Creations 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 sdk from '../../../index'; -import PropTypes from 'prop-types'; - -const RoomDirectoryButton = function(props) { - const ActionButton = sdk.getComponent('elements.ActionButton'); - return ( - - ); -}; - -RoomDirectoryButton.propTypes = { - size: PropTypes.string, - tooltip: PropTypes.bool, -}; - -export default RoomDirectoryButton; diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js deleted file mode 100644 index c6438da277..0000000000 --- a/src/components/views/elements/SettingsButton.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2017 Vector Creations 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 sdk from '../../../index'; -import PropTypes from 'prop-types'; - -const SettingsButton = function(props) { - const ActionButton = sdk.getComponent('elements.ActionButton'); - return ( - - ); -}; - -SettingsButton.propTypes = { - size: PropTypes.string, - tooltip: PropTypes.bool, -}; - -export default SettingsButton; diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js deleted file mode 100644 index 02d5677a7c..0000000000 --- a/src/components/views/elements/StartChatButton.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2017 Vector Creations 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 sdk from '../../../index'; -import PropTypes from 'prop-types'; - -const StartChatButton = function(props) { - const ActionButton = sdk.getComponent('elements.ActionButton'); - return ( - - ); -}; - -StartChatButton.propTypes = { - size: PropTypes.string, - tooltip: PropTypes.bool, -}; - -export default StartChatButton; diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index a595a91ba9..49af1560f1 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -1,6 +1,5 @@ /* Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -29,16 +28,8 @@ var Rooms = require('../../../Rooms'); import DMRoomMap from '../../../utils/DMRoomMap'; var Receipt = require('../../../utils/Receipt'); var constantTimeDispatcher = require('../../../ConstantTimeDispatcher'); -import AccessibleButton from '../elements/AccessibleButton'; -const HIDE_CONFERENCE_CHANS = true; - -const VERBS = { - 'm.favourite': 'favourite', - 'im.vector.fake.direct': 'tag direct chat', - 'im.vector.fake.recent': 'restore', - 'm.lowpriority': 'demote', -}; +var HIDE_CONFERENCE_CHANS = true; module.exports = React.createClass({ displayName: 'RoomList', @@ -62,7 +53,6 @@ module.exports = React.createClass({ getInitialState: function() { return { isLoadingLeftRooms: false, - totalRoomCount: null, lists: {}, incomingCall: null, }; @@ -83,7 +73,8 @@ module.exports = React.createClass({ // lookup for which lists a given roomId is currently in. this.listsForRoomId = {}; - this.refreshRoomList(); + var s = this.getRoomLists(); + this.setState(s); // order of the sublists //this.listOrder = []; @@ -326,29 +317,21 @@ module.exports = React.createClass({ // any changes to it incrementally, updating the appropriate sublists // as needed. // Alternatively we'd do something magical with Immutable.js or similar. - const lists = this.getRoomLists(); - let totalRooms = 0; - for (const l of Object.values(lists)) { - totalRooms += l.length; - } - this.setState({ - lists: this.getRoomLists(), - totalRoomCount: totalRooms, - }); - + this.setState(this.getRoomLists()); + // this._lastRefreshRoomListTs = Date.now(); }, getRoomLists: function() { var self = this; - const lists = {}; + var s = { lists: {} }; - lists["im.vector.fake.invite"] = []; - lists["m.favourite"] = []; - lists["im.vector.fake.recent"] = []; - lists["im.vector.fake.direct"] = []; - lists["m.lowpriority"] = []; - lists["im.vector.fake.archived"] = []; + s.lists["im.vector.fake.invite"] = []; + s.lists["m.favourite"] = []; + s.lists["im.vector.fake.recent"] = []; + s.lists["im.vector.fake.direct"] = []; + s.lists["m.lowpriority"] = []; + s.lists["im.vector.fake.archived"] = []; this.listsForRoomId = {}; var otherTagNames = {}; @@ -370,7 +353,7 @@ module.exports = React.createClass({ if (me.membership == "invite") { self.listsForRoomId[room.roomId].push("im.vector.fake.invite"); - lists["im.vector.fake.invite"].push(room); + s.lists["im.vector.fake.invite"].push(room); } else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) { // skip past this room & don't put it in any lists @@ -383,8 +366,8 @@ module.exports = React.createClass({ if (tagNames.length) { for (var i = 0; i < tagNames.length; i++) { var tagName = tagNames[i]; - lists[tagName] = lists[tagName] || []; - lists[tagName].push(room); + s.lists[tagName] = s.lists[tagName] || []; + s.lists[tagName].push(room); self.listsForRoomId[room.roomId].push(tagName); otherTagNames[tagName] = 1; } @@ -392,16 +375,16 @@ module.exports = React.createClass({ else if (dmRoomMap.getUserIdForRoomId(room.roomId)) { // "Direct Message" rooms (that we're still in and that aren't otherwise tagged) self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); - lists["im.vector.fake.direct"].push(room); + s.lists["im.vector.fake.direct"].push(room); } else { self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); - lists["im.vector.fake.recent"].push(room); + s.lists["im.vector.fake.recent"].push(room); } } else if (me.membership === "leave") { self.listsForRoomId[room.roomId].push("im.vector.fake.archived"); - lists["im.vector.fake.archived"].push(room); + s.lists["im.vector.fake.archived"].push(room); } else { console.error("unrecognised membership: " + me.membership + " - this should never happen"); @@ -425,7 +408,7 @@ module.exports = React.createClass({ ]; */ - return lists; + return s; }, _getScrollNode: function() { @@ -455,7 +438,6 @@ module.exports = React.createClass({ var incomingCallBox = document.getElementById("incomingCallBox"); if (incomingCallBox && incomingCallBox.parentElement) { var scrollArea = this._getScrollNode(); - if (!scrollArea) return; // Use the offset of the top of the scroll area from the window // as this is used to calculate the CSS fixed top position for the stickies var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset; @@ -479,7 +461,6 @@ module.exports = React.createClass({ // properly through React _initAndPositionStickyHeaders: function(initialise, scrollToPosition) { var scrollArea = this._getScrollNode(); - if (!scrollArea) return; // Use the offset of the top of the scroll area from the window // as this is used to calculate the CSS fixed top position for the stickies var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset; @@ -577,58 +558,6 @@ module.exports = React.createClass({ this.refs.gemscroll.forceUpdate(); }, - _getEmptyContent: function(section) { - const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); - - if (this.props.collapsed) { - return ; - } - - const StartChatButton = sdk.getComponent('elements.StartChatButton'); - const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); - const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); - if (this.state.totalRoomCount === 0) { - const TintableSvg = sdk.getComponent('elements.TintableSvg'); - switch (section) { - case 'im.vector.fake.direct': - return
- Press - - to start a chat with someone -
; - case 'im.vector.fake.recent': - return
- You're not in any rooms yet! Press - - to make a room or - - to browse the directory -
; - } - } - - const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section); - - return ; - }, - - _getHeaderItems: function(section) { - const StartChatButton = sdk.getComponent('elements.StartChatButton'); - const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); - const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); - switch (section) { - case 'im.vector.fake.direct': - return - - ; - case 'im.vector.fake.recent': - return - - - ; - } - }, - render: function() { var RoomSubList = sdk.getComponent('structures.RoomSubList'); var self = this; @@ -652,7 +581,7 @@ module.exports = React.createClass({ Date: Tue, 16 May 2017 14:50:19 +0100 Subject: [PATCH 34/69] Revert "Merge pull request #841 from matrix-org/luke/fix-double-dialogs" This reverts commit 1913a32fbd0c303080645f2661cacff25ccff232, reversing changes made to 0c16298c452089188bcc156cf213358988ce7341. --- src/components/views/dialogs/BaseDialog.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js index 279dedbd43..d0f34c5fbd 100644 --- a/src/components/views/dialogs/BaseDialog.js +++ b/src/components/views/dialogs/BaseDialog.js @@ -57,9 +57,7 @@ export default React.createClass({ } }, - // Must be when the key is released (and not pressed) otherwise componentWillUnmount - // will focus another element which will receive future key events - _onKeyUp: function(e) { + _onKeyDown: function(e) { if (e.keyCode === KeyCode.ESCAPE) { e.stopPropagation(); e.preventDefault(); @@ -81,7 +79,7 @@ export default React.createClass({ const TintableSvg = sdk.getComponent("elements.TintableSvg"); return ( -
+
From ff9c40472a5b1f52450347a890ea720c78e1aa39 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 14:50:29 +0100 Subject: [PATCH 35/69] Revert "Merge pull request #822 from t3chguy/BaseDialog_restore_focus" This reverts commit 0ac836919d54cd04705002d8e53d3514dcce8dde, reversing changes made to 7e07ffd55fe1475ce30e172fa5b0b37061e7375f. --- src/components/views/dialogs/BaseDialog.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js index d0f34c5fbd..0b2ca5225d 100644 --- a/src/components/views/dialogs/BaseDialog.js +++ b/src/components/views/dialogs/BaseDialog.js @@ -47,16 +47,6 @@ export default React.createClass({ children: React.PropTypes.node, }, - componentWillMount: function() { - this.priorActiveElement = document.activeElement; - }, - - componentWillUnmount: function() { - if (this.priorActiveElement !== null) { - this.priorActiveElement.focus(); - } - }, - _onKeyDown: function(e) { if (e.keyCode === KeyCode.ESCAPE) { e.stopPropagation(); @@ -77,7 +67,7 @@ export default React.createClass({ render: function() { const TintableSvg = sdk.getComponent("elements.TintableSvg"); - + return (
Date: Tue, 16 May 2017 15:35:22 +0100 Subject: [PATCH 36/69] Revert "comment out spammy CTD logging" This reverts commit 19482d751d82c162321d99c5bf95ac8fe950768b. --- src/ConstantTimeDispatcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ConstantTimeDispatcher.js b/src/ConstantTimeDispatcher.js index 6c2c3266aa..265ee11fd4 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 714cd6a10fd49df86caa9df4406e7a31cc7bfea1 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 16:00:34 +0100 Subject: [PATCH 37/69] Revert "recalculate roomlist when your invites change" This reverts commit ec6a1c4c750f959017cdf823402a6c9d86b16fe2. --- src/components/views/rooms/RoomList.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 49af1560f1..101f0838a9 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -265,16 +265,9 @@ module.exports = React.createClass({ }, onRoomStateMember: function(ev, state, member) { - if (ev.getStateKey() === MatrixClientPeg.get().credentials.userId && - ev.getPrevContent() && ev.getPrevContent().membership === "invite") - { - this._delayedRefreshRoomList(); - } - else { - constantTimeDispatcher.dispatch( - "RoomTile.refresh", member.roomId, {} - ); - } + constantTimeDispatcher.dispatch( + "RoomTile.refresh", member.roomId, {} + ); }, onRoomMemberName: function(ev, member) { From b063c605a86bdd9f216506e09bd93f54e42a29df Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 16:01:14 +0100 Subject: [PATCH 38/69] Revert "fix stupid typos in RoomList's shouldComponentUpdate" This reverts commit b0288ebd89841ad362a6804b41839ef9d3bdca7f. --- src/components/views/rooms/RoomList.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 101f0838a9..b99d26376f 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -44,9 +44,9 @@ module.exports = React.createClass({ shouldComponentUpdate: function(nextProps, nextState) { if (nextProps.collapsed !== this.props.collapsed) return true; if (nextProps.searchFilter !== this.props.searchFilter) return true; - if (nextState.lists !== this.state.lists || - nextState.isLoadingLeftRooms !== this.state.isLoadingLeftRooms || - nextState.incomingCall !== this.state.incomingCall) return true; + if (nextState.lists !== this.props.lists || + nextState.isLoadingLeftRooms !== this.isLoadingLeftRooms || + nextState.incomingCall !== this.incomingCall) return true; return false; }, From 7a949b6a4591cdb7c5fe93b8aef610388ef8eba4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 16:01:32 +0100 Subject: [PATCH 39/69] Revert "oops, actually refresh roomlist when its state changes!" This reverts commit 35a16edcccd07fdd4209783ce1efaee8115eb4b4. --- src/components/views/rooms/RoomList.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index b99d26376f..b4ab25a0f2 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -44,9 +44,6 @@ module.exports = React.createClass({ shouldComponentUpdate: function(nextProps, nextState) { if (nextProps.collapsed !== this.props.collapsed) return true; if (nextProps.searchFilter !== this.props.searchFilter) return true; - if (nextState.lists !== this.props.lists || - nextState.isLoadingLeftRooms !== this.isLoadingLeftRooms || - nextState.incomingCall !== this.incomingCall) return true; return false; }, From eddc2af92d7a465f755c472f3ea3da3cca3a185f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 16:01:54 +0100 Subject: [PATCH 40/69] Revert "HOW DID THIS EVER WORK?" This reverts commit 0d8d3c67106a3e84fd30de4016b9c852870f99b3. --- src/components/views/rooms/RoomList.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index b4ab25a0f2..b86d17d290 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -558,7 +558,6 @@ module.exports = React.createClass({
Date: Tue, 16 May 2017 16:02:13 +0100 Subject: [PATCH 41/69] Revert "unbreak stack overflow which fires on tests due to mocked timers" This reverts commit e69ea68133bb01dfd2093ffc5644edef24fbed70. --- src/components/views/rooms/RoomList.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index b86d17d290..d6e2ab633d 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -74,11 +74,7 @@ module.exports = React.createClass({ this.setState(s); // order of the sublists - //this.listOrder = []; - - // loop count to stop a stack overflow if the user keeps waggling the - // mouse for >30s in a row, or if running under mocha - this._delayedRefreshRoomListLoopCount = 0 + this.listOrder = []; }, componentDidMount: function() { @@ -288,12 +284,10 @@ module.exports = React.createClass({ // if the mouse has been moving over the RoomList in the last 500ms // then delay the refresh further to avoid bouncing around under the // cursor - if (Date.now() - this._lastMouseOverTs > 500 || this._delayedRefreshRoomListLoopCount > 60) { + if (Date.now() - this._lastMouseOverTs > 500) { this.refreshRoomList(); - this._delayedRefreshRoomListLoopCount = 0; } else { - this._delayedRefreshRoomListLoopCount++; this._delayedRefreshRoomList(); } }, 500), From ebfafb363972e77ef02f0e518573bf68ebc0ed15 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 16:11:01 +0100 Subject: [PATCH 42/69] Revert "Merge pull request #807 from matrix-org/matthew/quick-search" This reverts commit 0ad1d8caf3f83aca517d1afff492f1c3d01e9ad5, reversing changes made to 1189368aab8cfb22c1895f8ce6c0d8a8fbe7ca0b. --- package.json | 2 +- src/ConstantTimeDispatcher.js | 62 ----- src/KeyCode.js | 1 - src/components/structures/TimelinePanel.js | 1 - .../views/dialogs/QuestionDialog.js | 8 +- src/components/views/rooms/RoomList.js | 233 ++++++------------ src/components/views/rooms/RoomTile.js | 57 ++--- 7 files changed, 99 insertions(+), 265 deletions(-) delete mode 100644 src/ConstantTimeDispatcher.js diff --git a/package.json b/package.json index 3e1fa2d8f3..444d1c5369 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "react": "^15.4.0", "react-addons-css-transition-group": "15.3.2", "react-dom": "^15.4.0", - "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#39d858c", + "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef", "sanitize-html": "^1.11.1", "text-encoding-utf-8": "^1.0.1", "velocity-vector": "vector-im/velocity#059e3b2", diff --git a/src/ConstantTimeDispatcher.js b/src/ConstantTimeDispatcher.js deleted file mode 100644 index 265ee11fd4..0000000000 --- a/src/ConstantTimeDispatcher.js +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2017 Vector Creations 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. -*/ - -// singleton which dispatches invocations of a given type & argument -// rather than just a type (as per EventEmitter and Flux's dispatcher etc) -// -// This means you can have a single point which listens for an EventEmitter event -// and then dispatches out to one of thousands of RoomTiles (for instance) rather than -// having each RoomTile register for the EventEmitter event and having to -// iterate over all of them. -class ConstantTimeDispatcher { - constructor() { - // type -> arg -> [ listener(arg, params) ] - this.listeners = {}; - } - - register(type, arg, listener) { - if (!this.listeners[type]) this.listeners[type] = {}; - if (!this.listeners[type][arg]) this.listeners[type][arg] = []; - this.listeners[type][arg].push(listener); - } - - unregister(type, arg, listener) { - if (this.listeners[type] && this.listeners[type][arg]) { - var i = this.listeners[type][arg].indexOf(listener); - if (i > -1) { - this.listeners[type][arg].splice(i, 1); - } - } - else { - console.warn("Unregistering unrecognised listener (type=" + type + ", arg=" + arg + ")"); - } - } - - dispatch(type, arg, params) { - if (!this.listeners[type] || !this.listeners[type][arg]) { - console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")"); - return; - } - this.listeners[type][arg].forEach(listener=>{ - listener.call(arg, params); - }); - } -} - -if (!global.constantTimeDispatcher) { - global.constantTimeDispatcher = new ConstantTimeDispatcher(); -} -module.exports = global.constantTimeDispatcher; diff --git a/src/KeyCode.js b/src/KeyCode.js index f164dbc15c..c9cac01239 100644 --- a/src/KeyCode.js +++ b/src/KeyCode.js @@ -32,5 +32,4 @@ module.exports = { DELETE: 46, KEY_D: 68, KEY_E: 69, - KEY_K: 75, }; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index de43bd1c19..8794713501 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -590,7 +590,6 @@ var TimelinePanel = React.createClass({ this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0); dis.dispatch({ action: 'on_room_read', - room: this.props.timelineSet.room, }); } } diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js index 8e20b0d2bc..6012541b94 100644 --- a/src/components/views/dialogs/QuestionDialog.js +++ b/src/components/views/dialogs/QuestionDialog.js @@ -47,12 +47,6 @@ export default React.createClass({ this.props.onFinished(false); }, - componentDidMount: function() { - if (this.props.focus) { - this.refs.button.focus(); - } - }, - render: function() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const cancelButton = this.props.hasCancelButton ? ( @@ -69,7 +63,7 @@ export default React.createClass({ {this.props.description}
- {this.props.extraButtons} diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index d6e2ab633d..611dd10780 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -21,13 +21,13 @@ var GeminiScrollbar = require('react-gemini-scrollbar'); var MatrixClientPeg = require("../../../MatrixClientPeg"); var CallHandler = require('../../../CallHandler'); var RoomListSorter = require("../../../RoomListSorter"); +var Unread = require('../../../Unread'); var dis = require("../../../dispatcher"); var sdk = require('../../../index'); var rate_limited_func = require('../../../ratelimitedfunc'); var Rooms = require('../../../Rooms'); import DMRoomMap from '../../../utils/DMRoomMap'; var Receipt = require('../../../utils/Receipt'); -var constantTimeDispatcher = require('../../../ConstantTimeDispatcher'); var HIDE_CONFERENCE_CHANS = true; @@ -37,16 +37,10 @@ module.exports = React.createClass({ propTypes: { ConferenceHandler: React.PropTypes.any, collapsed: React.PropTypes.bool.isRequired, - selectedRoom: React.PropTypes.string, + currentRoom: React.PropTypes.string, searchFilter: React.PropTypes.string, }, - shouldComponentUpdate: function(nextProps, nextState) { - if (nextProps.collapsed !== this.props.collapsed) return true; - if (nextProps.searchFilter !== this.props.searchFilter) return true; - return false; - }, - getInitialState: function() { return { isLoadingLeftRooms: false, @@ -63,18 +57,12 @@ module.exports = React.createClass({ cli.on("Room.name", this.onRoomName); cli.on("Room.tags", this.onRoomTags); cli.on("Room.receipt", this.onRoomReceipt); - cli.on("RoomState.members", this.onRoomStateMember); + cli.on("RoomState.events", this.onRoomStateEvents); cli.on("RoomMember.name", this.onRoomMemberName); cli.on("accountData", this.onAccountData); - // lookup for which lists a given roomId is currently in. - this.listsForRoomId = {}; - var s = this.getRoomLists(); this.setState(s); - - // order of the sublists - this.listOrder = []; }, componentDidMount: function() { @@ -83,22 +71,7 @@ module.exports = React.createClass({ this._updateStickyHeaders(true); }, - componentWillReceiveProps: function(nextProps) { - // short-circuit react when the room changes - // to avoid rerendering all the sublists everywhere - if (nextProps.selectedRoom !== this.props.selectedRoom) { - if (this.props.selectedRoom) { - constantTimeDispatcher.dispatch( - "RoomTile.select", this.props.selectedRoom, {} - ); - } - constantTimeDispatcher.dispatch( - "RoomTile.select", nextProps.selectedRoom, { selected: true } - ); - } - }, - - componentDidUpdate: function(prevProps, prevState) { + componentDidUpdate: function() { // Reinitialise the stickyHeaders when the component is updated this._updateStickyHeaders(true); this._repositionIncomingCallBox(undefined, false); @@ -124,24 +97,10 @@ module.exports = React.createClass({ } break; case 'on_room_read': - // poke the right RoomTile to refresh, using the constantTimeDispatcher - // to avoid each and every RoomTile registering to the 'on_room_read' event - // XXX: if we like the constantTimeDispatcher we might want to dispatch - // directly from TimelinePanel rather than needlessly bouncing via here. - constantTimeDispatcher.dispatch( - "RoomTile.refresh", payload.room.roomId, {} - ); - - // also have to poke the right list(s) - var lists = this.listsForRoomId[payload.room.roomId]; - if (lists) { - lists.forEach(list=>{ - constantTimeDispatcher.dispatch( - "RoomSubList.refreshHeader", list, { room: payload.room } - ); - }); - } - + // Force an update because the notif count state is too deep to cause + // an update. This forces the local echo of reading notifs to be + // reflected by the RoomTiles. + this.forceUpdate(); break; } }, @@ -155,7 +114,7 @@ module.exports = React.createClass({ MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); MatrixClientPeg.get().removeListener("Room.tags", this.onRoomTags); MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt); - MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); + MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents); MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName); MatrixClientPeg.get().removeListener("accountData", this.onAccountData); } @@ -164,14 +123,10 @@ module.exports = React.createClass({ }, onRoom: function(room) { - // XXX: this happens rarely; ideally we should only update the correct - // sublists when it does (e.g. via a constantTimeDispatch to the right sublist) this._delayedRefreshRoomList(); }, onDeleteRoom: function(roomId) { - // XXX: this happens rarely; ideally we should only update the correct - // sublists when it does (e.g. via a constantTimeDispatch to the right sublist) this._delayedRefreshRoomList(); }, @@ -194,10 +149,6 @@ module.exports = React.createClass({ } }, - _onMouseOver: function(ev) { - this._lastMouseOverTs = Date.now(); - }, - onSubListHeaderClick: function(isHidden, scrollToPosition) { // The scroll area has expanded or contracted, so re-calculate sticky headers positions this._updateStickyHeaders(true, scrollToPosition); @@ -207,89 +158,41 @@ module.exports = React.createClass({ if (toStartOfTimeline) return; if (!room) return; if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return; - - // rather than regenerate our full roomlists, which is very heavy, we poke the - // correct sublists to just re-sort themselves. This isn't enormously reacty, - // but is much faster than the default react reconciler, or having to do voodoo - // with shouldComponentUpdate and a pleaseRefresh property or similar. - var lists = this.listsForRoomId[room.roomId]; - if (lists) { - lists.forEach(list=>{ - constantTimeDispatcher.dispatch("RoomSubList.sort", list, { room: room }); - }); - } - - // we have to explicitly hit the roomtile which just changed - constantTimeDispatcher.dispatch( - "RoomTile.refresh", room.roomId, {} - ); + this._delayedRefreshRoomList(); }, onRoomReceipt: function(receiptEvent, room) { // because if we read a notification, it will affect notification count // only bother updating if there's a receipt from us if (Receipt.findReadReceiptFromUserId(receiptEvent, MatrixClientPeg.get().credentials.userId)) { - var lists = this.listsForRoomId[room.roomId]; - if (lists) { - lists.forEach(list=>{ - constantTimeDispatcher.dispatch( - "RoomSubList.refreshHeader", list, { room: room } - ); - }); - } - - // we have to explicitly hit the roomtile which just changed - constantTimeDispatcher.dispatch( - "RoomTile.refresh", room.roomId, {} - ); + this._delayedRefreshRoomList(); } }, onRoomName: function(room) { - constantTimeDispatcher.dispatch( - "RoomTile.refresh", room.roomId, {} - ); - }, - - onRoomTags: function(event, room) { - // XXX: this happens rarely; ideally we should only update the correct - // sublists when it does (e.g. via a constantTimeDispatch to the right sublist) this._delayedRefreshRoomList(); }, - onRoomStateMember: function(ev, state, member) { - constantTimeDispatcher.dispatch( - "RoomTile.refresh", member.roomId, {} - ); + onRoomTags: function(event, room) { + this._delayedRefreshRoomList(); + }, + + onRoomStateEvents: function(ev, state) { + this._delayedRefreshRoomList(); }, onRoomMemberName: function(ev, member) { - constantTimeDispatcher.dispatch( - "RoomTile.refresh", member.roomId, {} - ); + this._delayedRefreshRoomList(); }, onAccountData: function(ev) { if (ev.getType() == 'm.direct') { - // XXX: this happens rarely; ideally we should only update the correct - // sublists when it does (e.g. via a constantTimeDispatch to the right sublist) - this._delayedRefreshRoomList(); - } - else if (ev.getType() == 'm.push_rules') { this._delayedRefreshRoomList(); } }, _delayedRefreshRoomList: new rate_limited_func(function() { - // if the mouse has been moving over the RoomList in the last 500ms - // then delay the refresh further to avoid bouncing around under the - // cursor - if (Date.now() - this._lastMouseOverTs > 500) { - this.refreshRoomList(); - } - else { - this._delayedRefreshRoomList(); - } + this.refreshRoomList(); }, 500), refreshRoomList: function() { @@ -297,10 +200,12 @@ module.exports = React.createClass({ // (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs)) // ); - // TODO: ideally we'd calculate this once at start, and then maintain - // any changes to it incrementally, updating the appropriate sublists - // as needed. - // Alternatively we'd do something magical with Immutable.js or similar. + // TODO: rather than bluntly regenerating and re-sorting everything + // every time we see any kind of room change from the JS SDK + // we could do incremental updates on our copy of the state + // based on the room which has actually changed. This would stop + // us re-rendering all the sublists every time anything changes anywhere + // in the state of the client. this.setState(this.getRoomLists()); // this._lastRefreshRoomListTs = Date.now(); @@ -317,9 +222,6 @@ module.exports = React.createClass({ s.lists["m.lowpriority"] = []; s.lists["im.vector.fake.archived"] = []; - this.listsForRoomId = {}; - var otherTagNames = {}; - const dmRoomMap = new DMRoomMap(MatrixClientPeg.get()); MatrixClientPeg.get().getRooms().forEach(function(room) { @@ -331,12 +233,7 @@ module.exports = React.createClass({ // ", target = " + me.events.member.getStateKey() + // ", prevMembership = " + me.events.member.getPrevContent().membership); - if (!self.listsForRoomId[room.roomId]) { - self.listsForRoomId[room.roomId] = []; - } - if (me.membership == "invite") { - self.listsForRoomId[room.roomId].push("im.vector.fake.invite"); s.lists["im.vector.fake.invite"].push(room); } else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) { @@ -347,27 +244,23 @@ module.exports = React.createClass({ { // Used to split rooms via tags var tagNames = Object.keys(room.tags); + if (tagNames.length) { for (var i = 0; i < tagNames.length; i++) { var tagName = tagNames[i]; s.lists[tagName] = s.lists[tagName] || []; - s.lists[tagName].push(room); - self.listsForRoomId[room.roomId].push(tagName); - otherTagNames[tagName] = 1; + s.lists[tagNames[i]].push(room); } } else if (dmRoomMap.getUserIdForRoomId(room.roomId)) { // "Direct Message" rooms (that we're still in and that aren't otherwise tagged) - 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); } } else if (me.membership === "leave") { - self.listsForRoomId[room.roomId].push("im.vector.fake.archived"); s.lists["im.vector.fake.archived"].push(room); } else { @@ -375,22 +268,44 @@ module.exports = React.createClass({ } }); - // we actually apply the sorting to this when receiving the prop in RoomSubLists. + 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"] = []; - // 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", - "im.vector.fake.recent", - "im.vector.fake.direct", - Object.keys(otherTagNames).filter(tagName=>{ - return (!tagName.match(/^m\.(favourite|lowpriority)$/)); - }).sort(), - "m.lowpriority", - "im.vector.fake.archived" - ]; -*/ + for (const room of oldRecents) { + const me = room.getMember(MatrixClientPeg.get().credentials.userId); + + if (me && Rooms.looksLikeDirectMessageRoom(room, me)) { + s.lists["im.vector.fake.direct"].push(room); + } else { + 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; + } + + // 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. return s; }, @@ -548,15 +463,15 @@ module.exports = React.createClass({ return ( -
+ autoshow={true} onScroll={ self._whenScrolling } ref="gemscroll"> +
@@ -567,9 +482,9 @@ module.exports = React.createClass({ verb="favourite" editable={ true } order="manual" + selectedRoom={ self.props.selectedRoom } incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } - selectedRoom={ self.props.selectedRoom } searchFilter={ self.props.searchFilter } onHeaderClick={ self.onSubListHeaderClick } onShowMoreRooms={ self.onShowMoreRooms } /> @@ -580,9 +495,9 @@ module.exports = React.createClass({ verb="tag direct chat" editable={ true } order="recent" + selectedRoom={ self.props.selectedRoom } incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } - selectedRoom={ self.props.selectedRoom } alwaysShowHeader={ true } searchFilter={ self.props.searchFilter } onHeaderClick={ self.onSubListHeaderClick } @@ -593,14 +508,14 @@ module.exports = React.createClass({ editable={ true } verb="restore" order="recent" + selectedRoom={ self.props.selectedRoom } incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } - selectedRoom={ self.props.selectedRoom } searchFilter={ self.props.searchFilter } onHeaderClick={ self.onSubListHeaderClick } onShowMoreRooms={ self.onShowMoreRooms } /> - { Object.keys(self.state.lists).sort().map(function(tagName) { + { Object.keys(self.state.lists).map(function(tagName) { if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { return ; @@ -625,9 +540,9 @@ module.exports = React.createClass({ verb="demote" editable={ true } order="recent" + selectedRoom={ self.props.selectedRoom } incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } - selectedRoom={ self.props.selectedRoom } searchFilter={ self.props.searchFilter } onHeaderClick={ self.onSubListHeaderClick } onShowMoreRooms={ self.onShowMoreRooms } /> @@ -636,8 +551,8 @@ module.exports = React.createClass({ label="Historical" editable={ false } order="recent" - collapsed={ self.props.collapsed } selectedRoom={ self.props.selectedRoom } + collapsed={ self.props.collapsed } alwaysShowHeader={ true } startAsHidden={ true } showSpinner={ self.state.isLoadingLeftRooms } diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 3b37d4608f..c123f31d19 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -27,8 +27,6 @@ var RoomNotifs = require('../../../RoomNotifs'); var FormattingUtils = require('../../../utils/FormattingUtils'); import AccessibleButton from '../elements/AccessibleButton'; var UserSettingsStore = require('../../../UserSettingsStore'); -var constantTimeDispatcher = require('../../../ConstantTimeDispatcher'); -var Unread = require('../../../Unread'); module.exports = React.createClass({ displayName: 'RoomTile', @@ -38,10 +36,12 @@ module.exports = React.createClass({ connectDropTarget: React.PropTypes.func, onClick: React.PropTypes.func, isDragging: React.PropTypes.bool, - selectedRoom: React.PropTypes.string, room: React.PropTypes.object.isRequired, collapsed: React.PropTypes.bool.isRequired, + selected: React.PropTypes.bool.isRequired, + unread: React.PropTypes.bool.isRequired, + highlight: React.PropTypes.bool.isRequired, isInvite: React.PropTypes.bool.isRequired, incomingCall: React.PropTypes.object, }, @@ -54,11 +54,10 @@ module.exports = React.createClass({ getInitialState: function() { return({ - hover: false, - badgeHover: false, + hover : false, + badgeHover : false, menuDisplayed: false, notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId), - selected: this.props.room ? (this.props.selectedRoom === this.props.room.roomId) : false, }); }, @@ -80,32 +79,23 @@ module.exports = React.createClass({ } }, + onAccountData: function(accountDataEvent) { + if (accountDataEvent.getType() == 'm.push_rules') { + this.setState({ + notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId), + }); + } + }, + componentWillMount: function() { - constantTimeDispatcher.register("RoomTile.refresh", this.props.room.roomId, this.onRefresh); - constantTimeDispatcher.register("RoomTile.select", this.props.room.roomId, this.onSelect); - this.onRefresh(); + MatrixClientPeg.get().on("accountData", this.onAccountData); }, componentWillUnmount: function() { - constantTimeDispatcher.unregister("RoomTile.refresh", this.props.room.roomId, this.onRefresh); - constantTimeDispatcher.unregister("RoomTile.select", this.props.room.roomId, this.onSelect); - }, - - componentWillReceiveProps: function(nextProps) { - this.onRefresh(); - }, - - onRefresh: function(params) { - this.setState({ - unread: Unread.doesRoomHaveUnreadMessages(this.props.room), - highlight: this.props.room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite, - }); - }, - - onSelect: function(params) { - this.setState({ - selected: params.selected, - }); + var cli = MatrixClientPeg.get(); + if (cli) { + MatrixClientPeg.get().removeListener("accountData", this.onAccountData); + } }, onClick: function(ev) { @@ -179,13 +169,13 @@ module.exports = React.createClass({ // var highlightCount = this.props.room.getUnreadNotificationCount("highlight"); const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge(); - const mentionBadges = this.state.highlight && this._shouldShowMentionBadge(); + const mentionBadges = this.props.highlight && this._shouldShowMentionBadge(); const badges = notifBadges || mentionBadges; var classes = classNames({ 'mx_RoomTile': true, - 'mx_RoomTile_selected': this.state.selected, - 'mx_RoomTile_unread': this.state.unread, + 'mx_RoomTile_selected': this.props.selected, + 'mx_RoomTile_unread': this.props.unread, 'mx_RoomTile_unreadNotify': notifBadges, 'mx_RoomTile_highlight': mentionBadges, 'mx_RoomTile_invited': (me && me.membership == 'invite'), @@ -231,7 +221,7 @@ module.exports = React.createClass({ 'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed, }); - if (this.state.selected) { + if (this.props.selected) { let nameSelected = {name}; label =
{ nameSelected }
; @@ -265,8 +255,7 @@ module.exports = React.createClass({ let ret = (
{ /* Only native elements can be wrapped in a DnD object. */} - +
From 75eea89c08354caf2f65e9bbeb60a572830c21c2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 May 2017 16:12:57 +0100 Subject: [PATCH 43/69] Revert "Merge pull request #765 from t3chguy/t3chguy/escape-closes-user-settings" This reverts commit a29d8c2af200af9bd5f55da60a10d1fc9c473764, reversing changes made to 1d836c7d02a6935313bfb05d94fc38ae05439480. --- src/components/structures/LoggedInView.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index c4eeb03d5f..25ca025a23 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -107,18 +107,6 @@ export default React.createClass({ var handled = false; switch (ev.keyCode) { - case KeyCode.ESCAPE: - - // Implemented this way so possible handling for other pages is neater - switch (this.props.page_type) { - case PageTypes.UserSettings: - this.props.onUserSettingsClose(); - handled = true; - break; - } - - break; - case KeyCode.UP: case KeyCode.DOWN: if (ev.altKey && !ev.shiftKey && !ev.ctrlKey && !ev.metaKey) { From 8695397abbe00d8a3d938580539929ff5f889a0c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 17 May 2017 01:41:42 +0100 Subject: [PATCH 44/69] Support for pasting files into normal composer We don't seem to be in any danger of getting a working RTE any time soon, so implement file pasting in the normal composer too. --- src/components/views/rooms/MessageComposer.js | 10 +++++---- .../views/rooms/MessageComposerInput.js | 9 ++------ .../views/rooms/MessageComposerInputOld.js | 22 ++++++++++++++++++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 0ee3c2082d..830d3f38ff 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -33,6 +33,7 @@ export default class MessageComposer extends React.Component { this.onHangupClick = this.onHangupClick.bind(this); this.onUploadClick = this.onUploadClick.bind(this); this.onUploadFileSelected = this.onUploadFileSelected.bind(this); + this.uploadFiles = this.uploadFiles.bind(this); this.onVoiceCallClick = this.onVoiceCallClick.bind(this); this.onInputContentChanged = this.onInputContentChanged.bind(this); this.onUpArrow = this.onUpArrow.bind(this); @@ -101,10 +102,11 @@ export default class MessageComposer extends React.Component { this.refs.uploadInput.click(); } - onUploadFileSelected(files, isPasted) { - if (!isPasted) - files = files.target.files; + onUploadFileSelected(files) { + this.uploadFiles(files.target.files); + } + uploadFiles(files) { let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); let TintableSvg = sdk.getComponent("elements.TintableSvg"); @@ -310,7 +312,7 @@ export default class MessageComposer extends React.Component { tryComplete={this._tryComplete} onUpArrow={this.onUpArrow} onDownArrow={this.onDownArrow} - onUploadFileSelected={this.onUploadFileSelected} + onFilesPasted={this.uploadFiles} tabComplete={this.props.tabComplete} // used for old messagecomposerinput/tabcomplete onContentChanged={this.onInputContentChanged} onInputStateChanged={this.onInputStateChanged} />, diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index e2fcc19f67..af361db235 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -84,7 +84,6 @@ export default class MessageComposerInput extends React.Component { this.onAction = this.onAction.bind(this); this.handleReturn = this.handleReturn.bind(this); this.handleKeyCommand = this.handleKeyCommand.bind(this); - this.handlePastedFiles = this.handlePastedFiles.bind(this); this.onEditorContentChanged = this.onEditorContentChanged.bind(this); this.setEditorState = this.setEditorState.bind(this); this.onUpArrow = this.onUpArrow.bind(this); @@ -477,10 +476,6 @@ export default class MessageComposerInput extends React.Component { return false; } - handlePastedFiles(files) { - this.props.onUploadFileSelected(files, true); - } - handleReturn(ev) { if (ev.shiftKey) { this.onEditorContentChanged(RichUtils.insertSoftNewline(this.state.editorState)); @@ -734,7 +729,7 @@ export default class MessageComposerInput extends React.Component { keyBindingFn={MessageComposerInput.getKeyBinding} handleKeyCommand={this.handleKeyCommand} handleReturn={this.handleReturn} - handlePastedFiles={this.handlePastedFiles} + handlePastedFiles={this.props.onFilesPasted} stripPastedStyles={!this.state.isRichtextEnabled} onTab={this.onTab} onUpArrow={this.onUpArrow} @@ -764,7 +759,7 @@ MessageComposerInput.propTypes = { onDownArrow: React.PropTypes.func, - onUploadFileSelected: React.PropTypes.func, + onFilesPasted: React.PropTypes.func, // attempts to confirm currently selected completion, returns whether actually confirmed tryComplete: React.PropTypes.func, diff --git a/src/components/views/rooms/MessageComposerInputOld.js b/src/components/views/rooms/MessageComposerInputOld.js index 378644478c..adc6bc2c91 100644 --- a/src/components/views/rooms/MessageComposerInputOld.js +++ b/src/components/views/rooms/MessageComposerInputOld.js @@ -69,6 +69,9 @@ export default React.createClass({ // The text to use a placeholder in the input box placeholder: React.PropTypes.string.isRequired, + + // callback to handle files pasted into the composer + onFilesPasted: React.PropTypes.func, }, componentWillMount: function() { @@ -439,10 +442,27 @@ export default React.createClass({ this.refs.textarea.focus(); }, + _onPaste: function(ev) { + const items = ev.clipboardData.items; + const files = []; + for (const item of items) { + if (item.kind === 'file') { + files.push(item.getAsFile()); + } + } + if (files.length && this.props.onFilesPasted) { + this.props.onFilesPasted(files); + return true; + } + return false; + }, + render: function() { return (
-