From be02ac3bc6b3340a8060e7c314c0baf77bdbce26 Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Fri, 17 Mar 2017 12:06:48 +0000
Subject: [PATCH 1/2] Fix people section again

 - Alter CSS due to removed mx_RoomDropTarget_avatar. The avatar was removed because it didn't particularly add anything and we needed space for "Drop here to tag as direct chat", which is quite long.
 - Use guessAndSetDMRoom as a convenience method for guessing the DM member and setting the state.
 - Do evil hacks to make DNDRoomTile do dragging of RoomTiles to and from the People section. Dragging a DM to and from Rooms/Favourites/Low Priority now works as one would expect. This is still not ideal however because edge cases exist where you have more than one tag set and then you drag a DM from "Favourites" to "Rooms" and the tile ends up in "People". This would require setting multiple tags, and breaks the 1-1 mapping between tags and sections even further. Ultimately the UI needs a rework.
---
 .../context_menus/RoomTileContextMenu.js      | 35 +++--------
 src/components/views/rooms/DNDRoomTile.js     | 61 ++++++++++++++-----
 src/components/views/rooms/RoomDropTarget.js  |  1 -
 .../views/rooms/_RoomDropTarget.scss          | 11 +---
 4 files changed, 57 insertions(+), 51 deletions(-)

diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js
index 289121f1..5b6cec88 100644
--- a/src/components/views/context_menus/RoomTileContextMenu.js
+++ b/src/components/views/context_menus/RoomTileContextMenu.js
@@ -138,31 +138,16 @@ module.exports = React.createClass({
 
         if (MatrixClientPeg.get().isGuest()) return;
 
-        let newTarget;
-        if (newIsDirectMessage) {
-            const guessedTarget = Rooms.guessDMRoomTarget(
-                this.props.room,
-                this.props.room.getMember(MatrixClientPeg.get().credentials.userId),
-            );
-            newTarget = guessedTarget.userId;
-        } else {
-            newTarget = null;
-        }
-
-        // give some time for the user to see the icon change first, since
-        // this will hide the context menu once it completes
-        q.delay(500).done(() => {
-            return Rooms.setDMRoom(this.props.room.roomId, newTarget).finally(() => {
-                // Close the context menu
-                if (this.props.onFinished) {
-                    this.props.onFinished();
-                };
-            }, (err) => {
-                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                Modal.createDialog(ErrorDialog, {
-                    title: "Failed to set Direct Message status of room",
-                    description: err.toString()
-                });
+        Rooms.guessAndSetDMRoom(this.props.room, newIsDirectMessage).finally(() => {
+            // Close the context menu
+            if (this.props.onFinished) {
+                this.props.onFinished();
+            };
+        }, (err) => {
+            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+            Modal.createDialog(ErrorDialog, {
+                title: "Failed to set Direct Message status of room",
+                description: err.toString()
             });
         });
     },
diff --git a/src/components/views/rooms/DNDRoomTile.js b/src/components/views/rooms/DNDRoomTile.js
index 6296552d..2fcdb47d 100644
--- a/src/components/views/rooms/DNDRoomTile.js
+++ b/src/components/views/rooms/DNDRoomTile.js
@@ -16,14 +16,16 @@ limitations under the License.
 
 'use strict';
 
-var React = require('react');
-var DragSource = require('react-dnd').DragSource;
-var DropTarget = require('react-dnd').DropTarget;
+import React from 'react';
+import {DragSource} from 'react-dnd';
+import {DropTarget} from 'react-dnd';
 
-var dis = require("matrix-react-sdk/lib/dispatcher");
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var sdk = require('matrix-react-sdk');
-var RoomTile = require('matrix-react-sdk/lib/components/views/rooms/RoomTile');
+import dis from 'matrix-react-sdk/lib/dispatcher';
+import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
+import sdk from 'matrix-react-sdk';
+import RoomTile from 'matrix-react-sdk/lib/components/views/rooms/RoomTile';
+import * as Rooms from 'matrix-react-sdk/lib/Rooms';
+import Modal from 'matrix-react-sdk/lib/Modal';
 
 /**
  * Defines a new Component, DNDRoomTile that wraps RoomTile, making it draggable.
@@ -72,22 +74,49 @@ var roomTileSource = {
             item.targetList.forceUpdate(); // as we're not using state
         }
 
+        const prevTag = item.originalList.props.tagName;
+        const newTag = item.targetList.props.tagName;
+
         if (monitor.didDrop() && item.targetList.props.editable) {
+            // Evil hack to get DMs behaving
+            if ((prevTag === undefined && newTag === 'im.vector.fake.direct') ||
+                (prevTag === 'im.vector.fake.direct' && newTag === undefined)
+            ) {
+                Rooms.guessAndSetDMRoom(
+                    item.room, newTag === 'im.vector.fake.direct',
+                ).done(() => {
+                    item.originalList.removeRoomTile(item.room);
+                }, (err) => {
+                    const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+                    console.error("Failed to set direct chat tag " + err);
+                    Modal.createDialog(ErrorDialog, {
+                        title: "Error",
+                        description: "Failed to set direct chat tag",
+                    });
+                });
+                return;
+            }
+
+            // 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 (item.targetList !== item.originalList && item.originalList.props.tagName) {
+            if (prevTag && prevTag !== 'im.vector.fake.direct' &&
+                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() {
+                MatrixClientPeg.get().deleteRoomTag(item.room.roomId, prevTag).finally(function() {
                     //component.state.set({ spinner: component.state.spinner-- });
                 }).fail(function(err) {
                     var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                    console.error("Failed to remove tag " + item.originalList.props.tagName + " from room: " + err);
+                    console.error("Failed to remove tag " + prevTag + " from room: " + err);
                     Modal.createDialog(ErrorDialog, {
                         title: "Error",
-                        description: "Failed to remove tag " + item.originalList.props.tagName + " from room",
+                        description: "Failed to remove tag " + prevTag + " from room",
                     });
                 });
             }
@@ -98,16 +127,18 @@ var roomTileSource = {
             }
 
             // if we moved lists or the ordering changed, add the new tag
-            if (item.targetList.props.tagName && (item.targetList !== item.originalList || newOrder)) {
+            if (newTag && newTag !== 'im.vector.fake.direct' &&
+                (item.targetList !== item.originalList || newOrder)
+            ) {
                 //component.state.set({ spinner: component.state.spinner ? component.state.spinner++ : 1 });
-                MatrixClientPeg.get().setRoomTag(item.room.roomId, item.targetList.props.tagName, newOrder).finally(function() {
+                MatrixClientPeg.get().setRoomTag(item.room.roomId, newTag, newOrder).finally(function() {
                     //component.state.set({ spinner: component.state.spinner-- });
                 }).fail(function(err) {
                     var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                    console.error("Failed to add tag " + item.targetList.props.tagName + " to room: " + err);
+                    console.error("Failed to add tag " + newTag + " to room: " + err);
                     Modal.createDialog(ErrorDialog, {
                         title: "Error",
-                        description: "Failed to add tag " + item.targetList.props.tagName + " to room",
+                        description: "Failed to add tag " + newTag + " to room",
                     });
                 });
             }
diff --git a/src/components/views/rooms/RoomDropTarget.js b/src/components/views/rooms/RoomDropTarget.js
index 789ba8fa..1c5eb3c1 100644
--- a/src/components/views/rooms/RoomDropTarget.js
+++ b/src/components/views/rooms/RoomDropTarget.js
@@ -31,7 +31,6 @@ module.exports = React.createClass({
         else {
             return (
                 <div className="mx_RoomDropTarget">
-                    <div className="mx_RoomDropTarget_avatar"></div>
                     <div className="mx_RoomDropTarget_label">
                         { this.props.label }
                     </div>
diff --git a/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss b/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss
index e91658e8..e0a50a95 100644
--- a/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss
+++ b/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss
@@ -38,21 +38,12 @@ limitations under the License.
     padding-bottom: 1px;
 }
 
-.mx_RoomDropTarget_avatar {
-    background-color: $primary-bg-color;
-    border-radius: 24px;
-    width: 24px;
-    height: 24px;
-    float: left;
-    margin-left: 7px;
-    margin-right: 7px;
-}
-
 .mx_RoomDropTarget_label {
     position: relative;
     margin-top: 3px;
     line-height: 21px;
     z-index: 1;
+    text-align: center;
 }
 
 .collapsed .mx_RoomDropTarget_avatar {

From abc5b2d5f401344388c6e819447ff4b21aca44fc Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Mon, 27 Mar 2017 09:44:33 +0100
Subject: [PATCH 2/2] UI delay in UI

---
 src/components/views/context_menus/RoomTileContextMenu.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js
index 5b6cec88..d981a367 100644
--- a/src/components/views/context_menus/RoomTileContextMenu.js
+++ b/src/components/views/context_menus/RoomTileContextMenu.js
@@ -138,7 +138,9 @@ module.exports = React.createClass({
 
         if (MatrixClientPeg.get().isGuest()) return;
 
-        Rooms.guessAndSetDMRoom(this.props.room, newIsDirectMessage).finally(() => {
+        Rooms.guessAndSetDMRoom(
+            this.props.room, newIsDirectMessage
+        ).delay(500).finally(() => {
             // Close the context menu
             if (this.props.onFinished) {
                 this.props.onFinished();