2018-04-11 22:58:04 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015, 2016 OpenMarket 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
import React from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import { MatrixClient } from 'matrix-js-sdk';
|
2018-04-12 23:43:44 +00:00
|
|
|
import { KeyCode } from '../../Keyboard';
|
|
|
|
import sdk from '../../index';
|
|
|
|
import dis from '../../dispatcher';
|
2018-04-11 22:58:04 +00:00
|
|
|
import VectorConferenceHandler from '../../VectorConferenceHandler';
|
|
|
|
|
2018-04-12 23:43:44 +00:00
|
|
|
import SettingsStore from '../../settings/SettingsStore';
|
2018-04-11 22:58:04 +00:00
|
|
|
|
|
|
|
|
2018-10-27 03:50:35 +00:00
|
|
|
const LeftPanel = React.createClass({
|
2018-04-11 22:58:04 +00:00
|
|
|
displayName: 'LeftPanel',
|
|
|
|
|
|
|
|
// NB. If you add props, don't forget to update
|
|
|
|
// shouldComponentUpdate!
|
|
|
|
propTypes: {
|
|
|
|
collapsed: PropTypes.bool.isRequired,
|
|
|
|
},
|
|
|
|
|
|
|
|
contextTypes: {
|
|
|
|
matrixClient: PropTypes.instanceOf(MatrixClient),
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
searchFilter: '',
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
|
|
|
this.focusedElement = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
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.disabled !== nextProps.disabled
|
|
|
|
) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.state.searchFilter !== nextState.searchFilter) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
_onFocus: function(ev) {
|
|
|
|
this.focusedElement = ev.target;
|
|
|
|
},
|
|
|
|
|
|
|
|
_onBlur: function(ev) {
|
|
|
|
this.focusedElement = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
_onKeyDown: function(ev) {
|
|
|
|
if (!this.focusedElement) return;
|
2018-06-16 07:52:14 +00:00
|
|
|
let handled = true;
|
2018-04-11 22:58:04 +00:00
|
|
|
|
|
|
|
switch (ev.keyCode) {
|
2018-06-16 07:50:53 +00:00
|
|
|
case KeyCode.TAB:
|
|
|
|
this._onMoveFocus(ev.shiftKey);
|
|
|
|
break;
|
2018-04-11 22:58:04 +00:00
|
|
|
case KeyCode.UP:
|
|
|
|
this._onMoveFocus(true);
|
|
|
|
break;
|
|
|
|
case KeyCode.DOWN:
|
|
|
|
this._onMoveFocus(false);
|
|
|
|
break;
|
2018-06-16 07:42:28 +00:00
|
|
|
case KeyCode.ENTER:
|
|
|
|
this._onMoveFocus(false);
|
2018-06-16 07:44:57 +00:00
|
|
|
if (this.focusedElement) {
|
|
|
|
this.focusedElement.click();
|
|
|
|
}
|
2018-06-16 07:42:28 +00:00
|
|
|
break;
|
2018-06-16 07:52:14 +00:00
|
|
|
default:
|
|
|
|
handled = false;
|
2018-04-11 22:58:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (handled) {
|
|
|
|
ev.stopPropagation();
|
|
|
|
ev.preventDefault();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_onMoveFocus: function(up) {
|
2018-06-16 07:42:40 +00:00
|
|
|
let element = this.focusedElement;
|
2018-04-11 22:58:04 +00:00
|
|
|
|
|
|
|
// unclear why this isn't needed
|
|
|
|
// var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending;
|
|
|
|
// this.focusDirection = up;
|
|
|
|
|
2018-06-16 07:42:40 +00:00
|
|
|
let descending = false; // are we currently descending or ascending through the DOM tree?
|
|
|
|
let classes;
|
2018-04-11 22:58:04 +00:00
|
|
|
|
|
|
|
do {
|
2018-06-16 07:42:40 +00:00
|
|
|
const child = up ? element.lastElementChild : element.firstElementChild;
|
|
|
|
const sibling = up ? element.previousElementSibling : element.nextElementSibling;
|
2018-04-11 22:58:04 +00:00
|
|
|
|
|
|
|
if (descending) {
|
|
|
|
if (child) {
|
|
|
|
element = child;
|
2018-06-16 07:42:40 +00:00
|
|
|
} else if (sibling) {
|
2018-04-11 22:58:04 +00:00
|
|
|
element = sibling;
|
2018-06-16 07:42:40 +00:00
|
|
|
} else {
|
2018-04-11 22:58:04 +00:00
|
|
|
descending = false;
|
|
|
|
element = element.parentElement;
|
|
|
|
}
|
2018-06-16 07:42:40 +00:00
|
|
|
} else {
|
2018-04-11 22:58:04 +00:00
|
|
|
if (sibling) {
|
|
|
|
element = sibling;
|
|
|
|
descending = true;
|
2018-06-16 07:42:40 +00:00
|
|
|
} else {
|
2018-04-11 22:58:04 +00:00
|
|
|
element = element.parentElement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element) {
|
|
|
|
classes = element.classList;
|
|
|
|
if (classes.contains("mx_LeftPanel")) { // we hit the top
|
|
|
|
element = up ? element.lastElementChild : element.firstElementChild;
|
|
|
|
descending = true;
|
|
|
|
}
|
|
|
|
}
|
2018-06-16 07:42:40 +00:00
|
|
|
} while (element && !(
|
2018-04-11 22:58:04 +00:00
|
|
|
classes.contains("mx_RoomTile") ||
|
2018-12-20 20:23:06 +00:00
|
|
|
classes.contains("mx_textinput_search")));
|
2018-04-11 22:58:04 +00:00
|
|
|
|
|
|
|
if (element) {
|
|
|
|
element.focus();
|
|
|
|
this.focusedElement = element;
|
|
|
|
this.focusedDescending = descending;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onHideClick: function() {
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'hide_left_panel',
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onSearch: function(term) {
|
|
|
|
this.setState({ searchFilter: term });
|
|
|
|
},
|
|
|
|
|
2018-11-05 08:35:44 +00:00
|
|
|
onSearchCleared: function(source) {
|
|
|
|
if (source === "keyboard") {
|
|
|
|
dis.dispatch({action: 'focus_composer'});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-04-11 22:58:04 +00:00
|
|
|
collectRoomList: function(ref) {
|
|
|
|
this._roomList = ref;
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
const RoomList = sdk.getComponent('rooms.RoomList');
|
|
|
|
const TagPanel = sdk.getComponent('structures.TagPanel');
|
2018-10-23 11:29:44 +00:00
|
|
|
const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton');
|
2018-11-02 14:27:17 +00:00
|
|
|
const SearchBox = sdk.getComponent('structures.SearchBox');
|
2018-04-11 22:58:04 +00:00
|
|
|
const CallPreview = sdk.getComponent('voip.CallPreview');
|
2018-10-26 13:57:57 +00:00
|
|
|
|
2018-04-11 22:58:04 +00:00
|
|
|
const tagPanelEnabled = !SettingsStore.getValue("TagPanel.disableTagPanel");
|
|
|
|
const tagPanel = tagPanelEnabled ? <TagPanel /> : <div />;
|
|
|
|
|
|
|
|
const containerClasses = classNames(
|
|
|
|
"mx_LeftPanel_container", "mx_fadable",
|
|
|
|
{
|
2018-10-16 15:38:34 +00:00
|
|
|
"collapsed": this.props.collapsed,
|
2018-04-11 22:58:04 +00:00
|
|
|
"mx_LeftPanel_container_hasTagPanel": tagPanelEnabled,
|
|
|
|
"mx_fadable_faded": this.props.disabled,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2018-11-02 14:29:18 +00:00
|
|
|
const searchBox = !this.props.collapsed ?
|
2018-11-05 08:35:44 +00:00
|
|
|
<SearchBox onSearch={ this.onSearch } onCleared={ this.onSearchCleared } /> :
|
2018-11-02 14:29:18 +00:00
|
|
|
undefined;
|
|
|
|
|
2018-04-11 22:58:04 +00:00
|
|
|
return (
|
|
|
|
<div className={containerClasses}>
|
|
|
|
{ tagPanel }
|
2018-11-05 08:32:37 +00:00
|
|
|
<aside className={"mx_LeftPanel dark-panel"} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
2018-11-05 11:15:03 +00:00
|
|
|
<TopLeftMenuButton collapsed={ this.props.collapsed } />
|
2018-11-02 14:29:18 +00:00
|
|
|
{ searchBox }
|
2018-04-11 22:58:04 +00:00
|
|
|
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
|
|
|
<RoomList
|
|
|
|
ref={this.collectRoomList}
|
|
|
|
collapsed={this.props.collapsed}
|
|
|
|
searchFilter={this.state.searchFilter}
|
|
|
|
ConferenceHandler={VectorConferenceHandler} />
|
|
|
|
</aside>
|
|
|
|
</div>
|
|
|
|
);
|
2018-05-25 02:17:29 +00:00
|
|
|
// <BottomLeftMenu collapsed={this.props.collapsed}/>
|
2018-10-27 03:50:35 +00:00
|
|
|
},
|
2018-04-11 22:58:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = LeftPanel;
|