From 77464c669fabafe01ffe2a105fa6ba53101df44c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 11 Dec 2015 00:40:28 +0000 Subject: [PATCH 1/9] switch to fake tag names for fake tags, and highlight new invites --- src/components/views/rooms/RoomList.js | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 2ff9541a07..576c5ee0cf 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -103,8 +103,11 @@ module.exports = React.createClass({ hl = 1; } + var me = room.getMember(MatrixClientPeg.get().credentials.userId); var actions = MatrixClientPeg.get().getPushActionsForEvent(ev); - if (actions && actions.tweaks && actions.tweaks.highlight) { + if ((actions && actions.tweaks && actions.tweaks.highlight) || + (me && me.membership == "invite")) + { hl = 2; } } @@ -153,17 +156,17 @@ module.exports = React.createClass({ var self = this; var s = { lists: {} }; - s.lists["m.invite"] = []; + s.lists["im.vector.fake.invite"] = []; s.lists["m.favourite"] = []; - s.lists["m.recent"] = []; + s.lists["im.vector.fake.recent"] = []; s.lists["m.lowpriority"] = []; - s.lists["m.archived"] = []; + s.lists["im.vector.fake.archived"] = []; MatrixClientPeg.get().getRooms().forEach(function(room) { var me = room.getMember(MatrixClientPeg.get().credentials.userId); if (me && me.membership == "invite") { - s.lists["m.invite"].push(room); + s.lists["im.vector.fake.invite"].push(room); } else { var shouldShowRoom = ( @@ -196,13 +199,13 @@ module.exports = React.createClass({ } } else { - s.lists["m.recent"].push(room); + s.lists["im.vector.fake.recent"].push(room); } } } }); - //console.log("calculated new roomLists; m.recent = " + s.lists["m.recent"]); + //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. @@ -235,7 +238,7 @@ module.exports = React.createClass({
{ expandButton } - - { Object.keys(self.state.lists).map(function(tagName) { - if (!tagName.match(/^m\.(invite|favourite|recent|lowpriority|archived)$/)) { + if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|archived))$/)) { return - Date: Fri, 11 Dec 2015 02:25:33 +0000 Subject: [PATCH 2/9] update the UI whilst searching --- src/components/structures/RoomView.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 025e0f1a4f..7f089f6f9b 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -431,6 +431,10 @@ module.exports = React.createClass({ } var self = this; + self.setState({ + searchInProgress: true + }); + MatrixClientPeg.get().search({ body: { search_categories: { @@ -484,6 +488,10 @@ module.exports = React.createClass({ title: "Search failed", description: error.toString() }); + }).finally(function() { + self.setState({ + searchInProgress: false + }); }); }, @@ -951,7 +959,7 @@ module.exports = React.createClass({ aux = ; } else if (this.state.searching) { - aux = ; + aux = ; } var conferenceCallNotification = null; From af3c43ca765294578fd53af5cfcecabe33220140 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 11 Dec 2015 02:58:59 +0000 Subject: [PATCH 3/9] show result counts --- src/components/structures/RoomView.js | 23 ++++++++++++++++++----- src/components/views/rooms/RoomHeader.js | 8 ++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 7f089f6f9b..1dc2440913 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -479,8 +479,10 @@ module.exports = React.createClass({ self.setState({ highlights: highlights, + searchTerm: term, searchResults: data, searchScope: scope, + searchCount: data.search_categories.room_events.count, }); }, function(error) { var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); @@ -505,10 +507,14 @@ module.exports = React.createClass({ var EventTile = sdk.getComponent('rooms.EventTile'); var self = this; - if (this.state.searchResults && - this.state.searchResults.search_categories.room_events.results && - this.state.searchResults.search_categories.room_events.groups) + if (this.state.searchResults) { + if (!this.state.searchResults.search_categories.room_events.results || + !this.state.searchResults.search_categories.room_events.groups) + { + return ret; + } + // XXX: this dance is foul, due to the results API not directly returning sorted results var results = this.state.searchResults.search_categories.room_events.results; var roomIdGroups = this.state.searchResults.search_categories.room_events.groups.room_id; @@ -985,15 +991,22 @@ module.exports = React.createClass({
; } - var messageComposer; + var messageComposer, searchInfo; if (!this.state.searchResults) { messageComposer = } + else { + searchInfo = { + searchTerm : this.state.searchTerm, + searchScope : this.state.searchScope, + searchCount : this.state.searchCount, + } + } return (
-
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 0f5084e7ee..f2ab5ea669 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -187,6 +187,7 @@ module.exports = React.createClass({ } var name = null; + var searchStatus = null; var topic_el = null; var cancel_button = null; var save_button = null; @@ -203,9 +204,16 @@ module.exports = React.createClass({ save_button =
Save Changes
} else { // + + var searchStatus; + if (this.props.searchInfo && this.props.searchInfo.searchTerm) { + searchStatus =
 ({ this.props.searchInfo.searchCount } results)
; + } + name =
{ this.props.room.name }
+ { searchStatus }
From 3b245f01312bc447ad4dc327dd0c8e82c00acf1f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 11 Dec 2015 03:22:44 +0000 Subject: [PATCH 4/9] discard stale search results --- src/components/structures/RoomView.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 1dc2440913..4c2bbfa8d1 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -459,6 +459,12 @@ module.exports = React.createClass({ } } }).then(function(data) { + + if (!self.state.searching || term !== self.refs.search_bar.refs.search_term.value) { + console.error("Discarding stale search results"); + return; + } + // for debugging: // data.search_categories.room_events.highlights = ["hello", "everybody"]; From 0beafeddb9e45bc3d4ce0f8f3510515f18b54b51 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 11 Dec 2015 15:01:16 +0000 Subject: [PATCH 5/9] Use the 'muted' icon when the mic is muted during a call --- src/components/views/rooms/RoomHeader.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index f2ab5ea669..819896db7b 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -241,9 +241,13 @@ module.exports = React.createClass({
Video call
; + var img = "img/voip.png"; + if (activeCall.isMicrophoneMuted()) { + img = "img/voip-mute.png"; + } voice_button =
- VoIP call + VoIP call
; } From b78add39f492d995c76f4a9fd41b7feba51e5f3b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 12 Dec 2015 16:35:40 +0000 Subject: [PATCH 6/9] fix d&d ui --- src/components/structures/RoomView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 4c2bbfa8d1..15d1ac3a53 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -991,7 +991,7 @@ module.exports = React.createClass({ if (this.state.draggingFile) { fileDropTarget =
- Drop File Here
+ Drop File Here
Drop File Here
; @@ -1014,6 +1014,7 @@ module.exports = React.createClass({
+ { fileDropTarget }
{ conferenceCallNotification } @@ -1021,7 +1022,6 @@ module.exports = React.createClass({
- { fileDropTarget }
  1. From bf70376d169ce80f107153ceffa732bb84d83583 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 12 Dec 2015 17:29:53 +0000 Subject: [PATCH 7/9] hoverover for full name of room --- 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 819896db7b..f5c7016bf5 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -212,7 +212,7 @@ module.exports = React.createClass({ name =
    -
    { this.props.room.name }
    +
    { this.props.room.name }
    { searchStatus }
    From 43ab6074c99a7290c260f5eff26930398e24d621 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Dec 2015 04:32:48 +0000 Subject: [PATCH 8/9] sacrifice a herd of goats to correctly size the remote video element when on a call. turns out flexbox isn't smart enough to let us say that the minimum height of the element should be the (scaled) intrinsic height of the video stream, and that the max height should be ~80% of the height of the normal timeline. so we fudge it with JS instead, which turns out to work fine. after a lot of poking at flexbox i'm fairly convinced this is a fundamental limitation: the implicit height of the video tag can only be used as the minimum height of the auxPanel if you give up on the flexbox shrink/grow being able to constrain it too. there's a chance I made a mistake, but whatever, this works well enough. --- src/components/structures/RoomView.js | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 15d1ac3a53..5bad92bf3a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -58,7 +58,7 @@ module.exports = React.createClass({ searching: false, searchResults: null, syncState: MatrixClientPeg.get().getSyncState(), - hasUnsentMessages: this._hasUnsentMessages(room) + hasUnsentMessages: this._hasUnsentMessages(room), } }, @@ -90,6 +90,8 @@ module.exports = React.createClass({ MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange); } + + window.removeEventListener('resize', this.onResize); }, onAction: function(payload) { @@ -277,6 +279,9 @@ module.exports = React.createClass({ } this._updateConfCallNotification(); + + window.addEventListener('resize', this.onResize); + this.onResize(); }, componentDidUpdate: function() { @@ -832,6 +837,27 @@ module.exports = React.createClass({ scrollNode.scrollTop = scrollNode.scrollHeight; }, + onResize: function(e) { + // It seems flexbox doesn't give us a way to constrain the auxPanel height to have + // a minimum of the height of the video element, whilst also capping it from pushing out the page + // so we have to do it via JS instead. In this implementation we cap the height by putting + // a maxHeight on the underlying remote video tag. + var auxPanelMaxHeight; + if (this.refs.callView) { + // XXX: don't understand why we have to call findDOMNode here in react 0.14 - it should already be a DOM node. + var video = ReactDOM.findDOMNode(this.refs.callView.refs.video.refs.remote); + + // header + footer + status + give us at least 100px of scrollback at all times. + auxPanelMaxHeight = window.innerHeight - (83 + 72 + 36 + 100); + + // XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway + // but it's better than the video going missing entirely + if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50; + + video.style.maxHeight = auxPanelMaxHeight + "px"; + } + }, + render: function() { var RoomHeader = sdk.getComponent('rooms.RoomHeader'); var MessageComposer = sdk.getComponent('rooms.MessageComposer'); @@ -1016,7 +1042,7 @@ module.exports = React.createClass({ onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} /> { fileDropTarget }
    - + { conferenceCallNotification } { aux }
    From 6ad6ed2a4930cb3dcc14fe5e026a5270ca4f127a Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Dec 2015 13:49:28 +0000 Subject: [PATCH 9/9] factor out room-leaving code into MatrixChat for now, and add a dedicated leave button in to the header bar for now --- src/components/structures/MatrixChat.js | 31 +++++++++++++++++++++++ src/components/structures/RoomView.js | 12 +++++++-- src/components/views/rooms/MemberInfo.js | 32 +++--------------------- src/components/views/rooms/MemberTile.js | 30 +++------------------- src/components/views/rooms/RoomHeader.js | 11 ++++++++ 5 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 25f25e919e..04a567d18f 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -29,6 +29,7 @@ var Login = require("./login/Login"); var Registration = require("./login/Registration"); var PostRegistration = require("./login/PostRegistration"); +var Modal = require("../../Modal"); var sdk = require('../../index'); var MatrixTools = require('../../MatrixTools'); var linkifyMatrix = require("../../linkify-matrix"); @@ -200,6 +201,36 @@ module.exports = React.createClass({ self.setState({errorText: 'Login failed.'}); }); + break; + case 'leave_room': + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + + var roomId = payload.room_id; + Modal.createDialog(QuestionDialog, { + title: "Leave room", + description: "Are you sure you want to leave the room?", + onFinished: function(should_leave) { + if (should_leave) { + var d = MatrixClientPeg.get().leave(roomId); + + // FIXME: controller shouldn't be loading a view :( + var Loader = sdk.getComponent("elements.Spinner"); + var modal = Modal.createDialog(Loader); + + d.then(function() { + modal.close(); + dis.dispatch({action: 'view_next_room'}); + }, function(err) { + modal.close(); + Modal.createDialog(ErrorDialog, { + title: "Failed to leave room", + description: err.toString() + }); + }); + } + } + }); break; case 'view_room': this.focusComposer = true; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 5bad92bf3a..5a87508d68 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -793,6 +793,14 @@ module.exports = React.createClass({ this.setState(this.getInitialState()); }, + onLeaveClick: function() { + dis.dispatch({ + action: 'leave_room', + room_id: this.props.roomId, + }); + this.props.onFinished(); + }, + onRejectButtonClicked: function(ev) { var self = this; this.setState({ @@ -853,7 +861,7 @@ module.exports = React.createClass({ // XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway // but it's better than the video going missing entirely if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50; - + video.style.maxHeight = auxPanelMaxHeight + "px"; } }, @@ -1039,7 +1047,7 @@ module.exports = React.createClass({ return (
    + onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} onLeaveClick={this.onLeaveClick} /> { fileDropTarget }
    diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 1478ce97a0..bf17195664 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -226,36 +226,10 @@ module.exports = React.createClass({ } }, - // FIXME: this is horribly duplicated with MemberTile's onLeaveClick. - // Not sure what the right solution to this is. onLeaveClick: function() { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - - var roomId = this.props.member.roomId; - Modal.createDialog(QuestionDialog, { - title: "Leave room", - description: "Are you sure you want to leave the room?", - onFinished: function(should_leave) { - if (should_leave) { - var d = MatrixClientPeg.get().leave(roomId); - - // FIXME: controller shouldn't be loading a view :( - var Loader = sdk.getComponent("elements.Spinner"); - var modal = Modal.createDialog(Loader); - - d.then(function() { - modal.close(); - dis.dispatch({action: 'view_next_room'}); - }, function(err) { - modal.close(); - Modal.createDialog(ErrorDialog, { - title: "Failed to leave room", - description: err.toString() - }); - }); - } - } + dis.dispatch({ + action: 'leave_room', + room_id: this.props.member.roomId, }); this.props.onFinished(); }, diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 4d99529d98..38d9285c0c 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -31,33 +31,11 @@ module.exports = React.createClass({ }, onLeaveClick: function() { - var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - - var roomId = this.props.member.roomId; - Modal.createDialog(QuestionDialog, { - title: "Leave room", - description: "Are you sure you want to leave the room?", - onFinished: function(should_leave) { - if (should_leave) { - var d = MatrixClientPeg.get().leave(roomId); - - // FIXME: controller shouldn't be loading a view :( - var Loader = sdk.getComponent("elements.Spinner"); - var modal = Modal.createDialog(Loader); - - d.then(function() { - modal.close(); - dis.dispatch({action: 'view_next_room'}); - }, function(err) { - modal.close(); - Modal.createDialog(ErrorDialog, { - title: "Failed to leave room", - description: err.toString() - }); - }); - } - } + dis.dispatch({ + action: 'leave_room', + room_id: this.props.member.roomId, }); + this.props.onFinished(); }, shouldComponentUpdate: function(nextProps, nextState) { diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index f5c7016bf5..07b2521b27 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -35,6 +35,8 @@ module.exports = React.createClass({ editing: React.PropTypes.bool, onSettingsClick: React.PropTypes.func, onSaveClick: React.PropTypes.func, + onSearchClick: React.PropTypes.func, + onLeaveClick: React.PropTypes.func, }, getDefaultProps: function() { @@ -251,6 +253,14 @@ module.exports = React.createClass({
    ; } + var exit_button; + if (this.props.onLeaveClick) { + exit_button = +
    + Leave room +
    ; + } + header =
    @@ -269,6 +279,7 @@ module.exports = React.createClass({ { video_button } { voice_button } { zoom_button } + { exit_button }
    Search