From 046e6e5d86db59016999dd0c4081fcf68278f791 Mon Sep 17 00:00:00 2001
From: Robin Ward <robin.ward@gmail.com>
Date: Mon, 20 May 2013 16:52:37 -0400
Subject: [PATCH] Simplified grouping on user views, fixed issue with messages
 not loading on initial load. Really simplified the restricted user route.

---
 .../user_private_messages_controller.js       |   7 +-
 .../javascripts/discourse/models/user.js      | 167 ++++--------------
 .../discourse/models/user_action.js           |  20 +--
 .../discourse/models/user_action_stat.js      |  21 ++-
 .../routes/discourse_restricted_user_route.js |  20 +--
 .../discourse/routes/user_activity_route.js   |  14 +-
 .../routes/user_private_messages_route.js     |  11 +-
 .../discourse/routes/user_route.js            |   7 +-
 .../templates/user/activity.js.handlebars     |   1 -
 app/serializers/user_serializer.rb            |  24 +--
 10 files changed, 92 insertions(+), 200 deletions(-)

diff --git a/app/assets/javascripts/discourse/controllers/user_private_messages_controller.js b/app/assets/javascripts/discourse/controllers/user_private_messages_controller.js
index 83d44e1e6..6607de7f9 100644
--- a/app/assets/javascripts/discourse/controllers/user_private_messages_controller.js
+++ b/app/assets/javascripts/discourse/controllers/user_private_messages_controller.js
@@ -8,13 +8,8 @@
 **/
 Discourse.UserPrivateMessagesController = Discourse.ObjectController.extend({
 
-  editPreferences: function() {
-    Discourse.URL.routeTo("/users/" + (this.get('content.username_lower')) + "/preferences");
-  },
-
   composePrivateMessage: function() {
-    var composerController;
-    composerController = Discourse.get('router.composerController');
+    var composerController = Discourse.get('router.composerController');
     return composerController.open({
       action: Discourse.Composer.PRIVATE_MESSAGE,
       archetypeId: 'private_message',
diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js
index 9fa45098f..f9eb8f511 100644
--- a/app/assets/javascripts/discourse/models/user.js
+++ b/app/assets/javascripts/discourse/models/user.js
@@ -187,6 +187,7 @@ Discourse.User = Discourse.Model.extend({
     if (Discourse.UserAction.statGroups[filter]) {
       filter = Discourse.UserAction.statGroups[filter].join(",");
     }
+
     this.set('streamFilter', filter);
     this.set('stream', Em.A());
     this.set('totalItems', 0);
@@ -254,17 +255,12 @@ Discourse.User = Discourse.Model.extend({
     @property statsCountNonPM
     @type {Integer}
   **/
-  statsCountNonPM: (function() {
-    var stats, total;
-    total = 0;
-    if (!(stats = this.get('stats'))) return 0;
-    this.get('stats').each(function(s) {
-      if (!s.get("isPM")) {
-        total += parseInt(s.count, 10);
-      }
+  statsCountNonPM: function() {
+    if (this.blank('statsExcludingPms')) return 0;
+    return this.get('statsExcludingPms').getEach('count').reduce(function (accum, val) {
+      return accum + val;
     });
-    return total;
-  }).property('stats.@each'),
+  }.property('statsExcludingPms.@each.count'),
 
   /**
   The user's stats, excluding PMs.
@@ -272,17 +268,10 @@ Discourse.User = Discourse.Model.extend({
     @property statsExcludingPms
     @type {Array}
   **/
-  statsExcludingPms: (function() {
-    var r;
-    r = [];
-    if (this.blank('stats')) return r;
-    this.get('stats').each(function(s) {
-      if (!s.get('isPM')) {
-        return r.push(s);
-      }
-    });
-    return r;
-  }).property('stats.@each'),
+  statsExcludingPms: function() {
+    if (this.blank('stats')) return [];
+    return this.get('stats').rejectProperty('isPM');
+  }.property('stats.@each.isPM'),
 
   /**
   This user's stats, only including PMs.
@@ -290,66 +279,10 @@ Discourse.User = Discourse.Model.extend({
     @property statsPmsOnly
     @type {Array}
   **/
-  statsPmsOnly: (function() {
-    var r;
-    r = [];
-    if (this.blank('stats')) return r;
-    this.get('stats').each(function(s) {
-      if (s.get('isPM')) return r.push(s);
-    });
-    return r;
-  }).property('stats.@each'),
-
-  /**
-  Number of items in this user's inbox.
-
-    @property inboxCount
-    @type {Integer}
-  **/
-  inboxCount: (function() {
-    var r;
-    r = 0;
-    this.get('stats').each(function(s) {
-      if (s.action_type === Discourse.UserAction.GOT_PRIVATE_MESSAGE) {
-        r = s.count;
-        return false;
-      }
-    });
-    return r;
-  }).property('stats.@each'),
-
-  /**
-    Number of items this user has sent.
-
-    @property sentItemsCount
-    @type {Integer}
-  **/
-  sentItemsCount: function() {
-    var r;
-    r = 0;
-    this.get('stats').each(function(s) {
-      if (s.action_type === Discourse.UserAction.NEW_PRIVATE_MESSAGE) {
-        r = s.count;
-        return false;
-      }
-    });
-    return r;
-  }.property('stats.@each'),
-
-  onDetailsLoaded: function(callback){
-    var _this = this;
-    this.set("loading",false);
-
-    if(callback){
-      this.onDetailsLoadedCallbacks = this.onDetailsLoadedCallbacks || [];
-      this.onDetailsLoadedCallbacks.push(callback);
-    } else {
-      var callbacks = this.onDetailsLoadedCallbacks;
-      $.each(callbacks, function(){
-        this.apply(_this);
-      });
-    }
-  },
+  statsPmsOnly: function() {
+    if (this.blank('stats')) return [];
+    return this.get('stats').filterProperty('isPM');
+  }.property('stats.@each.isPM'),
 
   /**
     Load extra details for the user
@@ -358,33 +291,25 @@ Discourse.User = Discourse.Model.extend({
   **/
   loadDetails: function() {
 
-    this.set("loading",true);
+    this.set('loading', true);
+
     // Check the preload store first
     var user = this;
-    var username = this.get('username');
-    PreloadStore.getAndRemove("user_" + username, function() {
+    var username = user.get('username');
+
+    return PreloadStore.getAndRemove("user_" + username, function() {
       return Discourse.ajax("/users/" + username + '.json');
     }).then(function (json) {
+
       // Create a user from the resulting JSON
       json.user.stats = Discourse.User.groupStats(json.user.stats.map(function(s) {
-        var stat = Em.Object.create(s);
-        stat.set('isPM', stat.get('action_type') === Discourse.UserAction.NEW_PRIVATE_MESSAGE ||
-                         stat.get('action_type') === Discourse.UserAction.GOT_PRIVATE_MESSAGE);
-        stat.set('description', Em.String.i18n('user_action_groups.' + stat.get('action_type')));
-        return stat;
+        if (s.count) s.count = parseInt(s.count, 10);
+        return Discourse.UserActionStat.create(s);
       }));
 
-      var count = 0;
-      if (json.user.stream) {
-        count = json.user.stream.length;
-        json.user.stream = Discourse.UserAction.collapseStream(json.user.stream.map(function(ua) {
-          return Discourse.UserAction.create(ua);
-        }));
-      }
-
       user.setProperties(json.user);
-      user.set('totalItems', count);
-      user.onDetailsLoaded();
+      user.set('loading', false);
+      return user;
     });
   }
 
@@ -412,41 +337,19 @@ Discourse.User.reopenClass({
     @returns {Object}
   **/
   groupStats: function(stats) {
-    var g,
-      _this = this;
-    g = {};
-    stats.each(function(s) {
-      var c, found, k, v, _ref;
-      found = false;
-      _ref = Discourse.UserAction.statGroups;
-      for (k in _ref) {
-        v = _ref[k];
-        if (v.contains(s.action_type)) {
-          found = true;
-          if (!g[k]) {
-            g[k] = Em.Object.create({
-              description: Em.String.i18n("user_action_groups." + k),
-              count: 0,
-              action_type: parseInt(k, 10)
-            });
-          }
-          g[k].count += parseInt(s.count, 10);
-          c = g[k].count;
-          if (s.action_type === k) {
-            g[k] = s;
-            s.count = c;
-          }
-        }
-      }
-      if (!found) {
-        g[s.action_type] = s;
-      }
+    var responses = Discourse.UserActionStat.create({
+      count: 0,
+      action_type: Discourse.UserAction.RESPONSE
     });
-    return stats.map(function(s) {
-      return g[s.action_type];
-    }).exclude(function(s) {
-      return !s;
+
+    stats.filterProperty('isResponse').forEach(function (stat) {
+      responses.set('count', responses.get('count') + stat.get('count'));
     });
+
+    var result = Em.A();
+    result.pushObject(responses);
+    result.pushObjects(stats.rejectProperty('isResponse'));
+    return(result);
   },
 
   /**
diff --git a/app/assets/javascripts/discourse/models/user_action.js b/app/assets/javascripts/discourse/models/user_action.js
index bfbc15939..8713b052f 100644
--- a/app/assets/javascripts/discourse/models/user_action.js
+++ b/app/assets/javascripts/discourse/models/user_action.js
@@ -69,7 +69,7 @@ Discourse.UserAction = Discourse.Model.extend({
         }
       }
     } else {
-      Ember.debug("Invalid user action: " + action);
+      return "";
     }
 
     return new Handlebars.SafeString(icon + " " + sentence);
@@ -87,20 +87,19 @@ Discourse.UserAction = Discourse.Model.extend({
     return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('post_number'));
   }).property(),
 
-  replyUrl: (function() {
+  replyUrl: function() {
     return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('reply_to_post_number'));
-  }).property(),
+  }.property(),
 
-  isPM: (function() {
+  isPM: function() {
     var a = this.get('action_type');
     return a === Discourse.UserAction.NEW_PRIVATE_MESSAGE || a === Discourse.UserAction.GOT_PRIVATE_MESSAGE;
-  }).property(),
+  }.property(),
 
-  isPostAction: (function() {
-    var a;
-    a = this.get('action_type');
+  isPostAction: function() {
+    var a = this.get('action_type');
     return a === Discourse.UserAction.RESPONSE || a === Discourse.UserAction.POST || a === Discourse.UserAction.NEW_TOPIC;
-  }).property(),
+  }.property(),
 
   addChild: function(action) {
     var bucket, current, groups, ua;
@@ -215,8 +214,7 @@ Discourse.UserAction.reopenClass({
 
 Discourse.UserAction.reopenClass({
   statGroups: (function() {
-    var g;
-    g = {};
+    var g = {};
     g[Discourse.UserAction.RESPONSE] = [Discourse.UserAction.RESPONSE, Discourse.UserAction.MENTION, Discourse.UserAction.QUOTE];
     return g;
   })()
diff --git a/app/assets/javascripts/discourse/models/user_action_stat.js b/app/assets/javascripts/discourse/models/user_action_stat.js
index 78566adb5..c00be91ee 100644
--- a/app/assets/javascripts/discourse/models/user_action_stat.js
+++ b/app/assets/javascripts/discourse/models/user_action_stat.js
@@ -6,6 +6,25 @@
   @namespace Discourse
   @module Discourse
 **/
-Discourse.UserActionStat = Discourse.Model.extend({});
+Discourse.UserActionStat = Discourse.Model.extend({
+
+  isPM: function() {
+    var actionType = this.get('action_type');
+    return actionType === Discourse.UserAction.NEW_PRIVATE_MESSAGE ||
+           actionType === Discourse.UserAction.GOT_PRIVATE_MESSAGE;
+  }.property('action_type'),
+
+  description: function() {
+    return Em.String.i18n('user_action_groups.' + this.get('action_type'));
+  }.property('description'),
+
+  isResponse: function() {
+    var actionType = this.get('action_type');
+    return actionType === Discourse.UserAction.RESPONSE ||
+           actionType === Discourse.UserAction.MENTION ||
+           actionType === Discourse.UserAction.QUOTE;
+  }.property('action_type')
+
+});
 
 
diff --git a/app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js b/app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js
index cbb75976f..dbd73f4b9 100644
--- a/app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js
+++ b/app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js
@@ -8,23 +8,9 @@
 **/
 Discourse.RestrictedUserRoute = Discourse.Route.extend({
 
-  enter: function(router, context) {
-    var _this = this;
-
-    // a bit hacky, but we don't have a fully loaded user at this point
-    //  so we need to wait for it
-    var user = this.controllerFor('user').get('content');
-    
-    if(user.can_edit === undefined) {
-      user.onDetailsLoaded(function(){
-        if (this.get('can_edit') === false) {
-          _this.transitionTo('user.activity');
-        }
-      });
-    }
-    
-    if(user.can_edit === false) {
-      this.transitionTo('user.activity');
+  redirect: function(user) {
+    if (user.get('can_edit') === false) {
+      this.transitionTo('user.activity', user);
     }
   }
 
diff --git a/app/assets/javascripts/discourse/routes/user_activity_route.js b/app/assets/javascripts/discourse/routes/user_activity_route.js
index f07d84a47..6e5427e7c 100644
--- a/app/assets/javascripts/discourse/routes/user_activity_route.js
+++ b/app/assets/javascripts/discourse/routes/user_activity_route.js
@@ -8,18 +8,16 @@
 **/
 Discourse.UserActivityRoute = Discourse.Route.extend({
 
+  model: function() {
+    return this.modelFor('user');
+  },
+
   renderTemplate: function() {
     this.render({ into: 'user', outlet: 'userOutlet' });
   },
 
-  setupController: function(controller) {
-    var userController = this.controllerFor('user');
-    var user = userController.get('content');
-    controller.set('content', user);
-    user.set('filter', null);
-    if (user.get('streamFilter')) {
-      user.filterStream(null);
-    }
+  setupController: function(controller, user) {
+    user.filterStream(null);
   }
 
 });
diff --git a/app/assets/javascripts/discourse/routes/user_private_messages_route.js b/app/assets/javascripts/discourse/routes/user_private_messages_route.js
index 740fa84bd..318a6a2ee 100644
--- a/app/assets/javascripts/discourse/routes/user_private_messages_route.js
+++ b/app/assets/javascripts/discourse/routes/user_private_messages_route.js
@@ -8,18 +8,21 @@
 **/
 Discourse.UserPrivateMessagesRoute = Discourse.RestrictedUserRoute.extend({
 
+  model: function() {
+    return this.modelFor('user');
+  },
+
   renderTemplate: function() {
     this.render({ into: 'user', outlet: 'userOutlet' });
   },
 
   setupController: function(controller, user) {
-    var _this = this;
-    user = this.controllerFor('user').get('content');
-    controller.set('content', user);
     user.filterStream(Discourse.UserAction.GOT_PRIVATE_MESSAGE);
+
+    var composerController = this.controllerFor('composer');
     Discourse.Draft.get('new_private_message').then(function(data) {
       if (data.draft) {
-        _this.controllerFor('composer').open({
+        composerController.open({
           draft: data.draft,
           draftKey: 'new_private_message',
           ignoreIfChanged: true,
diff --git a/app/assets/javascripts/discourse/routes/user_route.js b/app/assets/javascripts/discourse/routes/user_route.js
index 33da69294..2b5b9d62e 100644
--- a/app/assets/javascripts/discourse/routes/user_route.js
+++ b/app/assets/javascripts/discourse/routes/user_route.js
@@ -7,16 +7,13 @@
   @module Discourse
 **/
 Discourse.UserRoute = Discourse.Route.extend({
+
   model: function(params) {
-    return Discourse.User.create({username: params.username});
+    return Discourse.User.create({username: params.username}).loadDetails();
   },
 
   serialize: function(params) {
     return { username: Em.get(params, 'username').toLowerCase() };
-  },
-
-  setupController: function(controller, model) {
-    model.loadDetails();
   }
 
 });
diff --git a/app/assets/javascripts/discourse/templates/user/activity.js.handlebars b/app/assets/javascripts/discourse/templates/user/activity.js.handlebars
index 30b886f68..815eb8d66 100644
--- a/app/assets/javascripts/discourse/templates/user/activity.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/user/activity.js.handlebars
@@ -1,4 +1,3 @@
-
 <div id='user-info'>
   <nav class='buttons'>
     {{#if can_edit}}
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 50ee13bbe..8dea623af 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -9,7 +9,6 @@ class UserSerializer < BasicUserSerializer
              :created_at,
              :website,
              :can_edit,
-             :stream,
              :stats,
              :can_send_private_message_to_user,
              :bio_excerpt,
@@ -41,15 +40,15 @@ class UserSerializer < BasicUserSerializer
   end
 
   private_attributes :email,
-             :email_digests,
-             :email_private_messages,
-             :email_direct,
-             :digest_after_days,
-             :auto_track_topics_after_msecs,
-             :new_topic_duration_minutes, 
-             :external_links_in_new_tab,
-             :enable_quoting 
-             
+                     :email_digests,
+                     :email_private_messages,
+                     :email_direct,
+                     :digest_after_days,
+                     :auto_track_topics_after_msecs,
+                     :new_topic_duration_minutes,
+                     :external_links_in_new_tab,
+                     :enable_quoting
+
 
   def auto_track_topics_after_msecs
     object.auto_track_topics_after_msecs || SiteSetting.auto_track_topics_after
@@ -67,11 +66,6 @@ class UserSerializer < BasicUserSerializer
     UserAction.stats(object.id, scope)
   end
 
-  def stream
-    UserAction.stream(user_id: object.id, offset: 0, limit: 60, 
-                      guardian: scope, ignore_private_messages: true)
-  end
-
   def can_edit
     scope.can_edit?(object)
   end