Merge branch 'develop' of github.com:vector-im/riot-web into forward_message

This commit is contained in:
Michael Telatynski 2017-05-19 01:15:31 +01:00
commit eaeac1230f
311 changed files with 674 additions and 720 deletions

View file

@ -1,90 +0,0 @@
/*
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.
*/
/*
* THIS FILE IS AUTO-GENERATED
* You can edit it you like, but your changes will be overwritten,
* so you'd just be trying to swim upstream like a salmon.
* You are not a salmon.
*
* To update it, run:
* ./reskindex.js -h header
*/
module.exports.components = require('matrix-react-sdk/lib/component-index').components;
import structures$BottomLeftMenu from './components/structures/BottomLeftMenu';
structures$BottomLeftMenu && (module.exports.components['structures.BottomLeftMenu'] = structures$BottomLeftMenu);
import structures$CompatibilityPage from './components/structures/CompatibilityPage';
structures$CompatibilityPage && (module.exports.components['structures.CompatibilityPage'] = structures$CompatibilityPage);
import structures$HomePage from './components/structures/HomePage';
structures$HomePage && (module.exports.components['structures.HomePage'] = structures$HomePage);
import structures$LeftPanel from './components/structures/LeftPanel';
structures$LeftPanel && (module.exports.components['structures.LeftPanel'] = structures$LeftPanel);
import structures$RightPanel from './components/structures/RightPanel';
structures$RightPanel && (module.exports.components['structures.RightPanel'] = structures$RightPanel);
import structures$RoomDirectory from './components/structures/RoomDirectory';
structures$RoomDirectory && (module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory);
import structures$RoomSubList from './components/structures/RoomSubList';
structures$RoomSubList && (module.exports.components['structures.RoomSubList'] = structures$RoomSubList);
import structures$RoomSubListHeader from './components/structures/RoomSubListHeader';
structures$RoomSubListHeader && (module.exports.components['structures.RoomSubListHeader'] = structures$RoomSubListHeader);
import structures$SearchBox from './components/structures/SearchBox';
structures$SearchBox && (module.exports.components['structures.SearchBox'] = structures$SearchBox);
import structures$ViewSource from './components/structures/ViewSource';
structures$ViewSource && (module.exports.components['structures.ViewSource'] = structures$ViewSource);
import views$context_menus$MessageContextMenu from './components/views/context_menus/MessageContextMenu';
views$context_menus$MessageContextMenu && (module.exports.components['views.context_menus.MessageContextMenu'] = views$context_menus$MessageContextMenu);
import views$context_menus$RoomTileContextMenu from './components/views/context_menus/RoomTileContextMenu';
views$context_menus$RoomTileContextMenu && (module.exports.components['views.context_menus.RoomTileContextMenu'] = views$context_menus$RoomTileContextMenu);
import views$dialogs$BugReportDialog from './components/views/dialogs/BugReportDialog';
views$dialogs$BugReportDialog && (module.exports.components['views.dialogs.BugReportDialog'] = views$dialogs$BugReportDialog);
import views$dialogs$ChangelogDialog from './components/views/dialogs/ChangelogDialog';
views$dialogs$ChangelogDialog && (module.exports.components['views.dialogs.ChangelogDialog'] = views$dialogs$ChangelogDialog);
import views$directory$NetworkDropdown from './components/views/directory/NetworkDropdown';
views$directory$NetworkDropdown && (module.exports.components['views.directory.NetworkDropdown'] = views$directory$NetworkDropdown);
import views$elements$ImageView from './components/views/elements/ImageView';
views$elements$ImageView && (module.exports.components['views.elements.ImageView'] = views$elements$ImageView);
import views$elements$Spinner from './components/views/elements/Spinner';
views$elements$Spinner && (module.exports.components['views.elements.Spinner'] = views$elements$Spinner);
import views$globals$GuestWarningBar from './components/views/globals/GuestWarningBar';
views$globals$GuestWarningBar && (module.exports.components['views.globals.GuestWarningBar'] = views$globals$GuestWarningBar);
import views$globals$MatrixToolbar from './components/views/globals/MatrixToolbar';
views$globals$MatrixToolbar && (module.exports.components['views.globals.MatrixToolbar'] = views$globals$MatrixToolbar);
import views$globals$NewVersionBar from './components/views/globals/NewVersionBar';
views$globals$NewVersionBar && (module.exports.components['views.globals.NewVersionBar'] = views$globals$NewVersionBar);
import views$login$VectorCustomServerDialog from './components/views/login/VectorCustomServerDialog';
views$login$VectorCustomServerDialog && (module.exports.components['views.login.VectorCustomServerDialog'] = views$login$VectorCustomServerDialog);
import views$login$VectorLoginFooter from './components/views/login/VectorLoginFooter';
views$login$VectorLoginFooter && (module.exports.components['views.login.VectorLoginFooter'] = views$login$VectorLoginFooter);
import views$login$VectorLoginHeader from './components/views/login/VectorLoginHeader';
views$login$VectorLoginHeader && (module.exports.components['views.login.VectorLoginHeader'] = views$login$VectorLoginHeader);
import views$messages$DateSeparator from './components/views/messages/DateSeparator';
views$messages$DateSeparator && (module.exports.components['views.messages.DateSeparator'] = views$messages$DateSeparator);
import views$messages$MessageTimestamp from './components/views/messages/MessageTimestamp';
views$messages$MessageTimestamp && (module.exports.components['views.messages.MessageTimestamp'] = views$messages$MessageTimestamp);
import views$rooms$DNDRoomTile from './components/views/rooms/DNDRoomTile';
views$rooms$DNDRoomTile && (module.exports.components['views.rooms.DNDRoomTile'] = views$rooms$DNDRoomTile);
import views$rooms$RoomDropTarget from './components/views/rooms/RoomDropTarget';
views$rooms$RoomDropTarget && (module.exports.components['views.rooms.RoomDropTarget'] = views$rooms$RoomDropTarget);
import views$rooms$RoomTooltip from './components/views/rooms/RoomTooltip';
views$rooms$RoomTooltip && (module.exports.components['views.rooms.RoomTooltip'] = views$rooms$RoomTooltip);
import views$rooms$SearchBar from './components/views/rooms/SearchBar';
views$rooms$SearchBar && (module.exports.components['views.rooms.SearchBar'] = views$rooms$SearchBar);
import views$settings$IntegrationsManager from './components/views/settings/IntegrationsManager';
views$settings$IntegrationsManager && (module.exports.components['views.settings.IntegrationsManager'] = views$settings$IntegrationsManager);
import views$settings$Notifications from './components/views/settings/Notifications';
views$settings$Notifications && (module.exports.components['views.settings.Notifications'] = views$settings$Notifications);

View file

@ -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 }

View file

@ -60,6 +60,7 @@ module.exports = React.createClass({
},
componentWillMount: function() {
this._unmounted = false;
this.nextBatch = null;
this.filterTimeout = null;
this.scrollPanel = null;
@ -97,6 +98,10 @@ module.exports = React.createClass({
// sideOpacity: 1.0,
// middleOpacity: 1.0,
// });
if (this.filterTimeout) {
clearTimeout(this.filterTimeout);
}
this._unmounted = true;
},
refreshRoomList: function() {
@ -139,6 +144,11 @@ module.exports = React.createClass({
return;
}
if (this._unmounted) {
// if we've been unmounted, we don't care either.
return;
}
this.nextBatch = data.next_batch;
this.setState((s) => {
s.publicRooms.push(...data.chunk);
@ -156,6 +166,12 @@ module.exports = React.createClass({
// requests either
return;
}
if (this._unmounted) {
// if we've been unmounted, we don't care either.
return;
}
this.setState({ loading: false });
console.error("Failed to get publicRooms: %s", JSON.stringify(err));
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -204,7 +220,7 @@ module.exports = React.createClass({
}).done(() => {
modal.close();
this.refreshRoomList();
}, function(err) {
}, (err) => {
modal.close();
this.refreshRoomList();
console.error("Failed to " + step + ": " + err);

View file

@ -27,10 +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;
@ -73,12 +72,14 @@ 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,
@ -100,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) {
@ -137,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 {
@ -160,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)),
});
},
@ -230,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) });
},
@ -264,9 +249,10 @@ var RoomSubList = React.createClass({
if (badges) {
result[0] += notificationCount;
if (highlight) {
result[1] = true;
}
}
result[1] |= highlight;
}
return result;
}, [0, false]);
@ -374,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
@ -381,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 }
@ -391,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>;
@ -446,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) {
@ -480,16 +533,6 @@ var RoomSubList = React.createClass({
target = <RoomDropTarget label={ 'Drop here to ' + this.props.verb }/>;
}
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;
}
if (this.state.sortedList.length > 0 || this.props.editable) {
var subList;
var classes = "mx_RoomSubList";
@ -508,19 +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 }
/>
{ this._getHeaderJsx() }
{ subList }
</div>
);
@ -529,20 +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 }
/>
: undefined }
{ this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined }
{ (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined }
</div>
);

View file

@ -1,121 +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.
*/
'use strict';
var React = require('react');
var ReactDOM = require('react-dom');
var classNames = require('classnames');
var sdk = require('matrix-react-sdk')
var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils');
var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher');
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,
},
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() {
var TintableSvg = sdk.getComponent("elements.TintableSvg");
var subListNotifications = this.props.roomNotificationCount;
var subListNotifCount = subListNotifications[0];
var subListNotifHighlight = subListNotifications[1];
var chevronClasses = classNames({
'mx_RoomSubList_chevron': true,
'mx_RoomSubList_chevronRight': this.props.hidden,
'mx_RoomSubList_chevronDown': !this.props.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>;
}
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
var title;
var roomCount = this.props.roomCount;
if (this.props.collapsed) {
title = this.props.label;
if (roomCount !== '') {
title += " [" + roomCount + "]";
}
}
var incomingCall;
if (this.props.isIncomingCallRoom) {
var 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 }
<div className="mx_RoomSubList_roomCount">{ roomCount }</div>
<div className={chevronClasses}></div>
{ badge }
</AccessibleButton>
{ incomingCall }
</div>
);
},
});

View file

@ -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');

View file

@ -25,7 +25,7 @@ module.exports = React.createClass({
render: function() {
var date = new Date(this.props.ts);
return (
<span className="mx_MessageTimestamp" title={ DateUtils.formatDate(date) }>
<span className="mx_MessageTimestamp" title={ DateUtils.formatFullDate(date) }>
{ DateUtils.formatTime(date) }
</span>
);

View file

@ -160,7 +160,8 @@ hr.mx_RoomView_myReadMarker {
border-bottom: solid 1px $accent-color;
margin-top: 0px;
position: relative;
top: 5px;
top: -1px;
z-index: 1;
}
.mx_RoomView_statusArea {
@ -171,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;
@ -259,4 +260,4 @@ hr.mx_RoomView_myReadMarker {
.mx_RoomView_ongoingConfCallNotification a {
color: $accent-fg-color ! important;
}
}

View file

@ -219,6 +219,10 @@ input.mx_UserSettings_phoneNumberField {
margin-top: 10px;
}
.mx_UserSettings_avatarPicker_edit img {
cursor: pointer;
}
.mx_UserSettings_avatarPicker_edit > input {
display: none;
}

View file

@ -184,7 +184,7 @@ limitations under the License.
}
.mx_Login_field_prefix {
height: 33px;
height: 34px;
padding: 0px 5px;
line-height: 33px;
@ -197,7 +197,7 @@ limitations under the License.
}
.mx_Login_field_suffix {
height: 33px;
height: 34px;
padding: 0px 5px;
line-height: 33px;
@ -211,11 +211,16 @@ limitations under the License.
}
.mx_Login_username {
height: 16px;
flex-shrink: 1;
min-width: 0px;
border-radius: 3px;
}
.mx_Login_phoneNumberField {
height: 16px;
}
.mx_Login_field_has_prefix {
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
@ -226,8 +231,23 @@ limitations under the License.
border-bottom-right-radius: 0px;
}
.mx_Login_phoneSection {
display:flex;
}
.mx_Login_phoneCountry {
margin-bottom: 14px;
width: 150px;
/* To override mx_Login_field_prefix */
text-align: left;
padding: 0px;
background-color: $primary-bg-color;
}
.mx_Login_field_prefix .mx_Dropdown_input {
/* To use prefix border instead of dropdown border */
border: 0;
}
.mx_Login_phoneCountry .mx_Dropdown_option {
@ -240,6 +260,6 @@ limitations under the License.
}
.mx_Login_phoneCountry .mx_Dropdown_option img {
margin: 4px;
margin: 3px;
vertical-align: top;
}

View file

@ -48,6 +48,14 @@ limitations under the License.
width: 0
}
.mx_Dropdown.left_aligned .mx_Dropdown_arrow {
left: 10px;
}
.mx_Dropdown.left_aligned .mx_Dropdown_input > .mx_Dropdown_option {
padding-left: 25px;
}
.mx_Dropdown_option {
height: 35px;
line-height: 35px;
@ -55,9 +63,15 @@ limitations under the License.
padding-right: 8px;
}
.mx_Dropdown_option div {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mx_Dropdown_option img {
margin: 5px;
width: 25px;
width: 27px;
vertical-align: middle;
}

View file

@ -20,6 +20,7 @@ limitations under the License.
.mx_TextualEvent.mx_MemberEventListSummary_summary {
font-size: 14px;
display: inline-flex;
}
.mx_MemberEventListSummary_avatars {

View file

@ -12,7 +12,7 @@
<link rel="apple-touch-icon" sizes="144x144" href="vector-icons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="vector-icons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="vector-icons/apple-touch-icon-180x180.png">
<link rel="manifest" href="vector-icons/manifest.json">
<link rel="manifest" href="manifest.json">
<link rel="shortcut icon" href="vector-icons/favicon.ico">
<meta name="apple-mobile-web-app-title" content="Riot">
<meta name="application-name" content="Riot">

View file

@ -103,7 +103,7 @@ function checkBrowserFeatures(featureList) {
var validBrowser = checkBrowserFeatures([
"displaytable", "flexbox", "es5object", "es5function", "localstorage",
"objectfit"
"objectfit", "indexeddb", "webworkers",
]);
// Parse the given window.location and return parameters that can be used when calling

File diff suppressed because one or more lines are too long

View file

@ -20,13 +20,11 @@ limitations under the License.
import VectorBasePlatform from './VectorBasePlatform';
import dis from 'matrix-react-sdk/lib/dispatcher';
import q from 'q';
const electron = require('electron');
const remote = electron.remote;
import electron, {remote} from 'electron';
remote.autoUpdater.on('update-downloaded', onUpdateDownloaded);
function onUpdateDownloaded(ev, releaseNotes, ver, date, updateURL) {
function onUpdateDownloaded(ev: Event, releaseNotes: string, ver: string, date: Date, updateURL: string) {
dis.dispatch({
action: 'new_version',
currentVersion: remote.app.getVersion(),
@ -35,7 +33,7 @@ function onUpdateDownloaded(ev, releaseNotes, ver, date, updateURL) {
});
}
function platformFriendlyName() {
function platformFriendlyName(): string {
console.log(window.process);
switch (window.process.platform) {
case 'darwin':
@ -72,11 +70,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
}
}
supportsNotifications() : boolean {
supportsNotifications(): boolean {
return true;
}
maySendNotifications() : boolean {
maySendNotifications(): boolean {
return true;
}
@ -100,7 +98,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
icon: avatarUrl,
tag: 'vector',
silent: true, // we play our own sounds
}
},
);
notification.onclick = function() {
@ -123,7 +121,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
notif.close();
}
getAppVersion() {
getAppVersion(): Promise<string> {
return q(remote.app.getVersion());
}
@ -140,15 +138,15 @@ export default class ElectronPlatform extends VectorBasePlatform {
electron.ipcRenderer.send('install_update');
}
getDefaultDeviceDisplayName() {
getDefaultDeviceDisplayName(): string {
return 'Riot Desktop on ' + platformFriendlyName();
}
screenCaptureErrorString() {
screenCaptureErrorString(): ?string {
return null;
}
requestNotificationPermission() : Promise {
requestNotificationPermission(): Promise<string> {
return q('granted');
}

View file

@ -44,7 +44,7 @@ export default class VectorBasePlatform extends BasePlatform {
* Get a sensible default display name for the
* device Vector is running on
*/
getDefaultDeviceDisplayName() {
getDefaultDeviceDisplayName(): string {
return "Unknown device";
}
}

View file

@ -52,7 +52,7 @@ export default class WebPlatform extends VectorBasePlatform {
}
this.favicon.badge(notif, {
bgColor: bgColor
bgColor: bgColor,
});
} catch (e) {
console.warn(`Failed to set badge count: ${e.message}`);
@ -75,7 +75,7 @@ export default class WebPlatform extends VectorBasePlatform {
* Returns true if the platform supports displaying
* notifications, otherwise false.
*/
supportsNotifications() : boolean {
supportsNotifications(): boolean {
return Boolean(global.Notification);
}
@ -83,8 +83,8 @@ export default class WebPlatform extends VectorBasePlatform {
* Returns true if the application currently has permission
* to display notifications. Otherwise false.
*/
maySendNotifications() : boolean {
return global.Notification.permission == 'granted';
maySendNotifications(): boolean {
return global.Notification.permission === 'granted';
}
/**
@ -94,7 +94,7 @@ export default class WebPlatform extends VectorBasePlatform {
* that is 'granted' if the user allowed the request or
* 'denied' otherwise.
*/
requestNotificationPermission() : Promise {
requestNotificationPermission(): Promise<string> {
// annoyingly, the latest spec says this returns a
// promise, but this is only supported in Chrome 46
// and Firefox 47, so adapt the callback API.
@ -113,13 +113,13 @@ export default class WebPlatform extends VectorBasePlatform {
icon: avatarUrl,
tag: "vector",
silent: true, // we play our own sounds
}
},
);
notification.onclick = function() {
dis.dispatch({
action: 'view_room',
room_id: room.roomId
room_id: room.roomId,
});
global.focus();
notification.close();
@ -132,7 +132,7 @@ export default class WebPlatform extends VectorBasePlatform {
}, 5 * 1000);
}
_getVersion() {
_getVersion(): Promise<string> {
const deferred = q.defer();
// We add a cachebuster to the request to make sure that we know about
@ -148,19 +148,19 @@ export default class WebPlatform extends VectorBasePlatform {
},
(err, response, body) => {
if (err || response.status < 200 || response.status >= 300) {
if (err == null) err = { status: response.status };
if (err === null) err = { status: response.status };
deferred.reject(err);
return;
}
const ver = body.trim();
deferred.resolve(ver);
}
},
);
return deferred.promise;
}
getAppVersion() {
getAppVersion(): Promise<string> {
if (this.runningVersion !== null) {
return q(this.runningVersion);
}
@ -169,9 +169,9 @@ export default class WebPlatform extends VectorBasePlatform {
pollForUpdate() {
this._getVersion().done((ver) => {
if (this.runningVersion == null) {
if (this.runningVersion === null) {
this.runningVersion = ver;
} else if (this.runningVersion != ver) {
} else if (this.runningVersion !== ver) {
dis.dispatch({
action: 'new_version',
currentVersion: this.runningVersion,
@ -187,19 +187,18 @@ export default class WebPlatform extends VectorBasePlatform {
window.location.reload();
}
getDefaultDeviceDisplayName() {
getDefaultDeviceDisplayName(): string {
// strip query-string and fragment from uri
let u = url.parse(window.location.href);
const u = url.parse(window.location.href);
u.search = "";
u.hash = "";
let app_name = u.format();
const appName = u.format();
let ua = new UAParser();
return app_name + " via " + ua.getBrowser().name +
" on " + ua.getOS().name;
const ua = new UAParser();
return `${appName} via ${ua.getBrowser().name} on ${ua.getOS().name}`;
}
screenCaptureErrorString() {
screenCaptureErrorString(): ?string {
// it won't work at all if you're not on HTTPS so whine whine whine
if (!global.window || global.window.location.protocol !== "https:") {
return "You need to be using HTTPS to place a screen-sharing call.";

View file

@ -37,8 +37,12 @@ import q from "q";
// actually timestamps. We then purge the remaining logs. We also do this
// purge on startup to prevent logs from accumulating.
// the frequency with which we flush to indexeddb
const FLUSH_RATE_MS = 30 * 1000;
// the length of log data we keep in indexeddb (and include in the reports)
const MAX_LOG_SIZE = 1024 * 1024 * 1; // 1 MB
// A class which monkey-patches the global console and stores log lines.
class ConsoleLogger {
constructor() {
@ -232,7 +236,6 @@ class IndexedDBLogStore {
* is a big string with all the new-line delimited logs.
*/
async consume() {
const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB
const db = this.db;
// Returns: a string representing the concatenated logs for this ID.

View file

@ -17,6 +17,7 @@ limitations under the License.
import pako from 'pako';
import q from "q";
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
import rageshake from './rageshake'
@ -64,6 +65,8 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
userAgent = window.navigator.userAgent;
}
const client = MatrixClientPeg.get();
console.log("Sending bug report.");
const body = new FormData();
@ -72,6 +75,11 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
body.append('version', version);
body.append('user_agent', userAgent);
if (client) {
body.append('user_id', client.credentials.userId);
body.append('device_id', client.deviceId);
}
if (opts.sendLogs) {
progressCallback("Collecting logs");
const logs = await rageshake.getLogsForReport();