UX: User card loading (#7404)

UX: User card loading (#7404)

diff --git a/app/assets/javascripts/discourse/components/user-card-contents.js.es6 b/app/assets/javascripts/discourse/components/user-card-contents.js.es6
index 780ee6f..83e83ac 100644
--- a/app/assets/javascripts/discourse/components/user-card-contents.js.es6
+++ b/app/assets/javascripts/discourse/components/user-card-contents.js.es6
@@ -150,6 +150,9 @@ export default Ember.Component.extend(
     },
 
     _showCallback(username, $target) {
+      this._positionCard($target);
+      this.setProperties({ visible: true, loading: true });
+
       const args = { stats: false };
       args.include_post_count_for = this.get("topic.id");
       User.findByUsername(username, args)
@@ -160,8 +163,7 @@ export default Ember.Component.extend(
               user.topic_post_count[args.include_post_count_for]
             );
           }
-          this._positionCard($target);
-          this.setProperties({ user, visible: true });
+          this.setProperties({ user });
         })
         .catch(() => this._close())
         .finally(() => this.set("loading", null));
diff --git a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs
index 32cc458..7cad78a 100644
--- a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs
+++ b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs
@@ -1,223 +1,242 @@
 {{#if visible}}
   <div class="card-content">
+    {{#if loading}}
+      <div class="card-row first-row">
+        <div class="user-card-avatar">
+          <div class="card-avatar-placeholder animated-placeholder"></div>
+        </div>
+      </div>
 
-    <div class="card-row first-row">
-      <div class="user-card-avatar">
-        {{#if user.profile_hidden}}
-          <span class="card-huge-avatar">{{bound-avatar user "huge"}}</span>
-        {{else}}
-          <a href="{{user.path}}" {{action "showUser" user}} class="card-huge-avatar">{{bound-avatar user "huge"}}</a>
-        {{/if}}
-        {{#if user.primary_group_name}}
-          {{avatar-flair
-          flairURL=user.primary_group_flair_url
-          flairBgColor=user.primary_group_flair_bg_color
-          flairColor=user.primary_group_flair_color
-          groupName=user.primary_group_name}}
-        {{/if}}
-        {{plugin-outlet name="user-card-avatar-flair" args=(hash user=user) tagName='div'}}
+      <div class="card-row second-row">
+        <div class="animated-placeholder"></div>
+      </div>
+      <div class="card-row third-row">
+        <div class="animated-placeholder"></div>
       </div>
-      <div class="names">
-        <h1 class="{{staff}} {{newUser}} {{if nameFirst "full-name" "username"}}">
+      <div class="card-row fourth-row">
+        <div class="animated-placeholder"></div>
+      </div>
+      <div class="card-row sixth-row">
+        <div class="animated-placeholder"></div>
+      </div>
+    {{else}}
+      <div class="card-row first-row">
+        <div class="user-card-avatar">
           {{#if user.profile_hidden}}
-            <span class="name-username-wrapper">
-              {{if nameFirst user.name (format-username username)}}
-            </span>
+            <span class="card-huge-avatar">{{bound-avatar user "huge"}}</span>
           {{else}}
-            <a href="{{user.path}}" {{action "showUser" user}} class='user-profile-link'>
+            <a href="{{user.path}}" {{action "showUser" user}} class="card-huge-avatar">{{bound-avatar user "huge"}}</a>
+          {{/if}}
+          {{#if user.primary_group_name}}
+            {{avatar-flair
+            flairURL=user.primary_group_flair_url
+            flairBgColor=user.primary_group_flair_bg_color
+            flairColor=user.primary_group_flair_color
+            groupName=user.primary_group_name}}
+          {{/if}}
+          {{plugin-outlet name="user-card-avatar-flair" args=(hash user=user) tagName='div'}}
+        </div>
+        <div class="names">
+          <h1 class="{{staff}} {{newUser}} {{if nameFirst "full-name" "username"}}">
+            {{#if user.profile_hidden}}
               <span class="name-username-wrapper">
                 {{if nameFirst user.name (format-username username)}}
               </span>
-              {{user-status user currentUser=currentUser}}
-            </a>
+            {{else}}
+              <a href="{{user.path}}" {{action "showUser" user}} class='user-profile-link'>
+                <span class="name-username-wrapper">
+                  {{if nameFirst user.name (format-username username)}}
+                </span>
+                {{user-status user currentUser=currentUser}}
+              </a>
+            {{/if}}
+          </h1>
+          {{plugin-outlet name="user-card-after-username" args=(hash user=user showUser=(action "showUser" user)) tagName=''}}
+          {{#unless nameFirst}}
+            {{#if user.name}}
+              <h2 class='full-name'>{{user.name}}</h2>
+            {{/if}}
+          {{else}}
+            <h2 class='username'>{{username}}</h2>
+          {{/unless}}
+          {{#if user.title}}
+            <h2>{{user.title}}</h2>
           {{/if}}
-        </h1>
-        {{plugin-outlet name="user-card-after-username" args=(hash user=user showUser=(action "showUser" user)) tagName=''}}
-        {{#unless nameFirst}}
-          {{#if user.name}}
-            <h2 class='full-name'>{{user.name}}</h2>
+          {{#if user.staged}}
+            <h2 class="staged">{{i18n 'user.staged'}}</h2>
           {{/if}}
-        {{else}}
-          <h2 class='username'>{{username}}</h2>
-        {{/unless}}
-        {{#if user.title}}
-          <h2>{{user.title}}</h2>
-        {{/if}}
-        {{#if user.staged}}
-          <h2 class="staged">{{i18n 'user.staged'}}</h2>
-        {{/if}}
-        {{plugin-outlet name="user-card-post-names" args=(hash user=user) tagName='div'}}
-      </div>
-      <ul class="usercard-controls">
-        {{#if user.can_send_private_message_to_user}}
-          <li class='compose-pm'>
-            {{d-button
-            class="btn-primary"
-            action=(route-action "composePrivateMessage" user post)
-            icon="envelope"
-            label="user.private_message"}}
-          </li>
-        {{/if}}
-        {{#if showFilter}}
-          <li>
-            {{d-button
-            class="btn-default"
-            action=(action "togglePosts" user)
-            icon="filter"
-            translatedLabel=togglePostsLabel}}
-          </li>
-        {{/if}}
-        {{#if hasUserFilters}}
-          <li>
-            {{d-button
-            action=(action "cancelFilter")
-            icon="times"
-            label="topic.filters.cancel"}}
-          </li>
-        {{/if}}
-        {{#if showDelete}}
-          <li>
-            {{d-button
-            class="btn-danger"
-            action=(action "deleteUser")
-            actionParam=user
-            icon="exclamation-triangle"
-            label="admin.user.delete"}}
-          </li>
-        {{/if}}
-      </ul>
-      {{plugin-outlet
-      name="user-card-additional-controls"
-      args=(hash user=user close=(action "close"))
-      tagName=""}}
-    </div>
-
-    {{#if user.profile_hidden}}
-      <div class="card-row second-row">
-        <div class='profile-hidden'>
-          <span>{{i18n "user.profile_hidden"}}</span>
+          {{plugin-outlet name="user-card-post-names" args=(hash user=user) tagName='div'}}
         </div>
-      </div>
-    {{/if}}
-
-    {{#if isSuspendedOrHasBio}}
-      <div class="card-row second-row">
-        {{#if user.suspend_reason}}
-          <div class='suspended'>
-            <div class="suspension-date">
-              {{d-icon "ban"}}
-              {{i18n 'user.suspended_notice' date=user.suspendedTillDate}}
-            </div>
-            <div class='suspension-reason'>
-              <span class="suspension-reason-title">{{i18n 'user.suspended_reason'}}</span>

[... diff too long, it was truncated ...]

GitHub sha: 104a9e79