Merge branch 'develop' into matthew/settings
This commit is contained in:
commit
b9ba4475b8
8 changed files with 533 additions and 314 deletions
|
@ -529,6 +529,7 @@ module.exports = React.createClass({
|
|||
|
||||
onHangupClick: function() {
|
||||
var call = CallHandler.getCallForRoom(this.props.room.roomId);
|
||||
//var call = CallHandler.getAnyActiveCall();
|
||||
if (!call) {
|
||||
return;
|
||||
}
|
||||
|
@ -563,6 +564,7 @@ module.exports = React.createClass({
|
|||
|
||||
var callButton, videoCallButton, hangupButton;
|
||||
var call = CallHandler.getCallForRoom(this.props.room.roomId);
|
||||
//var call = CallHandler.getAnyActiveCall();
|
||||
if (this.props.callState && this.props.callState !== 'ended') {
|
||||
hangupButton =
|
||||
<div className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
|
||||
|
|
|
@ -108,8 +108,10 @@ module.exports = React.createClass({
|
|||
// <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
|
||||
|
||||
var searchStatus;
|
||||
if (this.props.searchInfo && this.props.searchInfo.searchTerm) {
|
||||
searchStatus = <div className="mx_RoomHeader_searchStatus"> ({ this.props.searchInfo.searchCount } results)</div>;
|
||||
// don't display the search count until the search completes and
|
||||
// gives us a non-null searchCount.
|
||||
if (this.props.searchInfo && this.props.searchInfo.searchCount !== null) {
|
||||
searchStatus = <div className="mx_RoomHeader_searchStatus"> (~{ this.props.searchInfo.searchCount } results)</div>;
|
||||
}
|
||||
|
||||
name =
|
||||
|
@ -134,7 +136,17 @@ module.exports = React.createClass({
|
|||
if (this.props.onLeaveClick) {
|
||||
leave_button =
|
||||
<div className="mx_RoomHeader_button mx_RoomHeader_leaveButton">
|
||||
<img src="img/leave.svg" title="Leave room" alt="Leave room" width="26" height="20" onClick={this.props.onLeaveClick}/>
|
||||
<img src="img/leave.svg" title="Leave room" alt="Leave room"
|
||||
width="26" height="20" onClick={this.props.onLeaveClick}/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
var forget_button;
|
||||
if (this.props.onForgetClick) {
|
||||
forget_button =
|
||||
<div className="mx_RoomHeader_button mx_RoomHeader_leaveButton">
|
||||
<img src="img/leave.svg" title="Forget room" alt="Forget room"
|
||||
width="26" height="20" onClick={this.props.onForgetClick}/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
@ -152,6 +164,7 @@ module.exports = React.createClass({
|
|||
{cancel_button}
|
||||
{save_button}
|
||||
<div className="mx_RoomHeader_rightRow">
|
||||
{ forget_button }
|
||||
{ leave_button }
|
||||
<div className="mx_RoomHeader_button">
|
||||
<img src="img/search.svg" title="Search" alt="Search" width="21" height="19" onClick={this.props.onSearchClick}/>
|
||||
|
|
|
@ -19,6 +19,7 @@ var React = require("react");
|
|||
var ReactDOM = require("react-dom");
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
var CallHandler = require('../../../CallHandler');
|
||||
var RoomListSorter = require("../../../RoomListSorter");
|
||||
var UnreadStatus = require('../../../UnreadStatus');
|
||||
var dis = require("../../../dispatcher");
|
||||
|
@ -38,13 +39,16 @@ module.exports = React.createClass({
|
|||
getInitialState: function() {
|
||||
return {
|
||||
activityMap: null,
|
||||
isLoadingLeftRooms: false,
|
||||
lists: {},
|
||||
incomingCall: null,
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
var cli = MatrixClientPeg.get();
|
||||
cli.on("Room", this.onRoom);
|
||||
cli.on("deleteRoom", this.onDeleteRoom);
|
||||
cli.on("Room.timeline", this.onRoomTimeline);
|
||||
cli.on("Room.name", this.onRoomName);
|
||||
cli.on("Room.tags", this.onRoomTags);
|
||||
|
@ -66,7 +70,21 @@ module.exports = React.createClass({
|
|||
this.tooltip = payload.tooltip;
|
||||
this._repositionTooltip();
|
||||
if (this.tooltip) this.tooltip.style.display = 'block';
|
||||
break
|
||||
break;
|
||||
case 'call_state':
|
||||
var call = CallHandler.getCall(payload.room_id);
|
||||
if (call && call.call_state === 'ringing') {
|
||||
this.setState({
|
||||
incomingCall: call
|
||||
});
|
||||
this._repositionIncomingCallBox(undefined, true);
|
||||
}
|
||||
else {
|
||||
this.setState({
|
||||
incomingCall: null
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -88,7 +106,26 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onRoom: function(room) {
|
||||
this.refreshRoomList();
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onDeleteRoom: function(roomId) {
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onArchivedHeaderClick: function(isHidden) {
|
||||
if (!isHidden) {
|
||||
var self = this;
|
||||
this.setState({ isLoadingLeftRooms: true });
|
||||
// we don't care about the response since it comes down via "Room"
|
||||
// events.
|
||||
MatrixClientPeg.get().syncLeftRooms().catch(function(err) {
|
||||
console.error("Failed to sync left rooms: %s", err);
|
||||
console.error(err);
|
||||
}).finally(function() {
|
||||
self.setState({ isLoadingLeftRooms: false });
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onRoomTimeline: function(ev, room, toStartOfTimeline) {
|
||||
|
@ -127,22 +164,57 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onRoomName: function(room) {
|
||||
this.refreshRoomList();
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onRoomTags: function(event, room) {
|
||||
this.refreshRoomList();
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onRoomStateEvents: function(ev, state) {
|
||||
setTimeout(this.refreshRoomList, 0);
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onRoomMemberName: function(ev, member) {
|
||||
setTimeout(this.refreshRoomList, 0);
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
_delayedRefreshRoomList: function() {
|
||||
// There can be 1000s of JS SDK events when rooms are initially synced;
|
||||
// we don't want to do lots of work rendering until things have settled.
|
||||
// Therefore, keep a 1s refresh buffer which will refresh the room list
|
||||
// at MOST once every 1s to prevent thrashing.
|
||||
var MAX_REFRESH_INTERVAL_MS = 1000;
|
||||
var self = this;
|
||||
|
||||
if (!self._lastRefreshRoomListTs) {
|
||||
self.refreshRoomList(); // first refresh evar
|
||||
}
|
||||
else {
|
||||
var timeWaitedMs = Date.now() - self._lastRefreshRoomListTs;
|
||||
if (timeWaitedMs > MAX_REFRESH_INTERVAL_MS) {
|
||||
clearTimeout(self._refreshRoomListTimerId);
|
||||
self._refreshRoomListTimerId = null;
|
||||
self.refreshRoomList(); // refreshed more than MAX_REFRESH_INTERVAL_MS ago
|
||||
}
|
||||
else {
|
||||
// refreshed less than MAX_REFRESH_INTERVAL_MS ago, wait the difference
|
||||
// if we aren't already waiting. If we are waiting then NOP, it will
|
||||
// fire soon, promise!
|
||||
if (!self._refreshRoomListTimerId) {
|
||||
self._refreshRoomListTimerId = setTimeout(function() {
|
||||
self.refreshRoomList();
|
||||
}, 10 + MAX_REFRESH_INTERVAL_MS - timeWaitedMs); // 10 is a buffer amount
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
refreshRoomList: function() {
|
||||
// console.log("DEBUG: Refresh room list delta=%s ms",
|
||||
// (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
|
||||
// );
|
||||
|
||||
// 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
|
||||
|
@ -150,6 +222,7 @@ module.exports = React.createClass({
|
|||
// 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();
|
||||
},
|
||||
|
||||
getRoomLists: function() {
|
||||
|
@ -168,9 +241,12 @@ module.exports = React.createClass({
|
|||
if (me && me.membership == "invite") {
|
||||
s.lists["im.vector.fake.invite"].push(room);
|
||||
}
|
||||
else if (me && me.membership === "leave") {
|
||||
s.lists["im.vector.fake.archived"].push(room);
|
||||
}
|
||||
else {
|
||||
var shouldShowRoom = (
|
||||
me && (me.membership == "join")
|
||||
me && (me.membership == "join" || me.membership === "ban")
|
||||
);
|
||||
|
||||
// hiding conf rooms only ever toggles shouldShowRoom to false
|
||||
|
@ -212,10 +288,58 @@ module.exports = React.createClass({
|
|||
return s;
|
||||
},
|
||||
|
||||
_getScrollNode: function() {
|
||||
var panel = ReactDOM.findDOMNode(this);
|
||||
if (!panel) return null;
|
||||
|
||||
if (panel.classList.contains('gm-prevented')) {
|
||||
return panel;
|
||||
} else {
|
||||
return panel.children[2]; // XXX: Fragile!
|
||||
}
|
||||
},
|
||||
|
||||
_repositionTooltips: function(e) {
|
||||
this._repositionTooltip(e);
|
||||
this._repositionIncomingCallBox(e, false);
|
||||
},
|
||||
|
||||
_repositionTooltip: function(e) {
|
||||
if (this.tooltip && this.tooltip.parentElement) {
|
||||
var scroll = ReactDOM.findDOMNode(this);
|
||||
this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - scroll.children[2].scrollTop) + "px";
|
||||
this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - this._getScrollNode().scrollTop) + "px";
|
||||
}
|
||||
},
|
||||
|
||||
_repositionIncomingCallBox: function(e, firstTime) {
|
||||
var incomingCallBox = document.getElementById("incomingCallBox");
|
||||
if (incomingCallBox && incomingCallBox.parentElement) {
|
||||
var scroll = this._getScrollNode();
|
||||
var top = (scroll.offsetTop + incomingCallBox.parentElement.offsetTop - scroll.scrollTop);
|
||||
|
||||
if (firstTime) {
|
||||
// scroll to make sure the callbox is on the screen...
|
||||
if (top < 10) { // 10px of vertical margin at top of screen
|
||||
scroll.scrollTop = incomingCallBox.parentElement.offsetTop - 10;
|
||||
}
|
||||
else if (top > scroll.clientHeight - incomingCallBox.offsetHeight + 50) {
|
||||
scroll.scrollTop = incomingCallBox.parentElement.offsetTop - scroll.offsetHeight + incomingCallBox.offsetHeight - 50;
|
||||
}
|
||||
// recalculate top in case we clipped it.
|
||||
top = (scroll.offsetTop + incomingCallBox.parentElement.offsetTop - scroll.scrollTop);
|
||||
}
|
||||
else {
|
||||
// stop the box from scrolling off the screen
|
||||
if (top < 10) {
|
||||
top = 10;
|
||||
}
|
||||
else if (top > scroll.clientHeight - incomingCallBox.offsetHeight + 50) {
|
||||
top = scroll.clientHeight - incomingCallBox.offsetHeight + 50;
|
||||
}
|
||||
}
|
||||
|
||||
incomingCallBox.style.top = top + "px";
|
||||
incomingCallBox.style.left = scroll.offsetLeft + scroll.offsetWidth + "px";
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -234,7 +358,7 @@ module.exports = React.createClass({
|
|||
var self = this;
|
||||
|
||||
return (
|
||||
<GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={self._repositionTooltip}>
|
||||
<GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={ self._repositionTooltips }>
|
||||
<div className="mx_RoomList">
|
||||
{ expandButton }
|
||||
|
||||
|
@ -244,6 +368,7 @@ module.exports = React.createClass({
|
|||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['m.favourite'] }
|
||||
|
@ -254,6 +379,7 @@ module.exports = React.createClass({
|
|||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.recent'] }
|
||||
|
@ -263,6 +389,7 @@ module.exports = React.createClass({
|
|||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
{ Object.keys(self.state.lists).map(function(tagName) {
|
||||
|
@ -276,6 +403,7 @@ module.exports = React.createClass({
|
|||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
}
|
||||
|
@ -287,19 +415,23 @@ module.exports = React.createClass({
|
|||
verb="demote"
|
||||
editable={ true }
|
||||
order="recent"
|
||||
bottommost={ self.state.lists['im.vector.fake.archived'].length === 0 }
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.archived'] }
|
||||
label="Historical"
|
||||
editable={ false }
|
||||
order="recent"
|
||||
bottommost={ true }
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
collapsed={ self.props.collapsed } />
|
||||
collapsed={ self.props.collapsed }
|
||||
alwaysShowHeader={ true }
|
||||
startAsHidden={ true }
|
||||
showSpinner={ self.state.isLoadingLeftRooms }
|
||||
onHeaderClick= { self.onArchivedHeaderClick }
|
||||
incomingCall={ self.state.incomingCall } />
|
||||
</div>
|
||||
</GeminiScrollbar>
|
||||
);
|
||||
|
|
|
@ -38,6 +38,7 @@ module.exports = React.createClass({
|
|||
highlight: React.PropTypes.bool.isRequired,
|
||||
isInvite: React.PropTypes.bool.isRequired,
|
||||
roomSubList: React.PropTypes.object.isRequired,
|
||||
incomingCall: React.PropTypes.object,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -105,6 +106,12 @@ module.exports = React.createClass({
|
|||
label = <RoomTooltip room={this.props.room}/>;
|
||||
}
|
||||
|
||||
var incomingCallBox;
|
||||
if (this.props.incomingCall) {
|
||||
var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
|
||||
incomingCallBox = <IncomingCallBox incomingCall={ this.props.incomingCall }/>;
|
||||
}
|
||||
|
||||
var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
|
||||
|
||||
// These props are injected by React DnD,
|
||||
|
@ -120,6 +127,7 @@ module.exports = React.createClass({
|
|||
{ badge }
|
||||
</div>
|
||||
{ label }
|
||||
{ incomingCallBox }
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue