diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index e290843ed4..5cfe456692 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"); @@ -203,6 +204,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._viewRoom(payload.room_id); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 872640f52e..8a865710fa 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) { @@ -272,6 +274,9 @@ module.exports = React.createClass({ } this._updateConfCallNotification(); + + window.addEventListener('resize', this.onResize); + this.onResize(); }, componentDidUpdate: function() { @@ -426,6 +431,10 @@ module.exports = React.createClass({ } var self = this; + self.setState({ + searchInProgress: true + }); + MatrixClientPeg.get().search({ body: { search_categories: { @@ -450,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"]; @@ -470,8 +485,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"); @@ -479,6 +496,10 @@ module.exports = React.createClass({ title: "Search failed", description: error.toString() }); + }).finally(function() { + self.setState({ + searchInProgress: false + }); }); }, @@ -492,10 +513,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; @@ -763,6 +788,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({ @@ -912,6 +945,27 @@ module.exports = React.createClass({ } }, + 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'); @@ -1051,7 +1105,7 @@ module.exports = React.createClass({ aux = ; } else if (this.state.searching) { - aux = ; + aux = ; } var conferenceCallNotification = null; @@ -1071,30 +1125,37 @@ module.exports = React.createClass({ if (this.state.draggingFile) { fileDropTarget =
- Drop File Here
+ Drop File Here
Drop File Here
; } - 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 (
- + + { fileDropTarget }
- + { conferenceCallNotification } { aux }
- { fileDropTarget }
  1. 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 0f5084e7ee..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() { @@ -187,6 +189,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 +206,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 }
    +
    { this.props.room.name }
    + { searchStatus }
    @@ -233,12 +243,24 @@ 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
    ; } + var exit_button; + if (this.props.onLeaveClick) { + exit_button = +
    + Leave room +
    ; + } + header =
    @@ -257,6 +279,7 @@ module.exports = React.createClass({ { video_button } { voice_button } { zoom_button } + { exit_button }
    Search
    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 -