2015-06-23 16:41:25 +01:00
|
|
|
/*
|
|
|
|
|
Copyright 2015 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2015-06-09 17:40:42 +01:00
|
|
|
var React = require('react');
|
2015-11-04 02:25:08 +00:00
|
|
|
var DragSource = require('react-dnd').DragSource;
|
|
|
|
|
var DropTarget = require('react-dnd').DropTarget;
|
2015-06-12 17:34:17 +01:00
|
|
|
var classNames = require('classnames');
|
|
|
|
|
|
2015-09-22 18:17:19 +01:00
|
|
|
var RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile')
|
2015-06-09 17:40:42 +01:00
|
|
|
|
2015-09-22 19:09:23 +01:00
|
|
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
2015-06-22 14:48:58 +01:00
|
|
|
|
2015-09-22 18:49:04 +01:00
|
|
|
var sdk = require('matrix-react-sdk')
|
2015-08-13 19:30:02 +01:00
|
|
|
|
2015-11-04 02:25:08 +00:00
|
|
|
/**
|
|
|
|
|
* Specifies the drag source contract.
|
|
|
|
|
* Only `beginDrag` function is required.
|
|
|
|
|
*/
|
|
|
|
|
var roomTileSource = {
|
2015-11-05 11:21:45 +00:00
|
|
|
canDrag: function(props, monitor) {
|
|
|
|
|
return props.roomSubList.props.editable;
|
|
|
|
|
},
|
|
|
|
|
|
2015-11-04 02:25:08 +00:00
|
|
|
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
|
2015-11-07 02:57:56 +00:00
|
|
|
lastTargetRoom: null,
|
|
|
|
|
lastYOffset: null,
|
|
|
|
|
lastYDelta: null,
|
2015-11-04 02:25:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log("roomTile beginDrag for " + item.room.roomId);
|
|
|
|
|
|
2015-11-07 02:57:56 +00:00
|
|
|
// doing this 'correctly' with state causes react-dnd to break seemingly due to the state transitions
|
|
|
|
|
props.room._dragging = true;
|
|
|
|
|
|
2015-11-04 02:25:08 +00:00
|
|
|
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());
|
|
|
|
|
|
2015-11-07 02:57:56 +00:00
|
|
|
props.room._dragging = false;
|
|
|
|
|
if (monitor.didDrop()) {
|
|
|
|
|
monitor.getDropResult().component.forceUpdate(); // as we're not using state
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-06 20:54:07 +01:00
|
|
|
if (monitor.didDrop() && item.targetList.props.editable) {
|
|
|
|
|
// if we moved lists, remove the old tag
|
|
|
|
|
if (item.targetList !== item.originalList) {
|
|
|
|
|
// commented out attempts to set a spinner on our target component as component is actually
|
|
|
|
|
// the original source component being dragged, not our target. To fix we just need to
|
|
|
|
|
// move all of this to endDrop in the target instead. FIXME later.
|
|
|
|
|
|
|
|
|
|
//component.state.set({ spinner: component.state.spinner ? component.state.spinner++ : 1 });
|
|
|
|
|
MatrixClientPeg.get().deleteRoomTag(item.room.roomId, item.originalList.props.tagName).finally(function() {
|
|
|
|
|
//component.state.set({ spinner: component.state.spinner-- });
|
|
|
|
|
}).fail(function(err) {
|
|
|
|
|
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
|
|
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
|
|
|
title: "Failed to remove tag " + item.originalList.props.tagName + " from room",
|
|
|
|
|
description: err.toString()
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var newOrder= {};
|
|
|
|
|
if (item.targetList.props.order === 'manual') {
|
2015-11-06 21:00:34 +01:00
|
|
|
newOrder['order'] = item.targetList.calcManualOrderTagData(item.room);
|
2015-11-06 20:54:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if we moved lists or the ordering changed, add the new tag
|
2015-11-06 21:33:28 +01:00
|
|
|
if (item.targetList.props.tagName && (item.targetList !== item.originalList || newOrder)) {
|
2015-11-06 20:54:07 +01:00
|
|
|
//component.state.set({ spinner: component.state.spinner ? component.state.spinner++ : 1 });
|
|
|
|
|
MatrixClientPeg.get().setRoomTag(item.room.roomId, item.targetList.props.tagName, newOrder).finally(function() {
|
|
|
|
|
//component.state.set({ spinner: component.state.spinner-- });
|
|
|
|
|
}).fail(function(err) {
|
|
|
|
|
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
|
|
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
|
|
|
title: "Failed to add tag " + item.targetList.props.tagName + " to room",
|
|
|
|
|
description: err.toString()
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// cancel the drop and reset our original position
|
2015-11-06 23:30:57 +01:00
|
|
|
console.log("cancelling drop & drag");
|
2015-11-04 02:25:08 +00:00
|
|
|
props.roomSubList.moveRoomTile(item.room, item.originalIndex);
|
|
|
|
|
if (item.targetList && item.targetList !== item.originalList) {
|
|
|
|
|
item.targetList.removeRoomTile(item.room);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var roomTileTarget = {
|
|
|
|
|
canDrop: function() {
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
hover: function(props, monitor) {
|
|
|
|
|
var item = monitor.getItem();
|
2015-11-07 02:57:56 +00:00
|
|
|
var off = monitor.getClientOffset();
|
|
|
|
|
// console.log("hovering on room " + props.room.roomId + ", isOver=" + monitor.isOver());
|
2015-11-04 02:25:08 +00:00
|
|
|
|
|
|
|
|
//console.log("item.targetList=" + item.targetList + ", roomSubList=" + props.roomSubList);
|
|
|
|
|
|
2015-11-06 23:30:57 +01:00
|
|
|
var switchedTarget = false;
|
2015-11-04 02:25:08 +00:00
|
|
|
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.
|
2015-11-07 02:57:56 +00:00
|
|
|
console.log("switched target sublist");
|
2015-11-06 23:30:57 +01:00
|
|
|
switchedTarget = true;
|
2015-11-04 02:25:08 +00:00
|
|
|
item.targetList.removeRoomTile(item.room);
|
|
|
|
|
item.targetList = props.roomSubList;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-06 20:54:07 +01:00
|
|
|
if (!item.targetList.props.editable) return;
|
|
|
|
|
|
2015-11-05 11:21:45 +00:00
|
|
|
if (item.targetList.props.order === 'manual') {
|
2015-11-07 02:57:56 +00:00
|
|
|
if (item.room.roomId !== props.room.roomId && props.room !== item.lastTargetRoom) {
|
|
|
|
|
// find the offset of the target tile in the list.
|
2015-11-05 11:21:45 +00:00
|
|
|
var roomTile = props.roomSubList.findRoomTile(props.room);
|
2015-11-07 02:57:56 +00:00
|
|
|
// shuffle the list to add our tile to that position.
|
2015-11-05 11:21:45 +00:00
|
|
|
props.roomSubList.moveRoomTile(item.room, roomTile.index);
|
|
|
|
|
}
|
2015-11-07 02:57:56 +00:00
|
|
|
|
|
|
|
|
// stop us from flickering between our droptarget and the previous room.
|
|
|
|
|
// whenever the cursor changes direction we have to reset the flicker-damping.
|
|
|
|
|
|
|
|
|
|
var yDelta = off.y - item.lastYOffset;
|
|
|
|
|
|
|
|
|
|
if ((yDelta > 0 && item.lastYDelta < 0) ||
|
|
|
|
|
(yDelta < 0 && item.lastYDelta > 0))
|
|
|
|
|
{
|
|
|
|
|
// the cursor changed direction - forget our previous room
|
|
|
|
|
item.lastTargetRoom = null;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// track the last room we were hovering over so we can stop
|
|
|
|
|
// bouncing back and forth if the droptarget is narrower than
|
|
|
|
|
// the other list items. The other way to do this would be
|
|
|
|
|
// to reduce the size of the hittarget on the list items, but
|
|
|
|
|
// can't see an easy way to do that.
|
|
|
|
|
item.lastTargetRoom = props.room;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (yDelta) item.lastYDelta = yDelta;
|
|
|
|
|
item.lastYOffset = off.y;
|
2015-11-05 11:21:45 +00:00
|
|
|
}
|
2015-11-06 23:30:57 +01:00
|
|
|
else if (switchedTarget) {
|
2015-11-05 11:21:45 +00:00
|
|
|
if (!props.roomSubList.findRoomTile(item.room).room) {
|
|
|
|
|
// add to the list in the right place
|
|
|
|
|
props.roomSubList.moveRoomTile(item.room, 0);
|
|
|
|
|
}
|
2015-11-06 23:30:57 +01:00
|
|
|
// we have to sort the list whatever to recalculate it
|
|
|
|
|
props.roomSubList.sortList();
|
2015-11-04 02:25:08 +00:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var RoomTile = React.createClass({
|
2015-06-19 16:21:09 +01:00
|
|
|
displayName: 'RoomTile',
|
2015-06-19 12:53:48 +01:00
|
|
|
mixins: [RoomTileController],
|
2015-10-11 02:09:14 +01:00
|
|
|
|
2015-11-04 02:25:08 +00:00
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
|
2015-10-11 02:09:14 +01:00
|
|
|
getInitialState: function() {
|
|
|
|
|
return( { hover : false });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onMouseEnter: function() {
|
|
|
|
|
this.setState( { hover : true });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onMouseLeave: function() {
|
|
|
|
|
this.setState( { hover : false });
|
|
|
|
|
},
|
|
|
|
|
|
2015-06-09 17:40:42 +01:00
|
|
|
render: function() {
|
2015-11-07 02:57:56 +00:00
|
|
|
// if (this.props.clientOffset) {
|
|
|
|
|
// //console.log("room " + this.props.room.roomId + " has dropTarget clientOffset " + this.props.clientOffset.x + "," + this.props.clientOffset.y);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
if (this.props.room._dragging) {
|
|
|
|
|
var RoomDropTarget = sdk.getComponent("molecules.RoomDropTarget");
|
|
|
|
|
return <RoomDropTarget placeholder={true}/>;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-22 14:48:58 +01:00
|
|
|
var myUserId = MatrixClientPeg.get().credentials.userId;
|
2015-11-03 14:35:39 +00:00
|
|
|
var me = this.props.room.currentState.members[myUserId];
|
2015-06-12 17:34:17 +01:00
|
|
|
var classes = classNames({
|
|
|
|
|
'mx_RoomTile': true,
|
2015-07-13 01:51:24 +01:00
|
|
|
'mx_RoomTile_selected': this.props.selected,
|
|
|
|
|
'mx_RoomTile_unread': this.props.unread,
|
|
|
|
|
'mx_RoomTile_highlight': this.props.highlight,
|
2015-11-03 14:35:39 +00:00
|
|
|
'mx_RoomTile_invited': (me && me.membership == 'invite'),
|
2015-06-12 17:34:17 +01:00
|
|
|
});
|
2015-10-24 02:02:33 +01:00
|
|
|
|
|
|
|
|
var name;
|
|
|
|
|
if (this.props.isInvite) {
|
2015-11-06 20:54:07 +01:00
|
|
|
name = this.props.room.getMember(myUserId).events.member.getSender();
|
2015-10-24 02:02:33 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2015-11-04 14:15:57 +00:00
|
|
|
// XXX: We should never display raw room IDs, but sometimes the room name js sdk gives is undefined
|
|
|
|
|
name = this.props.room.name || this.props.room.roomId;
|
2015-10-24 02:02:33 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-30 18:20:29 +00:00
|
|
|
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
|
2015-07-14 02:13:00 +01:00
|
|
|
var badge;
|
2015-07-18 00:48:22 +01:00
|
|
|
if (this.props.highlight) {
|
2015-07-18 01:21:56 +01:00
|
|
|
badge = <div className="mx_RoomTile_badge"/>;
|
2015-07-18 00:48:22 +01:00
|
|
|
}
|
|
|
|
|
/*
|
2015-07-14 02:13:00 +01:00
|
|
|
if (this.props.highlight) {
|
|
|
|
|
badge = <div className="mx_RoomTile_badge">!</div>;
|
|
|
|
|
}
|
|
|
|
|
else if (this.props.unread) {
|
|
|
|
|
badge = <div className="mx_RoomTile_badge">1</div>;
|
|
|
|
|
}
|
2015-07-16 22:39:38 +01:00
|
|
|
var nameCell;
|
|
|
|
|
if (badge) {
|
|
|
|
|
nameCell = <div className="mx_RoomTile_nameBadge"><div className="mx_RoomTile_name">{name}</div><div className="mx_RoomTile_badgeCell">{badge}</div></div>;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nameCell = <div className="mx_RoomTile_name">{name}</div>;
|
|
|
|
|
}
|
2015-07-18 00:48:22 +01:00
|
|
|
*/
|
2015-10-11 02:09:14 +01:00
|
|
|
|
2015-10-11 13:54:38 +01:00
|
|
|
var label;
|
2015-10-11 02:09:14 +01:00
|
|
|
if (!this.props.collapsed) {
|
2015-10-24 02:02:33 +01:00
|
|
|
var className = 'mx_RoomTile_name' + (this.props.isInvite ? ' mx_RoomTile_invite' : '');
|
|
|
|
|
label = <div className={ className }>{name}</div>;
|
2015-10-11 02:09:14 +01:00
|
|
|
}
|
|
|
|
|
else if (this.state.hover) {
|
2015-10-11 13:54:38 +01:00
|
|
|
var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
|
2015-10-11 15:00:43 +01:00
|
|
|
label = <RoomTooltip room={this.props.room}/>;
|
2015-10-11 02:09:14 +01:00
|
|
|
}
|
|
|
|
|
|
2015-09-22 18:49:04 +01:00
|
|
|
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
|
2015-11-04 02:25:08 +00:00
|
|
|
|
|
|
|
|
// 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(
|
2015-10-11 02:09:14 +01:00
|
|
|
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
2015-08-13 19:30:02 +01:00
|
|
|
<div className="mx_RoomTile_avatar">
|
2015-10-23 02:32:49 +01:00
|
|
|
<RoomAvatar room={this.props.room} width="24" height="24" />
|
2015-08-13 19:30:02 +01:00
|
|
|
{ badge }
|
|
|
|
|
</div>
|
2015-10-11 13:54:38 +01:00
|
|
|
{ label }
|
2015-06-09 17:40:42 +01:00
|
|
|
</div>
|
2015-11-04 02:25:08 +00:00
|
|
|
));
|
2015-06-09 17:40:42 +01:00
|
|
|
}
|
|
|
|
|
});
|
2015-11-04 02:25:08 +00:00
|
|
|
|
|
|
|
|
// Export the wrapped version, inlining the 'collect' functions
|
|
|
|
|
// to more closely resemble the ES7
|
|
|
|
|
module.exports =
|
2015-11-07 02:57:56 +00:00
|
|
|
DropTarget('RoomTile', roomTileTarget, function(connect, monitor) {
|
2015-11-04 02:25:08 +00:00
|
|
|
return {
|
|
|
|
|
// Call this function inside render()
|
|
|
|
|
// to let React DnD handle the drag events:
|
|
|
|
|
connectDropTarget: connect.dropTarget(),
|
2015-11-07 02:57:56 +00:00
|
|
|
isOver: monitor.isOver(),
|
2015-11-04 02:25:08 +00:00
|
|
|
}
|
|
|
|
|
})(
|
|
|
|
|
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));
|