Merge remote-tracking branch 'origin/develop' into dbkr/group_userlist

This commit is contained in:
David Baker 2017-09-13 16:35:37 +01:00
commit 06fe240be2
38 changed files with 1083 additions and 184 deletions

View file

@ -30,13 +30,14 @@ import VectorConferenceHandler from '../../VectorConferenceHandler';
var LeftPanel = React.createClass({
displayName: 'LeftPanel',
// NB. If you add props, don't forget to update
// shouldComponentUpdate!
propTypes: {
collapsed: React.PropTypes.bool.isRequired,
},
getInitialState: function() {
return {
showCallElement: null,
searchFilter: '',
};
},
@ -45,26 +46,25 @@ var LeftPanel = React.createClass({
this.focusedElement = null;
},
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
},
componentWillReceiveProps: function(newProps) {
this._recheckCallElement(newProps.selectedRoom);
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
},
onAction: function(payload) {
switch (payload.action) {
// listen for call state changes to prod the render method, which
// may hide the global CallView if the call it is tracking is dead
case 'call_state':
this._recheckCallElement(this.props.selectedRoom);
break;
shouldComponentUpdate: function(nextProps, nextState) {
// MatrixChat will update whenever the user switches
// rooms, but propagating this change all the way down
// the react tree is quite slow, so we cut this off
// here. The RoomTiles listen for the room change
// events themselves to know when to update.
// We just need to update if any of these things change.
if (
this.props.collapsed !== nextProps.collapsed ||
this.props.opacity !== nextProps.opacity
) {
return true;
}
if (this.state.searchFilter !== nextState.searchFilter) {
return true;
}
return false;
},
_onFocus: function(ev) {
@ -152,74 +152,41 @@ var LeftPanel = React.createClass({
}
},
_recheckCallElement: function(selectedRoomId) {
// if we aren't viewing a room with an ongoing call, but there is an
// active call, show the call element - we need to do this to make
// audio/video not crap out
var activeCall = CallHandler.getAnyActiveCall();
var callForRoom = CallHandler.getCallForRoom(selectedRoomId);
var showCall = (activeCall && activeCall.call_state === 'connected' && !callForRoom);
this.setState({
showCallElement: showCall
});
},
onHideClick: function() {
dis.dispatch({
action: 'hide_left_panel',
});
},
onCallViewClick: function() {
var call = CallHandler.getAnyActiveCall();
if (call) {
dis.dispatch({
action: 'view_room',
room_id: call.groupRoomId || call.roomId,
});
}
},
onSearch: function(term) {
this.setState({ searchFilter: term });
},
render: function() {
var RoomList = sdk.getComponent('rooms.RoomList');
var BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
const RoomList = sdk.getComponent('rooms.RoomList');
const BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
const CallPreview = sdk.getComponent('voip.CallPreview');
var topBox;
let topBox;
if (MatrixClientPeg.get().isGuest()) {
var LoginBox = sdk.getComponent('structures.LoginBox');
const LoginBox = sdk.getComponent('structures.LoginBox');
topBox = <LoginBox collapsed={ this.props.collapsed }/>;
}
else {
var SearchBox = sdk.getComponent('structures.SearchBox');
} else {
const SearchBox = sdk.getComponent('structures.SearchBox');
topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />;
}
var classes = "mx_LeftPanel mx_fadable";
let classes = "mx_LeftPanel mx_fadable";
if (this.props.collapsed) {
classes += " collapsed";
}
var callPreview;
if (this.state.showCallElement && !this.props.collapsed) {
var CallView = sdk.getComponent('voip.CallView');
callPreview = (
<CallView
className="mx_LeftPanel_callView" showVoice={true} onClick={this.onCallViewClick}
ConferenceHandler={VectorConferenceHandler} />
);
}
return (
<aside className={classes} style={{ opacity: this.props.opacity }}
onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
{ topBox }
{ callPreview }
<CallPreview ConferenceHandler={VectorConferenceHandler} />
<RoomList
selectedRoom={this.props.selectedRoom}
collapsed={this.props.collapsed}
searchFilter={this.state.searchFilter}
ConferenceHandler={VectorConferenceHandler} />

View file

@ -84,7 +84,7 @@ module.exports = React.createClass({
var self = this;
return (
<div className="mx_SearchBox">
<div className="mx_SearchBox mx_LoginBox">
{ loginButton }
{ toggleCollapse }
</div>

View file

@ -75,8 +75,8 @@ var RoomSubList = React.createClass({
order: React.PropTypes.string.isRequired,
// undefined if no room is selected (eg we are showing settings)
selectedRoom: React.PropTypes.string,
// passed through to RoomTile and used to highlight room with `!` regardless of notifications count
isInvite: React.PropTypes.bool,
startAsHidden: React.PropTypes.bool,
showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded
@ -104,6 +104,7 @@ var RoomSubList = React.createClass({
onHeaderClick: function() {}, // NOP
onShowMoreRooms: function() {}, // NOP
extraTiles: [],
isInvite: false,
};
},
@ -248,7 +249,7 @@ var RoomSubList = React.createClass({
return this.props.list.reduce(function(result, room, index) {
if (truncateAt === undefined || index >= truncateAt) {
var roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
var highlight = room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites';
var highlight = room.getUnreadNotificationCount('highlight') > 0 || self.props.isInvite;
var notificationCount = room.getUnreadNotificationCount();
const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState);
@ -368,7 +369,6 @@ var RoomSubList = React.createClass({
var self = this;
var DNDRoomTile = sdk.getComponent("rooms.DNDRoomTile");
return this.state.sortedList.map(function(room) {
var selected = room.roomId == self.props.selectedRoom;
// XXX: is it evil to pass in self as a prop to RoomTile?
return (
<DNDRoomTile
@ -376,10 +376,9 @@ var RoomSubList = React.createClass({
roomSubList={ self }
key={ room.roomId }
collapsed={ self.props.collapsed || false}
selected={ selected }
unread={ Unread.doesRoomHaveUnreadMessages(room) }
highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites' }
isInvite={ self.props.label === 'Invites' }
highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.isInvite }
isInvite={ self.props.isInvite }
refreshSubList={ self._updateSubListCount }
incomingCall={ null }
onClick={ self.onRoomTileClick }
@ -411,6 +410,9 @@ var RoomSubList = React.createClass({
var badge;
if (subListNotifCount > 0) {
badge = <div className={badgeClasses}>{ FormattingUtils.formatCount(subListNotifCount) }</div>;
} else if (this.props.isInvite) {
// no notifications but highlight anyway because this is an invite badge
badge = <div className={badgeClasses}>!</div>;
}
// When collapsed, allow a long hover on the header to show user

View file

@ -0,0 +1,30 @@
/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
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.
*/
'use strict';
import React from 'react';
import PropTypes from 'prop-types';
export default class GenericTextContextMenu extends React.Component {
static PropTypes = {
message: PropTypes.string.isRequired,
};
render() {
return <div>{ this.props.message }</div>;
}
}

View file

@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import sdk from 'matrix-react-sdk';
import SdkConfig from 'matrix-react-sdk/lib/SdkConfig';
import Modal from 'matrix-react-sdk/lib/Modal';
import { _t } from 'matrix-react-sdk/lib/languageHandler';
export default class BugReportDialog extends React.Component {
@ -64,8 +65,13 @@ export default class BugReportDialog extends React.Component {
progressCallback: this._sendProgressCallback,
}).then(() => {
if (!this._unmounted) {
this.setState({ busy: false, progress: null });
this.props.onFinished(false);
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Bug report sent', '', QuestionDialog, {
title: _t('Bug report sent'),
description: _t('Thank you!'),
hasCancelButton: false,
});
}
}, (err) => {
if (!this._unmounted) {

View file

@ -30,7 +30,7 @@ module.exports = React.createClass({
<div className="mx_Login_links">
<a href="https://medium.com/@RiotChat">blog</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="https://twitter.com/@RiotChat">twitter</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="https://github.com/vector-im/vector-web">github</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="https://github.com/vector-im/riot-web">github</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="https://matrix.org">{ _t('powered by Matrix') }</a>
</div>
);