From c884c5fc33bd85610e076c9ced216051b993d2c8 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Fri, 6 Nov 2015 20:54:07 +0100
Subject: [PATCH] actually manage manual ordering; support arbitrary tags; bug
 fixes

---
 src/controllers/organisms/RoomView.js         |  2 +-
 src/skins/vector/views/molecules/RoomTile.js  | 50 ++++++++++++--
 src/skins/vector/views/organisms/RoomList.js  | 69 ++++++++++++-------
 .../vector/views/organisms/RoomSubList.js     | 52 ++++++++++++--
 4 files changed, 135 insertions(+), 38 deletions(-)

diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js
index f5a8d28f..28b15a26 100644
--- a/src/controllers/organisms/RoomView.js
+++ b/src/controllers/organisms/RoomView.js
@@ -351,7 +351,7 @@ module.exports = {
                 upload: undefined
             });
         }).done(undefined, function() {
-            // display error message
+            // TODO: display error message
         });
     },
 
diff --git a/src/skins/vector/views/molecules/RoomTile.js b/src/skins/vector/views/molecules/RoomTile.js
index ad198694..5be7ac4c 100644
--- a/src/skins/vector/views/molecules/RoomTile.js
+++ b/src/skins/vector/views/molecules/RoomTile.js
@@ -56,16 +56,50 @@ var roomTileSource = {
 
         console.log("roomTile endDrag for " + item.room.roomId + " with didDrop=" + monitor.didDrop());
 
-        if (!monitor.didDrop() || !item.targetList.props.editable) {
+        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') {
+                newOrder['order' = item.targetList.calcManualOrderTagData(item.room);
+            }
+
+            // if we moved lists or the ordering changed, add the new tag
+            if (item.targetList.props.tagName && 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() {
+                    //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
             props.roomSubList.moveRoomTile(item.room, item.originalIndex);
             if (item.targetList && item.targetList !== item.originalList) {
                 item.targetList.removeRoomTile(item.room);
             }
-            return;
-        }
-        else {
-            // When dropped on a compatible target, actually set the right tags for the new ordering
-            // persistNewOrder(item.room, dropResult.listId);
         }
     }
 };
@@ -88,6 +122,8 @@ var roomTileTarget = {
             item.targetList = props.roomSubList;
         }
 
+        if (!item.targetList.props.editable) return;
+
         if (item.targetList.props.order === 'manual') {
             if (item.room.roomId !== props.room.roomId) {
                 var roomTile = props.roomSubList.findRoomTile(props.room);
@@ -146,7 +182,7 @@ var RoomTile = React.createClass({
 
         var name;
         if (this.props.isInvite) {
-            name = this.props.room.getMember(MatrixClientPeg.get().credentials.userId).events.member.getSender();
+            name = this.props.room.getMember(myUserId).events.member.getSender();
         }
         else {
             name = this.props.room.name;
diff --git a/src/skins/vector/views/organisms/RoomList.js b/src/skins/vector/views/organisms/RoomList.js
index 2e46c42f..98b1da54 100644
--- a/src/skins/vector/views/organisms/RoomList.js
+++ b/src/skins/vector/views/organisms/RoomList.js
@@ -38,54 +38,71 @@ module.exports = React.createClass({
                            null;
 
         var RoomSubList = sdk.getComponent('organisms.RoomSubList');
+        var self = this;
 
         return (
-            <div className="mx_RoomList" onScroll={this._repositionTooltip}>
+            <div className="mx_RoomList" onScroll={self._repositionTooltip}>
                 { expandButton }
 
-                <RoomSubList list={ this.state.lists['invites'] }
+                <RoomSubList list={ self.state.lists['invites'] }
                              label="Invites"
                              editable={ false }
                              order="recent"
-                             activityMap={ this.state.activityMap }
-                             selectedRoom={ this.props.selectedRoom }
-                             collapsed={ this.props.collapsed } />
+                             activityMap={ self.state.activityMap }
+                             selectedRoom={ self.props.selectedRoom }
+                             collapsed={ self.props.collapsed } />
 
-                <RoomSubList list={ this.state.lists['favourites'] }
+                <RoomSubList list={ self.state.lists['favourite'] }
                              label="Favourites"
-                             tagname="favourites"
+                             tagName="favourite"
                              verb="favourite"
                              editable={ true }
                              order="manual"
-                             activityMap={ this.state.activityMap }
-                             selectedRoom={ this.props.selectedRoom }
-                             collapsed={ this.props.collapsed } />
+                             activityMap={ self.state.activityMap }
+                             selectedRoom={ self.props.selectedRoom }
+                             collapsed={ self.props.collapsed } />
 
-                <RoomSubList list={ this.state.lists['recents'] }
-                             label="Recents"
+                <RoomSubList list={ self.state.lists['recents'] }
+                             label="Conversations"
                              editable={ true }
                              order="recent"
-                             activityMap={ this.state.activityMap }
-                             selectedRoom={ this.props.selectedRoom }
-                             collapsed={ this.props.collapsed } />
+                             activityMap={ self.state.activityMap }
+                             selectedRoom={ self.props.selectedRoom }
+                             collapsed={ self.props.collapsed } />
 
-                <RoomSubList list={ this.state.lists['hidden'] }
-                             label="Hidden"
-                             tagname="hidden"
-                             verb="hide"
+                <RoomSubList list={ self.state.lists['lowpriority'] }
+                             label="Low priority"
+                             tagName="lowpriority"
+                             verb="deprioritize"
                              editable={ true }
                              order="recent"
-                             activityMap={ this.state.activityMap }
-                             selectedRoom={ this.props.selectedRoom }
-                             collapsed={ this.props.collapsed } />
+                             activityMap={ self.state.activityMap }
+                             selectedRoom={ self.props.selectedRoom }
+                             collapsed={ self.props.collapsed } />
 
-                <RoomSubList list={ this.state.lists['archived'] }
+                { Object.keys(self.state.lists).map(function(tagName) {
+                    if (!tagName.match(/^(invites|favourite|recents|lowpriority|archived)$/)) {
+                        return <RoomSubList list={ self.state.lists[tagName] }
+                             key={ tagName }
+                             label={ tagName }
+                             tagName={ tagName }
+                             verb={ "tag as " + tagName }
+                             editable={ true }
+                             order="manual"
+                             activityMap={ self.state.activityMap }
+                             selectedRoom={ self.props.selectedRoom }
+                             collapsed={ self.props.collapsed } />
+
+                    }
+                }) }
+
+                <RoomSubList list={ self.state.lists['archived'] }
                              label="Historical"
                              editable={ false }
                              order="recent"
-                             activityMap={ this.state.activityMap }
-                             selectedRoom={ this.props.selectedRoom }
-                             collapsed={ this.props.collapsed } />
+                             activityMap={ self.state.activityMap }
+                             selectedRoom={ self.props.selectedRoom }
+                             collapsed={ self.props.collapsed } />
             </div>
         );
     }
diff --git a/src/skins/vector/views/organisms/RoomSubList.js b/src/skins/vector/views/organisms/RoomSubList.js
index b5655e37..27cfb2ae 100644
--- a/src/skins/vector/views/organisms/RoomSubList.js
+++ b/src/skins/vector/views/organisms/RoomSubList.js
@@ -48,7 +48,7 @@ var RoomSubList = React.createClass({
     propTypes: {
         list: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
         label: React.PropTypes.string.isRequired,
-        tagname: React.PropTypes.string,
+        tagName: React.PropTypes.string,
         editable: React.PropTypes.bool,
         order: React.PropTypes.string.isRequired,
         selectedRoom: React.PropTypes.string.isRequired,
@@ -88,8 +88,9 @@ var RoomSubList = React.createClass({
     },
 
     manualComparator: function(roomA, roomB) {
-        var a = roomA.tags[this.props.tagname].order;
-        var b = roomB.tags[this.props.tagname].order;
+        if (!roomA.tags[this.props.tagName] || !roomB.tags[this.props.tagName]) return 0;
+        var a = roomA.tags[this.props.tagName].order;
+        var b = roomB.tags[this.props.tagName].order;
         return a == b ? this.recentsComparator(roomA, roomB) : ( a > b  ? 1 : -1);
     },
 
@@ -150,7 +151,50 @@ var RoomSubList = React.createClass({
             room: room,
             index: index,
         });
-    },    
+    },
+
+    calcManualOrderTagData: function(room) {
+        var index = this.state.sortedList.indexOf(room); 
+
+        // we sort rooms by the lexicographic ordering of the 'order' metadata on their tags.
+        // for convenience, we calculate this for now a floating point number between 0.0 and 1.0.
+
+        var orderA = 0.0; // by default we're next to the beginning of the list
+        if (index > 0) {
+            var prevTag = this.state.sortedList[index - 1].tags[this.props.tagName];
+            if (!prevTag) {
+                console.error("Previous room in sublist is not tagged to be in this list. This should never happen.")
+            }
+            else if (prevTag.order === undefined) {
+                console.error("Previous room in sublist has no ordering metadata. This should never happen.");
+            }
+            else {
+                orderA = prevTag.order;
+            }
+        }
+
+        var orderB = 1.0; // by default we're next to the end of the list too
+        if (index < this.state.sortedList.length - 1) {
+            var nextTag = this.state.sortedList[index + 1].tags[this.props.tagName];
+            if (!nextTag) {
+                console.error("Next room in sublist is not tagged to be in this list. This should never happen.")
+            }
+            else if (nextTag.order === undefined) {
+                console.error("Next room in sublist has no ordering metadata. This should never happen.");
+            }
+            else {
+                orderB = nextTag.order;
+            }
+        }
+
+        var order = (orderA + orderB) / 2.0;
+        if (order === orderA || order === orderB) {
+            console.error("Cannot describe new list position.  This should be incredibly unlikely.");
+            // TODO: renumber the list
+        }
+
+        return order;
+    },
 
     makeRoomTiles: function() {
         var self = this;