UX: User card loading (#7404)

This commit is contained in:
Tim Lange 2019-04-24 10:55:09 +02:00 committed by Dan Ungureanu
parent 5b934cb33d
commit 104a9e79f9
3 changed files with 266 additions and 207 deletions

View File

@ -150,6 +150,9 @@ export default Ember.Component.extend(
}, },
_showCallback(username, $target) { _showCallback(username, $target) {
this._positionCard($target);
this.setProperties({ visible: true, loading: true });
const args = { stats: false }; const args = { stats: false };
args.include_post_count_for = this.get("topic.id"); args.include_post_count_for = this.get("topic.id");
User.findByUsername(username, args) User.findByUsername(username, args)
@ -160,8 +163,7 @@ export default Ember.Component.extend(
user.topic_post_count[args.include_post_count_for] user.topic_post_count[args.include_post_count_for]
); );
} }
this._positionCard($target); this.setProperties({ user });
this.setProperties({ user, visible: true });
}) })
.catch(() => this._close()) .catch(() => this._close())
.finally(() => this.set("loading", null)); .finally(() => this.set("loading", null));

View File

@ -1,223 +1,242 @@
{{#if visible}} {{#if visible}}
<div class="card-content"> <div class="card-content">
{{#if loading}}
<div class="card-row first-row"> <div class="card-row first-row">
<div class="user-card-avatar"> <div class="user-card-avatar">
{{#if user.profile_hidden}} <div class="card-avatar-placeholder animated-placeholder"></div>
<span class="card-huge-avatar">{{bound-avatar user "huge"}}</span> </div>
{{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> </div>
<div class="names">
<h1 class="{{staff}} {{newUser}} {{if nameFirst "full-name" "username"}}"> <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="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}} {{#if user.profile_hidden}}
<span class="name-username-wrapper"> <span class="card-huge-avatar">{{bound-avatar user "huge"}}</span>
{{if nameFirst user.name (format-username username)}}
</span>
{{else}} {{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"> <span class="name-username-wrapper">
{{if nameFirst user.name (format-username username)}} {{if nameFirst user.name (format-username username)}}
</span> </span>
{{user-status user currentUser=currentUser}} {{else}}
</a> <a href="{{user.path}}" {{action "showUser" user}} class='user-profile-link'>
{{/if}} <span class="name-username-wrapper">
</h1> {{if nameFirst user.name (format-username username)}}
{{plugin-outlet name="user-card-after-username" args=(hash user=user showUser=(action "showUser" user)) tagName=''}} </span>
{{#unless nameFirst}} {{user-status user currentUser=currentUser}}
{{#if user.name}} </a>
<h2 class='full-name'>{{user.name}}</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>
</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>
<span class="suspension-reason-description">{{user.suspend_reason}}</span>
</div>
</div>
{{else}}
{{#if user.bio_cooked}}
<div class='bio'>{{text-overflow class="overflow" text=user.bio_excerpt}}</div>
{{/if}}
{{/if}}
</div>
{{/if}}
{{#if hasLocationOrWebsite}}
<div class="card-row third-row">
<div class="location-and-website">
{{#if user.location}}
<span class='location'>{{d-icon "map-marker-alt"}}
<span>{{user.location}}</span></span>
{{/if}}
{{#if user.website_name}}
<span class='website-name'>
{{d-icon "globe"}}
{{#if linkWebsite}}
<a href="{{user.website}}" rel={{unless removeNoFollow 'nofollow noopener'}}
target="_blank">{{user.website_name}}</a>
{{else}}
<span title={{user.website}}>{{user.website_name}}</span>
{{/if}}
</span>
{{/if}}
{{plugin-outlet name="user-card-location-and-website" args=(hash user=user)}}
</div>
</div>
{{/if}}
<div class="card-row fourth-row">
{{#unless user.profile_hidden}}
<div class="metadata">
{{#if user.last_posted_at}}
<h3><span class='desc'>{{i18n 'last_post'}}</span>
{{format-date user.last_posted_at leaveAgo="true"}}</h3>
{{/if}}
<h3><span class='desc'>{{i18n 'joined'}}</span>
{{format-date user.created_at leaveAgo="true"}}</h3>
{{#if user.time_read}}
<h3 title="{{timeReadTooltip}}">
<span class='desc'>{{i18n 'time_read'}}</span>
{{format-duration user.time_read}}
{{#if showRecentTimeRead}}
<span>({{i18n 'time_read_recently' time_read=recentTimeRead}})</span>
{{/if}}
</h3>
{{/if}}
{{#if showCheckEmail}}
<h3 class="email">
{{d-icon "far-envelope" title="user.email.title"}}
{{#if user.email}}
{{user.email}}
{{else}}
{{d-button
action=(action "checkEmail")
actionParam=user
icon="far-envelope"
label="admin.users.check_email.text"
class="btn-primary"}}
{{/if}}
</h3>
{{/if}}
{{plugin-outlet name="user-card-metadata" args=(hash user=user)}}
</div>
{{/unless}}
{{plugin-outlet name="user-card-after-metadata" args=(hash user=user)}}
</div>
{{#if publicUserFields}}
<div class="card-row fifth-row">
<div class="public-user-fields">
{{#each publicUserFields as |uf|}}
{{#if uf.value}}
<div class="public-user-field {{uf.field.dasherized_name}}">
<span class="user-field-name">{{uf.field.name}}:</span>
<span class="user-field-value">{{uf.value}}</span>
</div>
{{/if}} {{/if}}
{{/each}} </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}}
{{#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> </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> </div>
{{/if}}
{{plugin-outlet name="user-card-before-badges" args=(hash user=user)}} {{#if user.profile_hidden}}
<div class="card-row second-row">
<div class='profile-hidden'>
<span>{{i18n "user.profile_hidden"}}</span>
</div>
</div>
{{/if}}
{{#if showBadges}} {{#if isSuspendedOrHasBio}}
<div class="card-row sixth-row"> <div class="card-row second-row">
{{#if user.featured_user_badges}} {{#if user.suspend_reason}}
<div class="badge-section"> <div class='suspended'>
{{#each user.featured_user_badges as |ub|}} <div class="suspension-date">
{{user-badge badge=ub.badge user=user}} {{d-icon "ban"}}
{{/each}} {{i18n 'user.suspended_notice' date=user.suspendedTillDate}}
{{#if showMoreBadges}} </div>
<span class='more-user-badges'> <div class='suspension-reason'>
{{#link-to 'user.badges' user}} <span class="suspension-reason-title">{{i18n 'user.suspended_reason'}}</span>
{{i18n 'badges.more_badges' count=moreBadgesCount}} <span class="suspension-reason-description">{{user.suspend_reason}}</span>
{{/link-to}} </div>
</div>
{{else}}
{{#if user.bio_cooked}}
<div class='bio'>{{text-overflow class="overflow" text=user.bio_excerpt}}</div>
{{/if}}
{{/if}}
</div>
{{/if}}
{{#if hasLocationOrWebsite}}
<div class="card-row third-row">
<div class="location-and-website">
{{#if user.location}}
<span class='location'>{{d-icon "map-marker-alt"}}
<span>{{user.location}}</span></span>
{{/if}}
{{#if user.website_name}}
<span class='website-name'>
{{d-icon "globe"}}
{{#if linkWebsite}}
<a href="{{user.website}}" rel={{unless removeNoFollow 'nofollow noopener'}}
target="_blank">{{user.website_name}}</a>
{{else}}
<span title={{user.website}}>{{user.website_name}}</span>
{{/if}}
</span> </span>
{{/if}} {{/if}}
{{plugin-outlet name="user-card-location-and-website" args=(hash user=user)}}
</div> </div>
{{/if}} </div>
</div> {{/if}}
{{/if}}
<div class="card-row fourth-row">
{{#unless user.profile_hidden}}
<div class="metadata">
{{#if user.last_posted_at}}
<h3><span class='desc'>{{i18n 'last_post'}}</span>
{{format-date user.last_posted_at leaveAgo="true"}}</h3>
{{/if}}
<h3><span class='desc'>{{i18n 'joined'}}</span>
{{format-date user.created_at leaveAgo="true"}}</h3>
{{#if user.time_read}}
<h3 title="{{timeReadTooltip}}">
<span class='desc'>{{i18n 'time_read'}}</span>
{{format-duration user.time_read}}
{{#if showRecentTimeRead}}
<span>({{i18n 'time_read_recently' time_read=recentTimeRead}})</span>
{{/if}}
</h3>
{{/if}}
{{#if showCheckEmail}}
<h3 class="email">
{{d-icon "far-envelope" title="user.email.title"}}
{{#if user.email}}
{{user.email}}
{{else}}
{{d-button
action=(action "checkEmail")
actionParam=user
icon="far-envelope"
label="admin.users.check_email.text"
class="btn-primary"}}
{{/if}}
</h3>
{{/if}}
{{plugin-outlet name="user-card-metadata" args=(hash user=user)}}
</div>
{{/unless}}
{{plugin-outlet name="user-card-after-metadata" args=(hash user=user)}}
</div>
{{#if publicUserFields}}
<div class="card-row fifth-row">
<div class="public-user-fields">
{{#each publicUserFields as |uf|}}
{{#if uf.value}}
<div class="public-user-field {{uf.field.dasherized_name}}">
<span class="user-field-name">{{uf.field.name}}:</span>
<span class="user-field-value">{{uf.value}}</span>
</div>
{{/if}}
{{/each}}
</div>
</div>
{{/if}}
{{plugin-outlet name="user-card-before-badges" args=(hash user=user)}}
{{#if showBadges}}
<div class="card-row sixth-row">
{{#if user.featured_user_badges}}
<div class="badge-section">
{{#each user.featured_user_badges as |ub|}}
{{user-badge badge=ub.badge user=user}}
{{/each}}
{{#if showMoreBadges}}
<span class='more-user-badges'>
{{#link-to 'user.badges' user}}
{{i18n 'badges.more_badges' count=moreBadgesCount}}
{{/link-to}}
</span>
{{/if}}
</div>
{{/if}}
</div>
{{/if}}
{{/if}}
</div> </div>
{{/if}} {{/if}}

View File

@ -2,6 +2,44 @@ $card_width: 580px;
$avatar_width: 120px; $avatar_width: 120px;
$avatar_margin: -50px; // negative margin makes avatars extend above cards $avatar_margin: -50px; // negative margin makes avatars extend above cards
// placeholder
@keyframes placeHolderShimmer {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
.animated-placeholder {
animation-duration: 1.25s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-name: placeHolderShimmer;
animation-timing-function: linear;
background: $primary-low;
background: linear-gradient(
to right,
$primary-low 10%,
$primary-low-mid 18%,
$primary-low 33%
);
height: 20px;
position: relative;
}
.card-avatar-placeholder {
width: $avatar_width;
height: $avatar_width;
border-radius: 100%;
animation-duration: 5s;
background: linear-gradient(
to right,
$primary-low 10%,
$primary-low-mid 38%,
$primary-low 73%
);
}
// shared styles for user and group cards // shared styles for user and group cards
#user-card, #user-card,
#group-card { #group-card {