Merge branch 'experimental' into nadonomy/icons

This commit is contained in:
Bruno Windels 2018-12-18 18:22:24 +01:00
commit 1d43712a8f
18 changed files with 207 additions and 120 deletions

View file

@ -19,14 +19,14 @@ limitations under the License.
each with a flex-shrink difference of 4 order of magnitude, each with a flex-shrink difference of 4 order of magnitude,
so they ideally wouldn't affect each other. so they ideally wouldn't affect each other.
lowest category: .mx_RoomSubList lowest category: .mx_RoomSubList
flex:-shrink: 10000000 flex-shrink: 10000000
distribute size of items within the same categery by their size distribute size of items within the same categery by their size
middle category: .mx_RoomSubList.resized-sized middle category: .mx_RoomSubList.resized-sized
flex:-shrink: 1000 flex-shrink: 1000
applied when using the resizer, will have a max-height set to it, applied when using the resizer, will have a max-height set to it,
to limit the size to limit the size
highest category: .mx_RoomSubList.resized-all highest category: .mx_RoomSubList.resized-all
flex:-shrink: 1 flex-shrink: 1
small flex-shrink value (1), is only added if you can drag the resizer so far small flex-shrink value (1), is only added if you can drag the resizer so far
so in practice you can only assign this category if there is enough space. so in practice you can only assign this category if there is enough space.
*/ */
@ -39,7 +39,7 @@ limitations under the License.
} }
.mx_RoomSubList_nonEmpty { .mx_RoomSubList_nonEmpty {
min-height: 76px; min-height: 70px;
.mx_AutoHideScrollbar_offset { .mx_AutoHideScrollbar_offset {
padding-bottom: 4px; padding-bottom: 4px;
@ -154,7 +154,7 @@ limitations under the License.
position: sticky; position: sticky;
left: 0; left: 0;
right: 0; right: 0;
height: 40px; height: 30px;
content: ""; content: "";
display: block; display: block;
z-index: 100; z-index: 100;
@ -162,10 +162,10 @@ limitations under the License.
} }
&.mx_IndicatorScrollbar_topOverflow > .mx_AutoHideScrollbar_offset { &.mx_IndicatorScrollbar_topOverflow > .mx_AutoHideScrollbar_offset {
margin-top: -40px; margin-top: -30px;
} }
&.mx_IndicatorScrollbar_bottomOverflow > .mx_AutoHideScrollbar_offset { &.mx_IndicatorScrollbar_bottomOverflow > .mx_AutoHideScrollbar_offset {
margin-bottom: -40px; margin-bottom: -30px;
} }
&.mx_IndicatorScrollbar_topOverflow::before { &.mx_IndicatorScrollbar_topOverflow::before {

View file

@ -192,9 +192,11 @@ $progressbar-color: #000;
// it has the appearance of a text box so the controls // it has the appearance of a text box so the controls
// appear to be part of the input // appear to be part of the input
:not(.mx_textinput) > input[type=text], .mx_MatrixChat {
:not(.mx_textinput) > input[type=search],
.mx_textinput { :not(.mx_textinput) > input[type=text],
:not(.mx_textinput) > input[type=search],
.mx_textinput {
display: block; display: block;
margin: 9px; margin: 9px;
box-sizing: border-box; box-sizing: border-box;
@ -202,22 +204,25 @@ $progressbar-color: #000;
color: $input-darker-fg-color; color: $input-darker-fg-color;
border-radius: 4px; border-radius: 4px;
border: 1px solid #c1c1c1; border: 1px solid #c1c1c1;
} flex: 0 0 auto;
}
.mx_textinput { .mx_textinput {
display: flex; display: flex;
align-items: center; align-items: center;
}
.mx_textinput > input[type=text], > input[type=text],
.mx_textinput > input[type=search] { > input[type=search] {
border: none; border: none;
flex: 1; flex: 1;
color: inherit; //from .mx_textinput color: inherit; //from .mx_textinput
}
}
} }
input[type=text], input[type=text],
input[type=search] { input[type=search],
input[type=password] {
padding: 9px; padding: 9px;
font-family: $font-family; font-family: $font-family;
font-size: 14px; font-size: 14px;

View file

@ -69,6 +69,7 @@ export default class AutoHideScrollbar extends React.Component {
this.onOverflow = this.onOverflow.bind(this); this.onOverflow = this.onOverflow.bind(this);
this.onUnderflow = this.onUnderflow.bind(this); this.onUnderflow = this.onUnderflow.bind(this);
this._collectContainerRef = this._collectContainerRef.bind(this); this._collectContainerRef = this._collectContainerRef.bind(this);
this._needsOverflowListener = null;
} }
onOverflow() { onOverflow() {
@ -81,36 +82,49 @@ export default class AutoHideScrollbar extends React.Component {
this.containerRef.classList.add("mx_AutoHideScrollbar_underflow"); this.containerRef.classList.add("mx_AutoHideScrollbar_underflow");
} }
_collectContainerRef(ref) { checkOverflow() {
if (ref && !this.containerRef) { if (!this._needsOverflowListener) {
this.containerRef = ref; return;
const needsOverflowListener =
document.body.classList.contains("mx_scrollbar_nooverlay");
if (needsOverflowListener) {
this.containerRef.addEventListener("overflow", this.onOverflow);
this.containerRef.addEventListener("underflow", this.onUnderflow);
} }
if (ref.scrollHeight > ref.clientHeight) { if (this.containerRef.scrollHeight > this.containerRef.clientHeight) {
this.onOverflow(); this.onOverflow();
} else { } else {
this.onUnderflow(); this.onUnderflow();
} }
} }
componentDidUpdate() {
this.checkOverflow();
}
componentDidMount() {
installBodyClassesIfNeeded();
this._needsOverflowListener =
document.body.classList.contains("mx_scrollbar_nooverlay");
if (this._needsOverflowListener) {
this.containerRef.addEventListener("overflow", this.onOverflow);
this.containerRef.addEventListener("underflow", this.onUnderflow);
}
this.checkOverflow();
}
_collectContainerRef(ref) {
if (ref && !this.containerRef) {
this.containerRef = ref;
}
if (this.props.wrappedRef) { if (this.props.wrappedRef) {
this.props.wrappedRef(ref); this.props.wrappedRef(ref);
} }
} }
componentWillUnmount() { componentWillUnmount() {
if (this.containerRef) { if (this._needsOverflowListener && this.containerRef) {
this.containerRef.removeEventListener("overflow", this.onOverflow); this.containerRef.removeEventListener("overflow", this.onOverflow);
this.containerRef.removeEventListener("underflow", this.onUnderflow); this.containerRef.removeEventListener("underflow", this.onUnderflow);
} }
} }
render() { render() {
installBodyClassesIfNeeded();
return (<div return (<div
ref={this._collectContainerRef} ref={this._collectContainerRef}
className={["mx_AutoHideScrollbar", this.props.className].join(" ")} className={["mx_AutoHideScrollbar", this.props.className].join(" ")}

View file

@ -1272,15 +1272,6 @@ export default React.createClass({
<TintableSvg src="img/icons-share.svg" width="16" height="16" /> <TintableSvg src="img/icons-share.svg" width="16" height="16" />
</AccessibleButton>, </AccessibleButton>,
); );
if (this.props.collapsedRhs) {
rightButtons.push(
<AccessibleButton className="mx_GroupHeader_button"
onClick={this._onShowRhsClick} title={_t('Show panel')} key="_maximiseButton"
>
<TintableSvg src="img/maximise.svg" width="10" height="16" />
</AccessibleButton>,
);
}
} }
const rightPanel = !this.props.collapsedRhs ? <RightPanel groupId={this.props.groupId} /> : undefined; const rightPanel = !this.props.collapsedRhs ? <RightPanel groupId={this.props.groupId} /> : undefined;
@ -1311,7 +1302,7 @@ export default React.createClass({
<div className="mx_GroupView_header_rightCol"> <div className="mx_GroupView_header_rightCol">
{ rightButtons } { rightButtons }
</div> </div>
<GroupHeaderButtons /> <GroupHeaderButtons collapsedRhs={this.props.collapsedRhs} />
</div> </div>
<MainSplit collapsedRhs={this.props.collapsedRhs} panel={rightPanel}> <MainSplit collapsedRhs={this.props.collapsedRhs} panel={rightPanel}>
<GeminiScrollbarWrapper className="mx_GroupView_body"> <GeminiScrollbarWrapper className="mx_GroupView_body">

View file

@ -21,41 +21,52 @@ export default class IndicatorScrollbar extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this._collectScroller = this._collectScroller.bind(this); this._collectScroller = this._collectScroller.bind(this);
this._collectScrollerComponent = this._collectScrollerComponent.bind(this);
this.checkOverflow = this.checkOverflow.bind(this); this.checkOverflow = this.checkOverflow.bind(this);
this._scrollElement = null;
this._autoHideScrollbar = null;
} }
_collectScroller(scroller) { _collectScroller(scroller) {
if (scroller && !this._scroller) { if (scroller && !this._scrollElement) {
this._scroller = scroller; this._scrollElement = scroller;
this._scroller.addEventListener("scroll", this.checkOverflow); this._scrollElement.addEventListener("scroll", this.checkOverflow);
this.checkOverflow(); this.checkOverflow();
} }
} }
_collectScrollerComponent(autoHideScrollbar) {
this._autoHideScrollbar = autoHideScrollbar;
}
checkOverflow() { checkOverflow() {
const hasTopOverflow = this._scroller.scrollTop > 0; const hasTopOverflow = this._scrollElement.scrollTop > 0;
const hasBottomOverflow = this._scroller.scrollHeight > const hasBottomOverflow = this._scrollElement.scrollHeight >
(this._scroller.scrollTop + this._scroller.clientHeight); (this._scrollElement.scrollTop + this._scrollElement.clientHeight);
if (hasTopOverflow) { if (hasTopOverflow) {
this._scroller.classList.add("mx_IndicatorScrollbar_topOverflow"); this._scrollElement.classList.add("mx_IndicatorScrollbar_topOverflow");
} else { } else {
this._scroller.classList.remove("mx_IndicatorScrollbar_topOverflow"); this._scrollElement.classList.remove("mx_IndicatorScrollbar_topOverflow");
} }
if (hasBottomOverflow) { if (hasBottomOverflow) {
this._scroller.classList.add("mx_IndicatorScrollbar_bottomOverflow"); this._scrollElement.classList.add("mx_IndicatorScrollbar_bottomOverflow");
} else { } else {
this._scroller.classList.remove("mx_IndicatorScrollbar_bottomOverflow"); this._scrollElement.classList.remove("mx_IndicatorScrollbar_bottomOverflow");
}
if (this._autoHideScrollbar) {
this._autoHideScrollbar.checkOverflow();
} }
} }
componentWillUnmount() { componentWillUnmount() {
if (this._scroller) { if (this._scrollElement) {
this._scroller.removeEventListener("scroll", this.checkOverflow); this._scrollElement.removeEventListener("scroll", this.checkOverflow);
} }
} }
render() { render() {
return (<AutoHideScrollbar wrappedRef={this._collectScroller} {... this.props}> return (<AutoHideScrollbar ref={this._collectScrollerComponent} wrappedRef={this._collectScroller} {... this.props}>
{ this.props.children } { this.props.children }
</AutoHideScrollbar>); </AutoHideScrollbar>);
} }

View file

@ -62,7 +62,7 @@ const LoggedInView = React.createClass({
// Called with the credentials of a registered user (if they were a ROU that // Called with the credentials of a registered user (if they were a ROU that
// transitioned to PWLU) // transitioned to PWLU)
onRegistered: PropTypes.func, onRegistered: PropTypes.func,
collapsedRhs: PropTypes.bool,
teamToken: PropTypes.string, teamToken: PropTypes.string,
// Used by the RoomView to handle joining rooms // Used by the RoomView to handle joining rooms
@ -438,7 +438,7 @@ const LoggedInView = React.createClass({
eventPixelOffset={this.props.initialEventPixelOffset} eventPixelOffset={this.props.initialEventPixelOffset}
key={this.props.currentRoomId || 'roomview'} key={this.props.currentRoomId || 'roomview'}
disabled={this.props.middleDisabled} disabled={this.props.middleDisabled}
collapsedRhs={this.props.collapseRhs} collapsedRhs={this.props.collapsedRhs}
ConferenceHandler={this.props.ConferenceHandler} ConferenceHandler={this.props.ConferenceHandler}
/>; />;
break; break;
@ -488,7 +488,7 @@ const LoggedInView = React.createClass({
page_element = <GroupView page_element = <GroupView
groupId={this.props.currentGroupId} groupId={this.props.currentGroupId}
isNew={this.props.currentGroupIsNew} isNew={this.props.currentGroupIsNew}
collapsedRhs={this.props.collapseRhs} collapsedRhs={this.props.collapsedRhs}
/>; />;
break; break;
} }

View file

@ -41,10 +41,13 @@ export default class MainSplit extends React.Component {
{onResized: this._onResized}, {onResized: this._onResized},
); );
resizer.setClassNames(classNames); resizer.setClassNames(classNames);
const rhsSize = window.localStorage.getItem("mx_rhs_size"); let rhsSize = window.localStorage.getItem("mx_rhs_size");
if (rhsSize !== null) { if (rhsSize !== null) {
resizer.forHandleAt(0).resize(parseInt(rhsSize, 10)); rhsSize = parseInt(rhsSize, 10);
} else {
rhsSize = 350;
} }
resizer.forHandleAt(0).resize(rhsSize);
resizer.attach(); resizer.attach();
this.resizer = resizer; this.resizer = resizer;
@ -55,7 +58,7 @@ export default class MainSplit extends React.Component {
} }
componentDidMount() { componentDidMount() {
if (this.props.panel && !this.collapsedRhs) { if (this.props.panel && !this.props.collapsedRhs) {
this._createResizer(); this._createResizer();
} }
} }

View file

@ -161,7 +161,7 @@ export default React.createClass({
viewUserId: null, viewUserId: null,
collapseLhs: false, collapseLhs: false,
collapseRhs: false, collapsedRhs: window.localStorage.getItem("mx_rhs_collapsed") === "true",
leftDisabled: false, leftDisabled: false,
middleDisabled: false, middleDisabled: false,
rightDisabled: false, rightDisabled: false,
@ -555,7 +555,7 @@ export default React.createClass({
break; break;
case 'view_user': case 'view_user':
// FIXME: ugly hack to expand the RightPanel and then re-dispatch. // FIXME: ugly hack to expand the RightPanel and then re-dispatch.
if (this.state.collapseRhs) { if (this.state.collapsedRhs) {
setTimeout(()=>{ setTimeout(()=>{
dis.dispatch({ dis.dispatch({
action: 'show_right_panel', action: 'show_right_panel',
@ -656,13 +656,15 @@ export default React.createClass({
}); });
break; break;
case 'hide_right_panel': case 'hide_right_panel':
window.localStorage.setItem("mx_rhs_collapsed", true);
this.setState({ this.setState({
collapseRhs: true, collapsedRhs: true,
}); });
break; break;
case 'show_right_panel': case 'show_right_panel':
window.localStorage.setItem("mx_rhs_collapsed", false);
this.setState({ this.setState({
collapseRhs: false, collapsedRhs: false,
}); });
break; break;
case 'panel_disable': { case 'panel_disable': {
@ -1217,7 +1219,7 @@ export default React.createClass({
view: VIEWS.LOGIN, view: VIEWS.LOGIN,
ready: false, ready: false,
collapseLhs: false, collapseLhs: false,
collapseRhs: false, collapsedRhs: false,
currentRoomId: null, currentRoomId: null,
page_type: PageTypes.RoomDirectory, page_type: PageTypes.RoomDirectory,
}); });

View file

@ -110,8 +110,9 @@ const RoomSubList = React.createClass({
if (this.isCollapsableOnClick()) { if (this.isCollapsableOnClick()) {
// The header isCollapsable, so the click is to be interpreted as collapse and truncation logic // The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
const isHidden = !this.state.hidden; const isHidden = !this.state.hidden;
this.setState({hidden: isHidden}); this.setState({hidden: isHidden}, () => {
this.props.onHeaderClick(isHidden); this.props.onHeaderClick(isHidden);
});
} else { } else {
// The header is stuck, so the click is to be interpreted as a scroll to the header // The header is stuck, so the click is to be interpreted as a scroll to the header
this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition); this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);

View file

@ -56,7 +56,6 @@ module.exports = React.createClass({
case 'focus_room_filter': case 'focus_room_filter':
if (this.refs.search) { if (this.refs.search) {
this.refs.search.focus(); this.refs.search.focus();
this.refs.search.select();
} }
break; break;
} }
@ -83,6 +82,10 @@ module.exports = React.createClass({
} }
}, },
_onFocus: function(ev) {
ev.target.select();
},
_clearSearch: function(source) { _clearSearch: function(source) {
this.refs.search.value = ""; this.refs.search.value = "";
this.onChange(); this.onChange();
@ -108,6 +111,7 @@ module.exports = React.createClass({
ref="search" ref="search"
className="mx_textinput_icon mx_textinput_search" className="mx_textinput_icon mx_textinput_search"
value={ this.state.searchTerm } value={ this.state.searchTerm }
onFocus={ this._onFocus }
onChange={ this.onChange } onChange={ this.onChange }
onKeyDown={ this._onKeyDown } onKeyDown={ this._onKeyDown }
placeholder={ _t('Filter room names') } placeholder={ _t('Filter room names') }

View file

@ -21,6 +21,7 @@ const ResizeHandle = (props) => {
ResizeHandle.propTypes = { ResizeHandle.propTypes = {
vertical: PropTypes.bool, vertical: PropTypes.bool,
reverse: PropTypes.bool, reverse: PropTypes.bool,
id: PropTypes.string,
}; };
export default ResizeHandle; export default ResizeHandle;

View file

@ -55,23 +55,23 @@ export default class GroupHeaderButtons extends HeaderButtons {
} }
renderButtons() { renderButtons() {
const isPhaseGroup = [ const groupPhases = [
RightPanel.Phase.GroupMemberInfo, RightPanel.Phase.GroupMemberInfo,
RightPanel.Phase.GroupMemberList, RightPanel.Phase.GroupMemberList,
].includes(this.state.phase); ];
const isPhaseRoom = [ const roomPhases = [
RightPanel.Phase.GroupRoomList, RightPanel.Phase.GroupRoomList,
RightPanel.Phase.GroupRoomInfo, RightPanel.Phase.GroupRoomInfo,
].includes(this.state.phase); ];
return [ return [
<HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg" <HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg"
isHighlighted={isPhaseGroup} isHighlighted={this.isPhase(groupPhases)}
clickPhase={RightPanel.Phase.GroupMemberList} clickPhase={RightPanel.Phase.GroupMemberList}
analytics={['Right Panel', 'Group Member List Button', 'click']} analytics={['Right Panel', 'Group Member List Button', 'click']}
/>, />,
<HeaderButton key="_roomsButton" title={_t('Rooms')} iconSrc="img/icons-room-nobg.svg" <HeaderButton key="_roomsButton" title={_t('Rooms')} iconSrc="img/icons-room-nobg.svg"
isHighlighted={isPhaseRoom} isHighlighted={this.isPhase(roomPhases)}
clickPhase={RightPanel.Phase.GroupRoomList} clickPhase={RightPanel.Phase.GroupRoomList}
analytics={['Right Panel', 'Group Room List Button', 'click']} analytics={['Right Panel', 'Group Room List Button', 'click']}
/>, />,

View file

@ -36,6 +36,7 @@ export default class HeaderButton extends React.Component {
dis.dispatch({ dis.dispatch({
action: 'view_right_panel_phase', action: 'view_right_panel_phase',
phase: this.props.clickPhase, phase: this.props.clickPhase,
fromHeader: true,
}); });
} }

View file

@ -18,6 +18,7 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
export default class HeaderButtons extends React.Component { export default class HeaderButtons extends React.Component {
@ -25,7 +26,7 @@ export default class HeaderButtons extends React.Component {
super(props); super(props);
this.state = { this.state = {
phase: initialPhase, phase: props.collapsedRhs ? null : initialPhase,
isUserPrivilegedInGroup: null, isUserPrivilegedInGroup: null,
}; };
this.onAction = this.onAction.bind(this); this.onAction = this.onAction.bind(this);
@ -47,13 +48,44 @@ export default class HeaderButtons extends React.Component {
}, extras)); }, extras));
} }
isPhase(phases) {
if (this.props.collapsedRhs) {
return false;
}
if (Array.isArray(phases)) {
return phases.includes(this.state.phase);
} else {
return phases === this.state.phase;
}
}
onAction(payload) { onAction(payload) {
if (payload.action === "view_right_panel_phase") { if (payload.action === "view_right_panel_phase") {
// only actions coming from header buttons should collapse the right panel
if (this.state.phase === payload.phase && payload.fromHeader) {
dis.dispatch({
action: 'hide_right_panel',
});
this.setState({
phase: null,
});
} else {
if (this.props.collapsedRhs && payload.fromHeader) {
dis.dispatch({
action: 'show_right_panel',
});
// emit payload again as the RightPanel didn't exist up
// till show_right_panel, just without the fromHeader flag
// as that would hide the right panel again
dis.dispatch(Object.assign({}, payload, {fromHeader: false}));
}
this.setState({ this.setState({
phase: payload.phase, phase: payload.phase,
}); });
} }
} }
}
render() { render() {
// inline style as this will be swapped around in future commits // inline style as this will be swapped around in future commits
@ -62,3 +94,7 @@ export default class HeaderButtons extends React.Component {
</div>; </div>;
} }
} }
HeaderButtons.propTypes = {
collapsedRhs: PropTypes.bool,
};

View file

@ -46,24 +46,24 @@ export default class RoomHeaderButtons extends HeaderButtons {
} }
renderButtons() { renderButtons() {
const isMembersPhase = [ const membersPhases = [
RightPanel.Phase.RoomMemberList, RightPanel.Phase.RoomMemberList,
RightPanel.Phase.RoomMemberInfo, RightPanel.Phase.RoomMemberInfo,
].includes(this.state.phase); ];
return [ return [
<HeaderButton key="_membersButton" title={_t('Members')} iconSrc="img/feather-icons/user.svg" <HeaderButton key="_membersButton" title={_t('Members')} iconSrc="img/feather-icons/user.svg"
isHighlighted={isMembersPhase} isHighlighted={this.isPhase(membersPhases)}
clickPhase={RightPanel.Phase.RoomMemberList} clickPhase={RightPanel.Phase.RoomMemberList}
analytics={['Right Panel', 'Member List Button', 'click']} analytics={['Right Panel', 'Member List Button', 'click']}
/>, />,
<HeaderButton key="_filesButton" title={_t('Files')} iconSrc="img/feather-icons/files.svg" <HeaderButton key="_filesButton" title={_t('Files')} iconSrc="img/feather-icons/files.svg"
isHighlighted={this.state.phase === RightPanel.Phase.FilePanel} isHighlighted={this.isPhase(RightPanel.Phase.FilePanel)}
clickPhase={RightPanel.Phase.FilePanel} clickPhase={RightPanel.Phase.FilePanel}
analytics={['Right Panel', 'File List Button', 'click']} analytics={['Right Panel', 'File List Button', 'click']}
/>, />,
<HeaderButton key="_notifsButton" title={_t('Notifications')} iconSrc="img/feather-icons/notifications.svg" <HeaderButton key="_notifsButton" title={_t('Notifications')} iconSrc="img/feather-icons/notifications.svg"
isHighlighted={this.state.phase === RightPanel.Phase.NotificationPanel} isHighlighted={this.isPhase(RightPanel.Phase.NotificationPanel)}
clickPhase={RightPanel.Phase.NotificationPanel} clickPhase={RightPanel.Phase.NotificationPanel}
analytics={['Right Panel', 'Notification List Button', 'click']} analytics={['Right Panel', 'Notification List Button', 'click']}
/>, />,

View file

@ -23,7 +23,6 @@ import sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from "../../../Modal"; import Modal from "../../../Modal";
import dis from "../../../dispatcher";
import RateLimitedFunc from '../../../ratelimitedfunc'; import RateLimitedFunc from '../../../ratelimitedfunc';
import * as linkify from 'linkifyjs'; import * as linkify from 'linkifyjs';
@ -146,10 +145,6 @@ module.exports = React.createClass({
MatrixClientPeg.get().sendStateEvent(this.props.room.roomId, 'm.room.avatar', {url: null}, ''); MatrixClientPeg.get().sendStateEvent(this.props.room.roomId, 'm.room.avatar', {url: null}, '');
}, },
onShowRhsClick: function(ev) {
dis.dispatch({ action: 'show_right_panel' });
},
onShareRoomClick: function(ev) { onShareRoomClick: function(ev) {
const ShareDialog = sdk.getComponent("dialogs.ShareDialog"); const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
Modal.createTrackedDialog('share room dialog', '', ShareDialog, { Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
@ -394,14 +389,6 @@ module.exports = React.createClass({
</AccessibleButton>; </AccessibleButton>;
} }
let rightPanelButtons;
if (this.props.collapsedRhs) {
rightPanelButtons =
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_showPanel" onClick={this.onShowRhsClick} title={_t('Show panel')}>
<TintableSvg src="img/maximise.svg" width="10" height="16" />
</AccessibleButton>;
}
let rightRow; let rightRow;
let manageIntegsButton; let manageIntegsButton;
if (this.props.room && this.props.room.roomId && this.props.inRoom) { if (this.props.room && this.props.room.roomId && this.props.inRoom) {
@ -419,7 +406,6 @@ module.exports = React.createClass({
{ manageIntegsButton } { manageIntegsButton }
{ forgetButton } { forgetButton }
{ searchButton } { searchButton }
{ rightPanelButtons }
</div>; </div>;
} }
@ -433,7 +419,7 @@ module.exports = React.createClass({
{ saveButton } { saveButton }
{ cancelButton } { cancelButton }
{ rightRow } { rightRow }
<RoomHeaderButtons /> <RoomHeaderButtons collapsedRhs={this.props.collapsedRhs} />
</div> </div>
</div> </div>
); );

View file

@ -152,6 +152,8 @@ module.exports = React.createClass({
} }
this.subListSizes[id] = newSize; this.subListSizes[id] = newSize;
window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.subListSizes)); window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.subListSizes));
// update overflow indicators
this._checkSubListsOverflow();
}, },
componentDidMount: function() { componentDidMount: function() {
@ -167,12 +169,10 @@ module.exports = React.createClass({
}); });
// load stored sizes // load stored sizes
Object.entries(this.subListSizes).forEach(([id, size]) => { Object.keys(this.subListSizes).forEach((key) => {
const handle = this.resizer.forHandleWithId(id); this._restoreSubListSize(key);
if (handle) {
handle.resize(size);
}
}); });
this._checkSubListsOverflow();
this.resizer.attach(); this.resizer.attach();
this.mounted = true; this.mounted = true;
@ -181,7 +181,11 @@ module.exports = React.createClass({
componentDidUpdate: function(prevProps) { componentDidUpdate: function(prevProps) {
this._repositionIncomingCallBox(undefined, false); this._repositionIncomingCallBox(undefined, false);
if (this.props.searchFilter !== prevProps.searchFilter) { if (this.props.searchFilter !== prevProps.searchFilter) {
Object.values(this._subListRefs).forEach(l => l.checkOverflow()); // restore sizes
Object.keys(this.subListSizes).forEach((key) => {
this._restoreSubListSize(key);
});
this._checkSubListsOverflow();
} }
}, },
@ -354,6 +358,11 @@ module.exports = React.createClass({
// Do this here so as to not render every time the selected tags // Do this here so as to not render every time the selected tags
// themselves change. // themselves change.
selectedTags: TagOrderStore.getSelectedTags(), selectedTags: TagOrderStore.getSelectedTags(),
}, () => {
// we don't need to restore any size here, do we?
// i guess we could have triggered a new group to appear
// that already an explicit size the last time it appeared ...
this._checkSubListsOverflow();
}); });
// this._lastRefreshRoomListTs = Date.now(); // this._lastRefreshRoomListTs = Date.now();
@ -485,9 +494,30 @@ module.exports = React.createClass({
(filter[0] === '#' && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter)))); (filter[0] === '#' && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter))));
}, },
_persistCollapsedState: function(key, collapsed) { _handleCollapsedState: function(key, collapsed) {
// persist collapsed state
this.collapsedState[key] = collapsed; this.collapsedState[key] = collapsed;
window.localStorage.setItem("mx_roomlist_collapsed", JSON.stringify(this.collapsedState)); window.localStorage.setItem("mx_roomlist_collapsed", JSON.stringify(this.collapsedState));
// load the persisted size configuration of the expanded sub list
if (!collapsed) {
this._restoreSubListSize(key);
}
// check overflow, as sub lists sizes have changed
// important this happens after calling resize above
this._checkSubListsOverflow();
},
_restoreSubListSize(key) {
const size = this.subListSizes[key];
const handle = this.resizer.forHandleWithId(key);
if (handle) {
handle.resize(size);
}
},
// check overflow for scroll indicator gradient
_checkSubListsOverflow() {
Object.values(this._subListRefs).forEach(l => l.checkOverflow());
}, },
_subListRef: function(key, ref) { _subListRef: function(key, ref) {
@ -520,7 +550,7 @@ module.exports = React.createClass({
const {key, label, onHeaderClick, ... otherProps} = props; const {key, label, onHeaderClick, ... otherProps} = props;
const chosenKey = key || label; const chosenKey = key || label;
const onSubListHeaderClick = (collapsed) => { const onSubListHeaderClick = (collapsed) => {
this._persistCollapsedState(chosenKey, collapsed); this._handleCollapsedState(chosenKey, collapsed);
if (onHeaderClick) { if (onHeaderClick) {
onHeaderClick(collapsed); onHeaderClick(collapsed);
} }

View file

@ -84,8 +84,10 @@ export class Resizer {
} }
_onMouseDown(event) { _onMouseDown(event) {
const target = event.target; // use closest in case the resize handle contains
if (!this._isResizeHandle(target) || target.parentElement !== this.container) { // child dom nodes that can be the target
const resizeHandle = event.target && event.target.closest(`.${this.classNames.handle}`);
if (!resizeHandle || resizeHandle.parentElement !== this.container) {
return; return;
} }
// prevent starting a drag operation // prevent starting a drag operation
@ -96,7 +98,7 @@ export class Resizer {
this.container.classList.add(this.classNames.resizing); this.container.classList.add(this.classNames.resizing);
} }
const {sizer, distributor} = this._createSizerAndDistributor(target); const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle);
const onMouseMove = (event) => { const onMouseMove = (event) => {
const offset = sizer.offsetFromEvent(event); const offset = sizer.offsetFromEvent(event);