diff --git a/src/skins/vector/views/molecules/RoomTile.js b/src/skins/vector/views/molecules/RoomTile.js
index bdaa621d19..383c08d161 100644
--- a/src/skins/vector/views/molecules/RoomTile.js
+++ b/src/skins/vector/views/molecules/RoomTile.js
@@ -17,6 +17,8 @@ limitations under the License.
'use strict';
var React = require('react');
+var DragSource = require('react-dnd').DragSource;
+var DropTarget = require('react-dnd').DropTarget;
var classNames = require('classnames');
var RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile')
@@ -25,10 +27,89 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var sdk = require('matrix-react-sdk')
-module.exports = React.createClass({
+/**
+ * Specifies the drag source contract.
+ * Only `beginDrag` function is required.
+ */
+var roomTileSource = {
+ beginDrag: function (props) {
+ // Return the data describing the dragged item
+ var item = {
+ room: props.room,
+ originalList: props.roomSubList,
+ originalIndex: props.roomSubList.findRoomTile(props.room).index,
+ targetList: props.roomSubList, // at first target is same as original
+ };
+
+ console.log("roomTile beginDrag for " + item.room.roomId);
+
+ return item;
+ },
+
+ endDrag: function (props, monitor, component) {
+ var item = monitor.getItem();
+ var dropResult = monitor.getDropResult();
+
+ console.log("roomTile endDrag for " + item.room.roomId + " with didDrop=" + monitor.didDrop());
+
+ if (!monitor.didDrop() || !item.targetList.props.editable) {
+ props.roomSubList.moveRoomTile(item.room, item.originalIndex);
+ if (item.targetList && item.targetList !== item.originalList) {
+ item.targetList.removeRoomTile(item.room);
+ }
+ return;
+ }
+ else {
+ // if it's not manual ordering, we'll need to position the tile correctly here according to the right ordering
+
+ // When dropped on a compatible target, actually set the right tags for the new ordering
+ // persistNewOrder(item.room, dropResult.listId);
+ }
+ }
+};
+
+var roomTileTarget = {
+ canDrop: function() {
+ return false;
+ },
+
+ hover: function(props, monitor) {
+ var item = monitor.getItem();
+ console.log("hovering on room " + props.room.roomId + ", isOver=" + monitor.isOver());
+
+ //console.log("item.targetList=" + item.targetList + ", roomSubList=" + props.roomSubList);
+
+ if (item.targetList !== props.roomSubList) {
+ // we've switched target, so remove the tile from the previous target.
+ // n.b. the previous target might actually be the source list.
+ item.targetList.removeRoomTile(item.room);
+ item.targetList = props.roomSubList;
+ }
+
+ if (item.targetList.props.order === 'manual' && item.room.roomId !== props.room.roomId) {
+ var roomTile = props.roomSubList.findRoomTile(props.room);
+ props.roomSubList.moveRoomTile(item.room, roomTile.index);
+ }
+ },
+};
+
+var RoomTile = React.createClass({
displayName: 'RoomTile',
mixins: [RoomTileController],
+ propTypes: {
+ connectDragSource: React.PropTypes.func.isRequired,
+ connectDropTarget: React.PropTypes.func.isRequired,
+ isDragging: React.PropTypes.bool.isRequired,
+ room: React.PropTypes.object.isRequired,
+ collapsed: React.PropTypes.bool.isRequired,
+ selected: React.PropTypes.bool.isRequired,
+ unread: React.PropTypes.bool.isRequired,
+ highlight: React.PropTypes.bool.isRequired,
+ isInvite: React.PropTypes.bool.isRequired,
+ roomSubList: React.PropTypes.object.isRequired,
+ },
+
getInitialState: function() {
return( { hover : false });
},
@@ -92,7 +173,14 @@ module.exports = React.createClass({
}
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
- return (
+
+ // These props are injected by React DnD,
+ // as defined by your `collect` function above:
+ var isDragging = this.props.isDragging;
+ var connectDragSource = this.props.connectDragSource;
+ var connectDropTarget = this.props.connectDropTarget;
+
+ return connectDragSource(connectDropTarget(
@@ -100,6 +188,26 @@ module.exports = React.createClass({
{ label }
- );
+ ));
}
});
+
+// Export the wrapped version, inlining the 'collect' functions
+// to more closely resemble the ES7
+module.exports =
+DropTarget('RoomTile', roomTileTarget, function(connect) {
+ return {
+ // Call this function inside render()
+ // to let React DnD handle the drag events:
+ connectDropTarget: connect.dropTarget(),
+ }
+})(
+DragSource('RoomTile', roomTileSource, function(connect, monitor) {
+ return {
+ // Call this function inside render()
+ // to let React DnD handle the drag events:
+ connectDragSource: connect.dragSource(),
+ // You can ask the monitor about the current drag state:
+ isDragging: monitor.isDragging()
+ };
+})(RoomTile));
\ No newline at end of file
diff --git a/src/skins/vector/views/organisms/RoomSubList.js b/src/skins/vector/views/organisms/RoomSubList.js
index b8747ecf22..68d55c7b90 100644
--- a/src/skins/vector/views/organisms/RoomSubList.js
+++ b/src/skins/vector/views/organisms/RoomSubList.js
@@ -17,10 +17,32 @@ limitations under the License.
'use strict';
var React = require('react');
+var DropTarget = require('react-dnd').DropTarget;
var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher');
-module.exports = React.createClass({
+var roomListTarget = {
+ canDrop: function() {
+ return true;
+ },
+
+ hover: function(props, monitor, component) {
+ var item = monitor.getItem();
+
+ if (component.state.sortedList.length == 0 && props.editable) {
+ console.log("hovering on sublist " + props.label + ", isOver=" + monitor.isOver());
+
+ if (item.targetList !== component) {
+ item.targetList.removeRoomTile(item.room);
+ item.targetList = component;
+ }
+
+ component.moveRoomTile(item.room, 0);
+ }
+ },
+};
+
+var RoomSubList = React.createClass({
displayName: 'RoomSubList',
propTypes: {
@@ -80,14 +102,64 @@ module.exports = React.createClass({
this.setState({ sortedList: list.sort(comparator) });
},
+ moveRoomTile: function(room, atIndex) {
+ console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex);
+ //console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms));
+ var found = this.findRoomTile(room);
+ var rooms = this.state.sortedList;
+ if (found.room) {
+ console.log("removing at index " + found.index + " and adding at index " + atIndex);
+ rooms.splice(found.index, 1);
+ rooms.splice(atIndex, 0, found.room);
+ }
+ else {
+ console.log("Adding at index " + atIndex);
+ rooms.splice(atIndex, 0, room);
+ }
+ this.setState({ sortedList: rooms });
+ // console.log("moveRoomTile after: " + JSON.stringify(this.state.rooms));
+ },
+
+ // XXX: this isn't invoked via a property method but indirectly via
+ // the roomList property method. Unsure how evil this is.
+ removeRoomTile: function(room) {
+ console.log("remove room " + room.roomId);
+ var found = this.findRoomTile(room);
+ var rooms = this.state.sortedList;
+ if (found.room) {
+ rooms.splice(found.index, 1);
+ }
+ else {
+ console.log*("Can't remove room " + room.roomId + " - can't find it");
+ }
+ this.setState({ sortedList: rooms });
+ },
+
+ findRoomTile: function(room) {
+ var index = this.state.sortedList.indexOf(room);
+ if (index >= 0) {
+ console.log("found: room: " + room + " with id " + room.roomId);
+ }
+ else {
+ console.log("didn't find room");
+ room = null;
+ }
+ return ({
+ room: room,
+ index: index,
+ });
+ },
+
makeRoomTiles: function() {
var self = this;
var RoomTile = sdk.getComponent("molecules.RoomTile");
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 (
0 || this.props.editable) {
- return (
+ return connectDropTarget(
-
{ this.props.label }
+
{ this.props.collapsed ? '' : this.props.label }
{ this.makeRoomTiles() }
@@ -122,3 +197,11 @@ module.exports = React.createClass({
}
});
+// Export the wrapped version, inlining the 'collect' functions
+// to more closely resemble the ES7
+module.exports =
+DropTarget('RoomTile', roomListTarget, function(connect) {
+ return {
+ connectDropTarget: connect.dropTarget(),
+ }
+})(RoomSubList);
diff --git a/src/skins/vector/views/pages/MatrixChat.js b/src/skins/vector/views/pages/MatrixChat.js
index 0cf754c23a..f34b6d4fd5 100644
--- a/src/skins/vector/views/pages/MatrixChat.js
+++ b/src/skins/vector/views/pages/MatrixChat.js
@@ -17,6 +17,8 @@ limitations under the License.
'use strict';
var React = require('react');
+var DragDropContext = require('react-dnd').DragDropContext;
+var HTML5Backend = require('react-dnd/modules/backends/HTML5');
var sdk = require('matrix-react-sdk')
var MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/MatrixChat')
@@ -28,7 +30,7 @@ var dis = require('matrix-react-sdk/lib/dispatcher');
var Matrix = require("matrix-js-sdk");
var ContextualMenu = require("../../../../ContextualMenu");
-module.exports = React.createClass({
+var MatrixChat = React.createClass({
displayName: 'MatrixChat',
mixins: [MatrixChatController],
@@ -172,3 +174,5 @@ module.exports = React.createClass({
}
}
});
+
+module.exports = DragDropContext(HTML5Backend)(MatrixChat);
\ No newline at end of file