diff --git a/package.json b/package.json index dbc27b5a08..f81e72e556 100644 --- a/package.json +++ b/package.json @@ -78,8 +78,6 @@ "react": "^15.4.0", "react-addons-css-transition-group": "15.3.2", "react-beautiful-dnd": "^4.0.0", - "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#5e97aef", "sanitize-html": "^1.14.1", diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index bebc109806..e97d9dd0a1 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -19,8 +19,6 @@ limitations under the License. import * as Matrix from 'matrix-js-sdk'; import React from 'react'; import PropTypes from 'prop-types'; -import { DragDropContext } from 'react-dnd'; -import HTML5Backend from 'react-dnd-html5-backend'; import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; import Notifier from '../../Notifier'; @@ -347,4 +345,4 @@ const LoggedInView = React.createClass({ }, }); -export default DragDropContext(HTML5Backend)(LoggedInView); +export default LoggedInView; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 733007677b..a5181b55a7 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1066,10 +1066,10 @@ export default React.createClass({ // 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 // it is safe to reset the timeline. - if (!self._loggedInView) { + if (!self._loggedInView || !self._loggedInView.child) { return true; } - return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId); + return self._loggedInView.child.canResetTimelineInRoom(roomId); }); cli.on('sync', function(state, prevState) { diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index d1ef6c2f2c..ca1fccd1f5 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -18,6 +18,7 @@ limitations under the License. 'use strict'; const React = require("react"); const ReactDOM = require("react-dom"); +import { DragDropContext } from 'react-beautiful-dnd'; import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; const GeminiScrollbar = require('react-gemini-scrollbar'); @@ -32,6 +33,8 @@ const Receipt = require('../../../utils/Receipt'); import TagOrderStore from '../../../stores/TagOrderStore'; import GroupStoreCache from '../../../stores/GroupStoreCache'; +import Modal from '../../../Modal'; + const HIDE_CONFERENCE_CHANS = true; function phraseForSection(section) { @@ -275,6 +278,103 @@ module.exports = React.createClass({ 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() { this.refreshRoomList(); }, 500), @@ -649,114 +749,116 @@ module.exports = React.createClass({ const self = this; return ( - -
- + + +
+ - + - + - + - + - { Object.keys(self.state.lists).map((tagName) => { - if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { - return ; - } - }) } + { Object.keys(self.state.lists).map((tagName) => { + if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { + return ; + } + }) } - + - -
-
+ +
+
+ ); }, }); diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 30cc64ef7a..43bc69dea2 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -35,10 +35,7 @@ module.exports = React.createClass({ displayName: 'RoomTile', propTypes: { - connectDragSource: PropTypes.func, - connectDropTarget: PropTypes.func, onClick: PropTypes.func, - isDragging: PropTypes.bool, room: PropTypes.object.isRequired, collapsed: PropTypes.bool.isRequired, @@ -256,35 +253,19 @@ module.exports = React.createClass({ directMessageIndicator = dm; } - // These props are injected by React DnD, - // as defined by your `collect` function above: - const isDragging = this.props.isDragging; - const connectDragSource = this.props.connectDragSource; - const connectDropTarget = this.props.connectDropTarget; - - - let ret = ( -
{ /* Only native elements can be wrapped in a DnD object. */ } - -
-
- - { directMessageIndicator } -
+ return +
+
+ + { directMessageIndicator }
-
- { label } - { badge } -
- { /* { incomingCallBox } */ } - { tooltip } -
- ); - - if (connectDropTarget) ret = connectDropTarget(ret); - if (connectDragSource) ret = connectDragSource(ret); - - return ret; +
+ { label } + { badge } +
+ { /* { incomingCallBox } */ } + { tooltip } +
; }, });