Merge branch 'new_release_process_stable' into develop
This reverts PRs on riot-web marked as notready, turning the develop branch into a more stable version.pull/3942/head
						commit
						7ea0ecd125
					
				|  | @ -61,6 +61,7 @@ | |||
|     "favico.js": "^0.3.10", | ||||
|     "filesize": "3.5.6", | ||||
|     "flux": "~2.0.3", | ||||
|     "gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279", | ||||
|     "gfm.css": "^1.1.1", | ||||
|     "highlight.js": "^9.0.0", | ||||
|     "linkifyjs": "^2.1.3", | ||||
|  | @ -73,7 +74,7 @@ | |||
|     "react-dnd": "^2.1.4", | ||||
|     "react-dnd-html5-backend": "^2.1.2", | ||||
|     "react-dom": "^15.4.0", | ||||
|     "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#39d858c", | ||||
|     "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef", | ||||
|     "sanitize-html": "^1.11.1", | ||||
|     "ua-parser-js": "^0.7.10", | ||||
|     "url": "^0.11.0" | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| /* | ||||
| Copyright 2015, 2016 OpenMarket Ltd | ||||
| Copyright 2017 Vector Creations Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
|  | @ -15,8 +14,13 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import sdk from 'matrix-react-sdk'; | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var ReactDOM = require('react-dom'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'BottomLeftMenu', | ||||
|  | @ -26,28 +30,121 @@ module.exports = React.createClass({ | |||
|         teamToken: React.PropTypes.string, | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return({ | ||||
|             directoryHover : false, | ||||
|             roomsHover : false, | ||||
|             homeHover: false, | ||||
|             peopleHover : false, | ||||
|             settingsHover : false, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     // Room events
 | ||||
|     onDirectoryClick: function() { | ||||
|         dis.dispatch({ action: 'view_room_directory' }); | ||||
|     }, | ||||
| 
 | ||||
|     onDirectoryMouseEnter: function() { | ||||
|         this.setState({ directoryHover: true }); | ||||
|     }, | ||||
| 
 | ||||
|     onDirectoryMouseLeave: function() { | ||||
|         this.setState({ directoryHover: false }); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomsClick: function() { | ||||
|         dis.dispatch({ action: 'view_create_room' }); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomsMouseEnter: function() { | ||||
|         this.setState({ roomsHover: true }); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomsMouseLeave: function() { | ||||
|         this.setState({ roomsHover: false }); | ||||
|     }, | ||||
| 
 | ||||
|     // Home button events
 | ||||
|     onHomeClick: function() { | ||||
|         dis.dispatch({ action: 'view_home_page' }); | ||||
|     }, | ||||
| 
 | ||||
|     onHomeMouseEnter: function() { | ||||
|         this.setState({ homeHover: true }); | ||||
|     }, | ||||
| 
 | ||||
|     onHomeMouseLeave: function() { | ||||
|         this.setState({ homeHover: false }); | ||||
|     }, | ||||
| 
 | ||||
|     // People events
 | ||||
|     onPeopleClick: function() { | ||||
|         dis.dispatch({ action: 'view_create_chat' }); | ||||
|     }, | ||||
| 
 | ||||
|     onPeopleMouseEnter: function() { | ||||
|         this.setState({ peopleHover: true }); | ||||
|     }, | ||||
| 
 | ||||
|     onPeopleMouseLeave: function() { | ||||
|         this.setState({ peopleHover: false }); | ||||
|     }, | ||||
| 
 | ||||
|     // Settings events
 | ||||
|     onSettingsClick: function() { | ||||
|         dis.dispatch({ action: 'view_user_settings' }); | ||||
|     }, | ||||
| 
 | ||||
|     onSettingsMouseEnter: function() { | ||||
|         this.setState({ settingsHover: true }); | ||||
|     }, | ||||
| 
 | ||||
|     onSettingsMouseLeave: function() { | ||||
|         this.setState({ settingsHover: false }); | ||||
|     }, | ||||
| 
 | ||||
|     // Get the label/tooltip to show
 | ||||
|     getLabel: function(label, show) { | ||||
|         if (show) { | ||||
|             var RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); | ||||
|             return <RoomTooltip className="mx_BottomLeftMenu_tooltip" label={label} />; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         const HomeButton = sdk.getComponent('elements.HomeButton'); | ||||
|         const StartChatButton = sdk.getComponent('elements.StartChatButton'); | ||||
|         const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); | ||||
|         const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); | ||||
|         const SettingsButton = sdk.getComponent('elements.SettingsButton'); | ||||
|         var TintableSvg = sdk.getComponent('elements.TintableSvg'); | ||||
| 
 | ||||
|         var homeButton; | ||||
|         if (this.props.teamToken) { | ||||
|             homeButton = <HomeButton tooltip={true} />; | ||||
|             homeButton = ( | ||||
|                 <AccessibleButton className="mx_BottomLeftMenu_homePage" onClick={ this.onHomeClick } onMouseEnter={ this.onHomeMouseEnter } onMouseLeave={ this.onHomeMouseLeave } > | ||||
|                     <TintableSvg src="img/icons-home.svg" width="25" height="25" /> | ||||
|                     { this.getLabel("Welcome page", this.state.homeHover) } | ||||
|                 </AccessibleButton> | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_BottomLeftMenu"> | ||||
|                 <div className="mx_BottomLeftMenu_options"> | ||||
|                     { homeButton } | ||||
|                     <StartChatButton tooltip={true} /> | ||||
|                     <RoomDirectoryButton tooltip={true} /> | ||||
|                     <CreateRoomButton tooltip={true} /> | ||||
|                     <span className="mx_BottomLeftMenu_settings"> | ||||
|                         <SettingsButton tooltip={true} /> | ||||
|                     </span> | ||||
|                     <AccessibleButton className="mx_BottomLeftMenu_people" onClick={ this.onPeopleClick } onMouseEnter={ this.onPeopleMouseEnter } onMouseLeave={ this.onPeopleMouseLeave } > | ||||
|                         <TintableSvg src="img/icons-people.svg" width="25" height="25" /> | ||||
|                         { this.getLabel("Start chat", this.state.peopleHover) } | ||||
|                     </AccessibleButton> | ||||
|                     <AccessibleButton className="mx_BottomLeftMenu_directory" onClick={ this.onDirectoryClick } onMouseEnter={ this.onDirectoryMouseEnter } onMouseLeave={ this.onDirectoryMouseLeave } > | ||||
|                         <TintableSvg src="img/icons-directory.svg" width="25" height="25"/> | ||||
|                         { this.getLabel("Room directory", this.state.directoryHover) } | ||||
|                     </AccessibleButton> | ||||
|                     <AccessibleButton className="mx_BottomLeftMenu_createRoom" onClick={ this.onRoomsClick } onMouseEnter={ this.onRoomsMouseEnter } onMouseLeave={ this.onRoomsMouseLeave } > | ||||
|                         <TintableSvg src="img/icons-create-room.svg" width="25" height="25" /> | ||||
|                         { this.getLabel("Create new room", this.state.roomsHover) } | ||||
|                     </AccessibleButton> | ||||
|                     <AccessibleButton className="mx_BottomLeftMenu_settings" onClick={ this.onSettingsClick } onMouseEnter={ this.onSettingsMouseEnter } onMouseLeave={ this.onSettingsMouseLeave } > | ||||
|                         <TintableSvg src="img/icons-settings.svg" width="25" height="25" /> | ||||
|                         { this.getLabel("Settings", this.state.settingsHover) } | ||||
|                     </AccessibleButton> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|  |  | |||
|  | @ -19,11 +19,9 @@ limitations under the License. | |||
| var React = require('react'); | ||||
| var DragDropContext = require('react-dnd').DragDropContext; | ||||
| var HTML5Backend = require('react-dnd-html5-backend'); | ||||
| var KeyCode = require('matrix-react-sdk/lib/KeyCode'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| 
 | ||||
| 
 | ||||
| var VectorConferenceHandler = require('../../VectorConferenceHandler'); | ||||
| var CallHandler = require("matrix-react-sdk/lib/CallHandler"); | ||||
| 
 | ||||
|  | @ -42,10 +40,6 @@ var LeftPanel = React.createClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this.focusedElement = null; | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|     }, | ||||
|  | @ -68,91 +62,6 @@ var LeftPanel = React.createClass({ | |||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _onFocus: function(ev) { | ||||
|         this.focusedElement = ev.target; | ||||
|     }, | ||||
| 
 | ||||
|     _onBlur: function(ev) { | ||||
|         this.focusedElement = null; | ||||
|     }, | ||||
| 
 | ||||
|     _onKeyDown: function(ev) { | ||||
|         if (!this.focusedElement) return; | ||||
|         let handled = false; | ||||
| 
 | ||||
|         switch (ev.keyCode) { | ||||
|             case KeyCode.UP: | ||||
|                 this._onMoveFocus(true); | ||||
|                 handled = true; | ||||
|                 break; | ||||
|             case KeyCode.DOWN: | ||||
|                 this._onMoveFocus(false); | ||||
|                 handled = true; | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         if (handled) { | ||||
|             ev.stopPropagation(); | ||||
|             ev.preventDefault(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _onMoveFocus: function(up) { | ||||
|         var element = this.focusedElement; | ||||
| 
 | ||||
|         // unclear why this isn't needed
 | ||||
|         // var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending;
 | ||||
|         // this.focusDirection = up;
 | ||||
| 
 | ||||
|         var descending = false; // are we currently descending or ascending through the DOM tree?
 | ||||
|         var classes; | ||||
| 
 | ||||
|         do { | ||||
|             var child = up ? element.lastElementChild : element.firstElementChild; | ||||
|             var sibling = up ? element.previousElementSibling : element.nextElementSibling; | ||||
| 
 | ||||
|             if (descending) { | ||||
|                 if (child) { | ||||
|                     element = child; | ||||
|                 } | ||||
|                 else if (sibling) { | ||||
|                     element = sibling; | ||||
|                 } | ||||
|                 else { | ||||
|                     descending = false; | ||||
|                     element = element.parentElement; | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 if (sibling) { | ||||
|                     element = sibling; | ||||
|                     descending = true; | ||||
|                 } | ||||
|                 else { | ||||
|                     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; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } while(element && !( | ||||
|             classes.contains("mx_RoomTile") || | ||||
|             classes.contains("mx_SearchBox_search") || | ||||
|             classes.contains("mx_RoomSubList_ellipsis"))); | ||||
| 
 | ||||
|         if (element) { | ||||
|             element.focus(); | ||||
|             this.focusedElement = element; | ||||
|             this.focusedDescending = descending; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _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
 | ||||
|  | @ -211,8 +120,7 @@ var LeftPanel = React.createClass({ | |||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <aside className={classes} style={{ opacity: this.props.opacity }} | ||||
|                    onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }> | ||||
|             <aside className={classes} style={{ opacity: this.props.opacity }}> | ||||
|                 <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } /> | ||||
|                 { collapseButton } | ||||
|                 { callPreview } | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| /* | ||||
| Copyright 2017 Vector Creations Ltd | ||||
| Copyright 2015, 2016 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  | @ -28,11 +27,9 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | |||
| var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs'); | ||||
| var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils'); | ||||
| var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); | ||||
| var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher'); | ||||
| var RoomSubListHeader = require('./RoomSubListHeader.js'); | ||||
| import Modal from 'matrix-react-sdk/lib/Modal'; | ||||
| 
 | ||||
| // turn this on for drag & drop console debugging galore
 | ||||
| // turn this on for drop & drag console debugging galore
 | ||||
| var debug = false; | ||||
| 
 | ||||
| const TRUNCATE_AT = 10; | ||||
|  | @ -75,17 +72,17 @@ var RoomSubList = React.createClass({ | |||
| 
 | ||||
|         order: React.PropTypes.string.isRequired, | ||||
| 
 | ||||
|         // undefined if no room is selected (eg we are showing settings)
 | ||||
|         selectedRoom: React.PropTypes.string, | ||||
| 
 | ||||
|         startAsHidden: React.PropTypes.bool, | ||||
|         showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded
 | ||||
|         collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
 | ||||
|         onHeaderClick: React.PropTypes.func, | ||||
|         alwaysShowHeader: React.PropTypes.bool, | ||||
|         selectedRoom: React.PropTypes.string, | ||||
|         incomingCall: React.PropTypes.object, | ||||
|         onShowMoreRooms: React.PropTypes.func, | ||||
|         searchFilter: React.PropTypes.string, | ||||
|         emptyContent: React.PropTypes.node, // content shown if the list is empty
 | ||||
|         headerItems: React.PropTypes.node, // content shown in the sublist header
 | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|  | @ -104,31 +101,13 @@ var RoomSubList = React.createClass({ | |||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         constantTimeDispatcher.register("RoomSubList.sort", this.props.tagName, this.onSort); | ||||
|         constantTimeDispatcher.register("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh); | ||||
|         this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order); | ||||
|         this._fixUndefinedOrder(this.props.list); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         constantTimeDispatcher.unregister("RoomSubList.sort", this.props.tagName, this.onSort); | ||||
|         constantTimeDispatcher.unregister("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillReceiveProps: function(newProps) { | ||||
|         // order the room list appropriately before we re-render
 | ||||
|         //if (debug) console.log("received new props, list = " + newProps.list);
 | ||||
|         this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order); | ||||
|         this._fixUndefinedOrder(newProps.list); | ||||
|     }, | ||||
| 
 | ||||
|     onSort: function() { | ||||
|         this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order); | ||||
|         // we deliberately don't waste time trying to fix undefined ordering here
 | ||||
|     }, | ||||
| 
 | ||||
|     onRefresh: function() { | ||||
|         this.forceUpdate(); | ||||
|     }, | ||||
| 
 | ||||
|     applySearchFilter: function(list, filter) { | ||||
|  | @ -141,7 +120,7 @@ var RoomSubList = React.createClass({ | |||
|     // The header is collapsable if it is hidden or not stuck
 | ||||
|     // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
 | ||||
|     isCollapsableOnClick: function() { | ||||
|         var stuck = this.refs.header.refs.header.dataset.stuck; | ||||
|         var stuck = this.refs.header.dataset.stuck; | ||||
|         if (this.state.hidden || stuck === undefined || stuck === "none") { | ||||
|             return true; | ||||
|         } else { | ||||
|  | @ -164,15 +143,14 @@ var RoomSubList = React.createClass({ | |||
|             this.props.onHeaderClick(isHidden); | ||||
|         } else { | ||||
|             // 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.refs.header.dataset.originalPosition); | ||||
|             this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onRoomTileClick(roomId, ev) { | ||||
|     onRoomTileClick(roomId) { | ||||
|         dis.dispatch({ | ||||
|             action: 'view_room', | ||||
|             room_id: roomId, | ||||
|             clear_search: (ev && (ev.keyCode == 13 || ev.keyCode == 32)), | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|  | @ -234,6 +212,9 @@ var RoomSubList = React.createClass({ | |||
|         if (order === "manual") comparator = this.manualComparator; | ||||
|         if (order === "recent") comparator = this.recentsComparator; | ||||
| 
 | ||||
|         // Fix undefined orders here, and make sure the backend gets updated as well
 | ||||
|         this._fixUndefinedOrder(list); | ||||
| 
 | ||||
|         //if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list);
 | ||||
|         this.setState({ sortedList: list.sort(comparator) }); | ||||
|     }, | ||||
|  | @ -268,9 +249,10 @@ var RoomSubList = React.createClass({ | |||
| 
 | ||||
|                 if (badges) { | ||||
|                     result[0] += notificationCount; | ||||
|                     if (highlight) { | ||||
|                         result[1] = true; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 result[1] |= highlight; | ||||
|             } | ||||
|             return result; | ||||
|         }, [0, false]); | ||||
|  | @ -378,6 +360,7 @@ 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 | ||||
|  | @ -385,7 +368,9 @@ var RoomSubList = React.createClass({ | |||
|                     roomSubList={ self } | ||||
|                     key={ room.roomId } | ||||
|                     collapsed={ self.props.collapsed || false} | ||||
|                     selectedRoom={ self.props.selectedRoom } | ||||
|                     selected={ selected } | ||||
|                     unread={ Unread.doesRoomHaveUnreadMessages(room) } | ||||
|                     highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites' } | ||||
|                     isInvite={ self.props.label === 'Invites' } | ||||
|                     refreshSubList={ self._updateSubListCount } | ||||
|                     incomingCall={ null } | ||||
|  | @ -395,6 +380,70 @@ var RoomSubList = React.createClass({ | |||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _getHeaderJsx: function() { | ||||
|         var TintableSvg = sdk.getComponent("elements.TintableSvg"); | ||||
| 
 | ||||
|         var subListNotifications = this.roomNotificationCount(); | ||||
|         var subListNotifCount = subListNotifications[0]; | ||||
|         var subListNotifHighlight = subListNotifications[1]; | ||||
| 
 | ||||
|         var roomCount = this.props.list.length > 0 ? this.props.list.length : ''; | ||||
| 
 | ||||
|         var chevronClasses = classNames({ | ||||
|             'mx_RoomSubList_chevron': true, | ||||
|             'mx_RoomSubList_chevronRight': this.state.hidden, | ||||
|             'mx_RoomSubList_chevronDown': !this.state.hidden, | ||||
|         }); | ||||
| 
 | ||||
|         var badgeClasses = classNames({ | ||||
|             'mx_RoomSubList_badge': true, | ||||
|             'mx_RoomSubList_badgeHighlight': subListNotifHighlight, | ||||
|         }); | ||||
| 
 | ||||
|         var badge; | ||||
|         if (subListNotifCount > 0) { | ||||
|             badge = <div className={badgeClasses}>{ FormattingUtils.formatCount(subListNotifCount) }</div>; | ||||
|         } | ||||
| 
 | ||||
|         // When collapsed, allow a long hover on the header to show user
 | ||||
|         // the full tag name and room count
 | ||||
|         var title; | ||||
|         if (this.props.collapsed) { | ||||
|             title = this.props.label; | ||||
|             if (roomCount !== '') { | ||||
|                 title += " [" + roomCount + "]"; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         var incomingCall; | ||||
|         if (this.props.incomingCall) { | ||||
|             var self = this; | ||||
|             // Check if the incoming call is for this section
 | ||||
|             var incomingCallRoom = this.props.list.filter(function(room) { | ||||
|                 return self.props.incomingCall.roomId === room.roomId; | ||||
|             }); | ||||
| 
 | ||||
|             if (incomingCallRoom.length === 1) { | ||||
|                 var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox"); | ||||
|                 incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         var tabindex = this.props.searchFilter === "" ? "0" : "-1"; | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_RoomSubList_labelContainer" title={ title } ref="header"> | ||||
|                 <AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}> | ||||
|                     { this.props.collapsed ? '' : this.props.label } | ||||
|                     <div className="mx_RoomSubList_roomCount">{ roomCount }</div> | ||||
|                     <div className={chevronClasses}></div> | ||||
|                     { badge } | ||||
|                     { incomingCall } | ||||
|                 </AccessibleButton> | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
| 
 | ||||
|     _createOverflowTile: function(overflowCount, totalCount) { | ||||
|         var content = <div className="mx_RoomSubList_chevronDown"></div>; | ||||
| 
 | ||||
|  | @ -450,7 +499,7 @@ var RoomSubList = React.createClass({ | |||
|             // gets triggered and another list is passed in. Doing it one at a time means that
 | ||||
|             // we always correctly calculate the highest order for the list - stops multiple
 | ||||
|             // rooms getting the same order. This is only really relevant for the first time this
 | ||||
|             // is run with historical room tag data, after that there should only be one undefined
 | ||||
|             // is run with historical room tag data, after that there should only be undefined
 | ||||
|             // in the list at a time anyway.
 | ||||
|             for (let i = 0; i < list.length; i++) { | ||||
|                 if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) { | ||||
|  | @ -472,25 +521,16 @@ var RoomSubList = React.createClass({ | |||
| 
 | ||||
|     render: function() { | ||||
|         var connectDropTarget = this.props.connectDropTarget; | ||||
|         var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); | ||||
|         var TruncatedList = sdk.getComponent('elements.TruncatedList'); | ||||
| 
 | ||||
|         var label = this.props.collapsed ? null : this.props.label; | ||||
| 
 | ||||
|         let content; | ||||
|         if (this.state.sortedList.length == 0) { | ||||
|             content = this.props.emptyContent; | ||||
|         } else { | ||||
|             content = this.makeRoomTiles(); | ||||
|         } | ||||
|         //console.log("render: " + JSON.stringify(this.state.sortedList));
 | ||||
| 
 | ||||
|         var roomCount = this.props.list.length > 0 ? this.props.list.length : ''; | ||||
| 
 | ||||
|         var isIncomingCallRoom; | ||||
|         if (this.props.incomingCall) { | ||||
|             // Check if the incoming call is for this section
 | ||||
|             isIncomingCallRoom = this.props.list.find(room=>{ | ||||
|                 return this.props.incomingCall.roomId === room.roomId; | ||||
|             }) ? true : false; | ||||
|         var target; | ||||
|         if (this.state.sortedList.length == 0 && this.props.editable) { | ||||
|             target = <RoomDropTarget label={ 'Drop here to ' + this.props.verb }/>; | ||||
|         } | ||||
| 
 | ||||
|         if (this.state.sortedList.length > 0 || this.props.editable) { | ||||
|  | @ -500,7 +540,8 @@ var RoomSubList = React.createClass({ | |||
|             if (!this.state.hidden) { | ||||
|                 subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt} | ||||
|                                          createOverflowElement={this._createOverflowTile} > | ||||
|                                 { content } | ||||
|                                 { target } | ||||
|                                 { this.makeRoomTiles() } | ||||
|                           </TruncatedList>; | ||||
|             } | ||||
|             else { | ||||
|  | @ -510,20 +551,7 @@ var RoomSubList = React.createClass({ | |||
| 
 | ||||
|             return connectDropTarget( | ||||
|                 <div> | ||||
|                     <RoomSubListHeader | ||||
|                         ref='header' | ||||
|                         label={ this.props.label } | ||||
|                         tagName={ this.props.tagName } | ||||
|                         roomCount={ roomCount } | ||||
|                         collapsed={ this.props.collapsed } | ||||
|                         hidden={ this.state.hidden } | ||||
|                         incomingCall={ this.props.incomingCall } | ||||
|                         isIncomingCallRoom={ isIncomingCallRoom } | ||||
|                         roomNotificationCount={ this.roomNotificationCount() } | ||||
|                         onClick={ this.onClick } | ||||
|                         onHeaderClick={ this.props.onHeaderClick } | ||||
|                         headerItems={this.props.headerItems} | ||||
|                     /> | ||||
|                     { this._getHeaderJsx() } | ||||
|                     { subList } | ||||
|                 </div> | ||||
|             ); | ||||
|  | @ -532,21 +560,7 @@ var RoomSubList = React.createClass({ | |||
|             var Loader = sdk.getComponent("elements.Spinner"); | ||||
|             return ( | ||||
|                 <div className="mx_RoomSubList"> | ||||
|                     { this.props.alwaysShowHeader ?  | ||||
|                         <RoomSubListHeader | ||||
|                             ref='header' | ||||
|                             label={ this.props.label } | ||||
|                             tagName={ this.props.tagName } | ||||
|                             roomCount={ roomCount } | ||||
|                             collapsed={ this.props.collapsed } | ||||
|                             hidden={ this.state.hidden } | ||||
|                             isIncomingCallRoom={ isIncomingCallRoom } | ||||
|                             roomNotificationCount={ this.roomNotificationCount() } | ||||
|                             onClick={ this.onClick } | ||||
|                             onHeaderClick={ this.props.onHeaderClick } | ||||
|                             headerItems={this.props.headerItems} | ||||
|                         /> | ||||
|                      : undefined } | ||||
|                     { this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined } | ||||
|                     { (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined } | ||||
|                 </div> | ||||
|             ); | ||||
|  |  | |||
|  | @ -1,117 +0,0 @@ | |||
| /* | ||||
| Copyright 2017 Vector Creations 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. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import classNames from 'classnames'; | ||||
| import sdk from 'matrix-react-sdk'; | ||||
| import { formatCount } from 'matrix-react-sdk/lib/utils/FormattingUtils'; | ||||
| import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton'; | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'RoomSubListHeader', | ||||
| 
 | ||||
|     propTypes: { | ||||
|         label: React.PropTypes.string.isRequired, | ||||
|         tagName: React.PropTypes.string, | ||||
|         roomCount: React.PropTypes.oneOfType([ | ||||
|             React.PropTypes.string, | ||||
|             React.PropTypes.number | ||||
|         ]), | ||||
|         collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
 | ||||
|         incomingCall: React.PropTypes.object, | ||||
|         isIncomingCallRoom: React.PropTypes.bool, | ||||
|         roomNotificationCount: React.PropTypes.array, | ||||
|         hidden: React.PropTypes.bool, | ||||
|         onClick: React.PropTypes.func, | ||||
|         onHeaderClick: React.PropTypes.func, | ||||
|         headerItems: React.PropTypes.node, // content shown in the sublist header
 | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|         return { | ||||
|             onHeaderClick: function() {}, // NOP
 | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         // constantTimeDispatcher.register("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
 | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         // constantTimeDispatcher.unregister("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
 | ||||
|     }, | ||||
| 
 | ||||
|     // onRefresh: function() {
 | ||||
|     //     this.forceUpdate();
 | ||||
|     // },
 | ||||
| 
 | ||||
|     render: function() { | ||||
|         const TintableSvg = sdk.getComponent("elements.TintableSvg"); | ||||
| 
 | ||||
|         const subListNotifications = this.props.roomNotificationCount; | ||||
|         const subListNotifCount = subListNotifications[0]; | ||||
|         const subListNotifHighlight = subListNotifications[1]; | ||||
| 
 | ||||
|         const chevronClasses = classNames({ | ||||
|             'mx_RoomSubList_chevron': true, | ||||
|             'mx_RoomSubList_chevronRight': this.props.hidden, | ||||
|             'mx_RoomSubList_chevronDown': !this.props.hidden, | ||||
|         }); | ||||
| 
 | ||||
|         const badgeClasses = classNames({ | ||||
|             'mx_RoomSubList_badge': true, | ||||
|             'mx_RoomSubList_badgeHighlight': subListNotifHighlight, | ||||
|         }); | ||||
| 
 | ||||
|         let badge; | ||||
|         if (subListNotifCount > 0) { | ||||
|             badge = <div className={badgeClasses}>{ formatCount(subListNotifCount) }</div>; | ||||
|         } else if (subListNotifHighlight) { | ||||
|             badge = <div className={badgeClasses}>!</div>;    | ||||
|         } | ||||
| 
 | ||||
|         // When collapsed, allow a long hover on the header to show user
 | ||||
|         // the full tag name and room count
 | ||||
|         let title; | ||||
|         const roomCount = this.props.roomCount; | ||||
|         if (this.props.collapsed) { | ||||
|             title = this.props.label; | ||||
|             if (roomCount !== '') { | ||||
|                 title += " [" + roomCount + "]"; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let incomingCall; | ||||
|         if (this.props.isIncomingCallRoom) { | ||||
|             const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox"); | ||||
|             incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>; | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_RoomSubList_labelContainer" title={ title } ref="header"> | ||||
|                 <AccessibleButton onClick={ this.props.onClick } className="mx_RoomSubList_label" tabIndex="0"> | ||||
|                     { this.props.collapsed ? '' : this.props.label } | ||||
|                     {this.props.headerItems} | ||||
|                     <div className="mx_RoomSubList_roomCount">{ roomCount }</div> | ||||
|                     <div className={chevronClasses}></div> | ||||
|                     { badge } | ||||
|                 </AccessibleButton> | ||||
|                 { incomingCall } | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
| }); | ||||
| 
 | ||||
|  | @ -21,7 +21,6 @@ var sdk = require('matrix-react-sdk') | |||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc'); | ||||
| var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); | ||||
| var KeyCode = require('matrix-react-sdk/lib/KeyCode'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'SearchBox', | ||||
|  | @ -39,23 +38,25 @@ module.exports = React.createClass({ | |||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|         document.addEventListener('keydown', this._onKeyDown); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         dis.unregister(this.dispatcherRef); | ||||
|         document.removeEventListener('keydown', this._onKeyDown); | ||||
|     }, | ||||
| 
 | ||||
|     onAction: function(payload) { | ||||
|         // Disabling this as I find it really really annoying, and was used to the
 | ||||
|         // previous behaviour - see https://github.com/vector-im/riot-web/issues/3348
 | ||||
| /*         | ||||
|         switch (payload.action) { | ||||
|             // Clear up the text field when a room is selected.
 | ||||
|             case 'view_room': | ||||
|                 if (payload.clear_search && this.refs.search) { | ||||
|                 if (this.refs.search) { | ||||
|                     this._clearSearch(); | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
| */         | ||||
|     }, | ||||
| 
 | ||||
|     onChange: function() { | ||||
|  | @ -89,38 +90,6 @@ module.exports = React.createClass({ | |||
|         this.onChange(); | ||||
|     }, | ||||
| 
 | ||||
|     _onKeyDown: function(ev) { | ||||
|         let handled = false; | ||||
|         const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; | ||||
|         let ctrlCmdOnly; | ||||
|         if (isMac) { | ||||
|             ctrlCmdOnly = ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey; | ||||
|         } else { | ||||
|             ctrlCmdOnly = ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey; | ||||
|         } | ||||
| 
 | ||||
|         switch (ev.keyCode) { | ||||
|             case KeyCode.ESCAPE: | ||||
|                 this._clearSearch(); | ||||
|                 dis.dispatch({action: 'focus_composer'}); | ||||
|                 break; | ||||
|             case KeyCode.KEY_K: | ||||
|                 if (ctrlCmdOnly) { | ||||
|                     if (this.refs.search) { | ||||
|                         this.refs.search.focus(); | ||||
|                         this.refs.search.select(); | ||||
|                     } | ||||
|                     handled = true; | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         if (handled) { | ||||
|             ev.stopPropagation(); | ||||
|             ev.preventDefault(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var TintableSvg = sdk.getComponent('elements.TintableSvg'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,7 +27,6 @@ | |||
| @import "./matrix-react-sdk/views/elements/_MemberEventListSummary.scss"; | ||||
| @import "./matrix-react-sdk/views/elements/_ProgressBar.scss"; | ||||
| @import "./matrix-react-sdk/views/elements/_RichText.scss"; | ||||
| @import "./matrix-react-sdk/views/elements/_RoleButton.scss"; | ||||
| @import "./matrix-react-sdk/views/login/_InteractiveAuthEntryComponents.scss"; | ||||
| @import "./matrix-react-sdk/views/login/_ServerConfig.scss"; | ||||
| @import "./matrix-react-sdk/views/messages/_MEmoteBody.scss"; | ||||
|  |  | |||
|  | @ -172,7 +172,7 @@ hr.mx_RoomView_myReadMarker { | |||
| 
 | ||||
|     max-height: 0px; | ||||
|     background-color: $primary-bg-color; | ||||
|     z-index: 5; | ||||
|     z-index: 1000; | ||||
|     overflow: hidden; | ||||
| 
 | ||||
|     -webkit-transition: all .2s ease-out; | ||||
|  | @ -260,4 +260,4 @@ hr.mx_RoomView_myReadMarker { | |||
| 
 | ||||
| .mx_RoomView_ongoingConfCallNotification a { | ||||
|     color: $accent-fg-color ! important; | ||||
| } | ||||
| } | ||||
|  | @ -1,33 +0,0 @@ | |||
| /* | ||||
| Copyright 2107 Vector Creations 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. | ||||
| */ | ||||
| 
 | ||||
| .mx_RoleButton { | ||||
|     margin-left: 4px; | ||||
|     margin-right: 4px; | ||||
|     cursor: pointer; | ||||
|     display: inline-block; | ||||
| } | ||||
| 
 | ||||
| .mx_RoleButton object { | ||||
|     pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .mx_RoleButton_tooltip { | ||||
|     display: inline-block; | ||||
|     position: relative; | ||||
|     top: -25px; | ||||
|     left: 6px; | ||||
| } | ||||
|  | @ -1,6 +1,5 @@ | |||
| /* | ||||
| Copyright 2015, 2016 OpenMarket Ltd | ||||
| Copyright 2107 Vector Creations Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
|  | @ -38,25 +37,3 @@ limitations under the License. | |||
| .mx_RoomList_scrollbar .gm-scrollbar.-vertical { | ||||
|     z-index: 6; | ||||
| } | ||||
| 
 | ||||
| .mx_RoomList_emptySubListTip { | ||||
|     font-size: 13px; | ||||
|     margin-left: 18px; | ||||
|     margin-right: 18px; | ||||
|     margin-top: 8px; | ||||
|     margin-bottom: 7px; | ||||
|     padding: 5px; | ||||
|     border: 1px dashed $accent-color; | ||||
|     color: $primary-fg-color; | ||||
|     background-color: $droptarget-bg-color; | ||||
|     border-radius: 4px; | ||||
| } | ||||
| 
 | ||||
| .mx_RoomList_emptySubListTip .mx_RoleButton { | ||||
|     vertical-align: -3px; | ||||
| } | ||||
| 
 | ||||
| .mx_RoomList_headerButtons { | ||||
|     position: absolute; | ||||
|     right: 60px; | ||||
| } | ||||
|  |  | |||
|  | @ -64,25 +64,43 @@ limitations under the License. | |||
|     pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .collapsed .mx_RoleButton { | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_homePage, | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_directory, | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_createRoom, | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_people, | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_settings { | ||||
|     display: inline-block; | ||||
|     cursor: pointer; | ||||
| } | ||||
| 
 | ||||
| .collapsed .mx_BottomLeftMenu_homePage, | ||||
| .collapsed .mx_BottomLeftMenu_directory, | ||||
| .collapsed .mx_BottomLeftMenu_createRoom, | ||||
| .collapsed .mx_BottomLeftMenu_people, | ||||
| .collapsed .mx_BottomLeftMenu_settings { | ||||
|     margin-right: 0px ! important; | ||||
|     padding-top: 3px ! important; | ||||
|     padding-bottom: 3px ! important; | ||||
| } | ||||
| 
 | ||||
| .mx_BottomLeftMenu_options .mx_RoleButton { | ||||
|     margin-left: 0px; | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_homePage, | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_directory, | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_createRoom, | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_people { | ||||
|     margin-right: 10px; | ||||
| } | ||||
| 
 | ||||
| .mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings { | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_settings { | ||||
|     float: right; | ||||
| } | ||||
| 
 | ||||
| .mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings .mx_RoleButton { | ||||
|     margin-right: 0px; | ||||
| } | ||||
| 
 | ||||
| .mx_LeftPanel.collapsed .mx_BottomLeftMenu_settings { | ||||
|     float: none; | ||||
| } | ||||
| 
 | ||||
| .mx_LeftPanel .mx_BottomLeftMenu_tooltip { | ||||
|     display: inline-block; | ||||
|     position: relative; | ||||
|     top: -25px; | ||||
|     left: 6px; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker