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