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

pull/21833/head
Weblate 2018-01-19 14:49:55 +00:00
commit aff1fcde90
5 changed files with 218 additions and 139 deletions

View File

@ -78,8 +78,6 @@
"react": "^15.4.0", "react": "^15.4.0",
"react-addons-css-transition-group": "15.3.2", "react-addons-css-transition-group": "15.3.2",
"react-beautiful-dnd": "^4.0.0", "react-beautiful-dnd": "^4.0.0",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.4.0", "react-dom": "^15.4.0",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef", "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"sanitize-html": "^1.14.1", "sanitize-html": "^1.14.1",

View File

@ -19,8 +19,6 @@ limitations under the License.
import * as Matrix from 'matrix-js-sdk'; import * as Matrix from 'matrix-js-sdk';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
import Notifier from '../../Notifier'; import Notifier from '../../Notifier';
@ -347,4 +345,4 @@ const LoggedInView = React.createClass({
}, },
}); });
export default DragDropContext(HTML5Backend)(LoggedInView); export default LoggedInView;

View File

@ -1066,10 +1066,10 @@ export default React.createClass({
// this if we are not scrolled up in the view. To find out, delegate to // this if we are not scrolled up in the view. To find out, delegate to
// the timeline panel. If the timeline panel doesn't exist, then we assume // the timeline panel. If the timeline panel doesn't exist, then we assume
// it is safe to reset the timeline. // it is safe to reset the timeline.
if (!self._loggedInView) { if (!self._loggedInView || !self._loggedInView.child) {
return true; return true;
} }
return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId); return self._loggedInView.child.canResetTimelineInRoom(roomId);
}); });
cli.on('sync', function(state, prevState) { cli.on('sync', function(state, prevState) {

View File

@ -18,6 +18,7 @@ limitations under the License.
'use strict'; 'use strict';
const React = require("react"); const React = require("react");
const ReactDOM = require("react-dom"); const ReactDOM = require("react-dom");
import { DragDropContext } from 'react-beautiful-dnd';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
const GeminiScrollbar = require('react-gemini-scrollbar'); const GeminiScrollbar = require('react-gemini-scrollbar');
@ -32,6 +33,8 @@ const Receipt = require('../../../utils/Receipt');
import TagOrderStore from '../../../stores/TagOrderStore'; import TagOrderStore from '../../../stores/TagOrderStore';
import GroupStoreCache from '../../../stores/GroupStoreCache'; import GroupStoreCache from '../../../stores/GroupStoreCache';
import Modal from '../../../Modal';
const HIDE_CONFERENCE_CHANS = true; const HIDE_CONFERENCE_CHANS = true;
function phraseForSection(section) { function phraseForSection(section) {
@ -275,6 +278,103 @@ module.exports = React.createClass({
this.forceUpdate(); this.forceUpdate();
}, },
onRoomTileEndDrag: function(result) {
if (!result.destination) return;
let newTag = result.destination.droppableId.split('_')[1];
let prevTag = result.source.droppableId.split('_')[1];
if (newTag === 'undefined') newTag = undefined;
if (prevTag === 'undefined') prevTag = undefined;
const roomId = result.draggableId.split('_')[1];
const room = MatrixClientPeg.get().getRoom(roomId);
const newIndex = result.destination.index;
// Evil hack to get DMs behaving
if ((prevTag === undefined && newTag === 'im.vector.fake.direct') ||
(prevTag === 'im.vector.fake.direct' && newTag === undefined)
) {
Rooms.guessAndSetDMRoom(
room, newTag === 'im.vector.fake.direct',
).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to set direct chat tag " + err);
Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, {
title: _t('Failed to set direct chat tag'),
description: ((err && err.message) ? err.message : _t('Operation failed')),
});
});
return;
}
const hasChangedSubLists = result.source.droppableId !== result.destination.droppableId;
let newOrder = null;
// Is the tag ordered manually?
if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
const newList = Object.assign({}, this.state.lists[newTag]);
// If the room was moved "down" (increasing index) in the same list we
// need to use the orders of the tiles with indices shifted by +1
const offset = (
newTag === prevTag && result.source.index < result.destination.index
) ? 1 : 0;
const prevOrder = newIndex === 0 ?
0 : newList[offset + newIndex - 1].tags[newTag].order;
const nextOrder = newIndex === newList.length ?
1 : newList[offset + newIndex].tags[newTag].order;
newOrder = {
order: (prevOrder + nextOrder) / 2.0,
};
}
// More evilness: We will still be dealing with moving to favourites/low prio,
// but we avoid ever doing a request with 'im.vector.fake.direct`.
//
// if we moved lists, remove the old tag
if (prevTag && prevTag !== 'im.vector.fake.direct' &&
hasChangedSubLists
) {
// Optimistic update of what will happen to the room tags
delete room.tags[prevTag];
MatrixClientPeg.get().deleteRoomTag(roomId, prevTag).catch(function(err) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to remove tag " + prevTag + " from room: " + err);
Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, {
title: _t('Failed to remove tag %(tagName)s from room', {tagName: prevTag}),
description: ((err && err.message) ? err.message : _t('Operation failed')),
});
});
}
// if we moved lists or the ordering changed, add the new tag
if (newTag && newTag !== 'im.vector.fake.direct' &&
(hasChangedSubLists || newOrder)
) {
// Optimistic update of what will happen to the room tags
room.tags[newTag] = newOrder;
MatrixClientPeg.get().setRoomTag(roomId, newTag, newOrder).catch(function(err) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to add tag " + newTag + " to room: " + err);
Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, {
title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}),
description: ((err && err.message) ? err.message : _t('Operation failed')),
});
});
}
// Refresh to display the optimistic updates - this needs to be done in the
// same tick as the drag finishing otherwise the room will pop back to its
// previous position - hence no delayed refresh
this.refreshRoomList();
},
_delayedRefreshRoomList: new rate_limited_func(function() { _delayedRefreshRoomList: new rate_limited_func(function() {
this.refreshRoomList(); this.refreshRoomList();
}, 500), }, 500),
@ -649,6 +749,7 @@ module.exports = React.createClass({
const self = this; const self = this;
return ( return (
<DragDropContext onDragEnd={this.onRoomTileEndDrag}>
<GeminiScrollbar className="mx_RoomList_scrollbar" <GeminiScrollbar className="mx_RoomList_scrollbar"
autoshow={true} onScroll={self._whenScrolling} ref="gemscroll"> autoshow={true} onScroll={self._whenScrolling} ref="gemscroll">
<div className="mx_RoomList"> <div className="mx_RoomList">
@ -757,6 +858,7 @@ module.exports = React.createClass({
onShowMoreRooms={self.onShowMoreRooms} /> onShowMoreRooms={self.onShowMoreRooms} />
</div> </div>
</GeminiScrollbar> </GeminiScrollbar>
</DragDropContext>
); );
}, },
}); });

View File

@ -35,10 +35,7 @@ module.exports = React.createClass({
displayName: 'RoomTile', displayName: 'RoomTile',
propTypes: { propTypes: {
connectDragSource: PropTypes.func,
connectDropTarget: PropTypes.func,
onClick: PropTypes.func, onClick: PropTypes.func,
isDragging: PropTypes.bool,
room: PropTypes.object.isRequired, room: PropTypes.object.isRequired,
collapsed: PropTypes.bool.isRequired, collapsed: PropTypes.bool.isRequired,
@ -256,16 +253,7 @@ module.exports = React.createClass({
directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />; directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
} }
// These props are injected by React DnD, return <AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
// as defined by your `collect` function above:
const isDragging = this.props.isDragging;
const connectDragSource = this.props.connectDragSource;
const connectDropTarget = this.props.connectDropTarget;
let ret = (
<div> { /* Only native elements can be wrapped in a DnD object. */ }
<AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<div className={avatarClasses}> <div className={avatarClasses}>
<div className="mx_RoomTile_avatar_container"> <div className="mx_RoomTile_avatar_container">
<RoomAvatar room={this.props.room} width={24} height={24} /> <RoomAvatar room={this.props.room} width={24} height={24} />
@ -278,13 +266,6 @@ module.exports = React.createClass({
</div> </div>
{ /* { incomingCallBox } */ } { /* { incomingCallBox } */ }
{ tooltip } { tooltip }
</AccessibleButton> </AccessibleButton>;
</div>
);
if (connectDropTarget) ret = connectDropTarget(ret);
if (connectDragSource) ret = connectDragSource(ret);
return ret;
}, },
}); });