Implement user hover cards and basic profile pages

This commit is contained in:
Toby Zerner 2015-03-12 10:40:03 +10:30
parent f1ceb41d3f
commit 2cd59c876d
16 changed files with 252 additions and 18 deletions

View File

@ -6,7 +6,22 @@ var precompileTemplate = Ember.Handlebars.compile;
Component for the username/avatar in a post header.
*/
export default Ember.Component.extend({
tagName: 'h3',
classNames: ['post-user'],
layout: precompileTemplate('{{#if post.user}}{{#link-to "user" post.user}}{{user-avatar post.user}} {{user-name post.user}}{{/link-to}}{{else}}{{user-avatar post.user}} {{user-name post.user}}{{/if}}{{ui/item-list items=post.user.badges class="badges"}}')
layout: precompileTemplate('{{#if post.user}}<h3>{{#link-to "user" post.user}}{{user-avatar post.user}} {{user-name post.user}}{{/link-to}} {{ui/item-list items=post.user.badges class="badges"}}</h3>{{#if showCard}}{{user/user-card user=post.user class="user-card-popover" controlsButtonClass="btn btn-default btn-icon btn-sm btn-naked"}}{{/if}}{{else}}<h3>{{user-avatar post.user}} {{user-name post.user}}</h3>{{/if}}'),
didInsertElement: function() {
var component = this;
var timeout;
this.$().bind('mouseover', '> a, .user-card', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
component.set('showCard', true);
}, 250);
}).bind('mouseout', '> a, .user-card', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
component.set('showCard', false);
}, 250);
});
}
});

View File

@ -0,0 +1,62 @@
import Ember from 'ember';
import HasItemLists from 'flarum/mixins/has-item-lists';
export default Ember.Component.extend(HasItemLists, {
layoutName: 'components/user/user-card',
classNames: ['user-card'],
attributeBindings: ['style'],
itemLists: ['controls', 'info'],
style: Ember.computed('user.color', function() {
return 'background-color: '+this.get('user.color');
}),
bioEditable: Ember.computed.and('user.canEdit', 'editable'),
showBio: Ember.computed.or('user.bioHtml', 'bioEditable'),
didInsertElement: function() {
this.$().on('click', '.user-bio a', function(e) {
e.stopPropagation();
});
},
actions: {
editBio: function() {
if (!this.get('bioEditable')) {
return;
}
this.set('editingBio', true);
var component = this;
Ember.run.scheduleOnce('afterRender', this, function() {
this.$('.user-bio textarea').focus().blur(function() {
component.send('saveBio', $(this).val());
});
});
},
saveBio: function(value) {
var user = this.get('user');
user.set('bio', value);
user.save();
this.set('editingBio', false);
}
},
populateControls: function(items) {
this.addActionItem(items, 'edit', 'Edit', 'pencil');
this.addActionItem(items, 'delete', 'Delete', 'times');
},
populateInfo: function(items) {
items.pushObjectWithTag(Ember.Component.extend({
layout: Ember.Handlebars.compile('{{fa-icon "circle"}} Online')
}), 'lastActiveTime');
items.pushObjectWithTag(Ember.Component.extend({
layout: Ember.Handlebars.compile('Joined {{human-time user.joinTime}}'),
user: this.get('user')
}), 'joinTime');
}
});

View File

@ -0,0 +1,5 @@
import Ember from 'ember';
export default Ember.Controller.extend({
});

View File

@ -10,6 +10,8 @@ export default DS.Model.extend(HasItemLists, {
email: DS.attr('string'),
password: DS.attr('string'),
avatarUrl: DS.attr('string'),
bio: DS.attr('string'),
bioHtml: DS.attr('string'),
groups: DS.hasMany('group'),

View File

@ -6,7 +6,6 @@ var Router = Ember.Router.extend({
});
Router.map(function() {
this.resource('index', {path: '/'}, function() {
this.resource('discussion', {path: '/:id/:slug'}, function() {
this.route('near', {path: '/:near'});
@ -16,9 +15,10 @@ Router.map(function() {
this.resource('user', {path: '/u/:username'}, function() {
this.route('activity');
this.route('posts');
this.resource('preferences');
this.route('edit');
});
this.resource('settings');
});
export default Router;

View File

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('user', params.username);
}
});

View File

@ -33,5 +33,6 @@
@import "@{flarum-base}index.less";
@import "@{flarum-base}discussion.less";
@import "@{flarum-base}user.less";
@import "@{flarum-base}login.less";
@import "@{flarum-base}signup.less";

View File

@ -1,12 +1,13 @@
.avatar-size(@size) {
width: @size;
height: @size;
border-radius: @size / 2;
border-radius: @size;
font-size: @size / 2;
line-height: @size;
}
.avatar {
display: inline-block;
box-sizing: content-box;
color: @fl-body-bg;
font-weight: 300;
text-align: center;

View File

@ -20,7 +20,8 @@
.badge {
.badge-size(24px);
border: 2px solid @fl-body-bg;
background: @fl-body-secondary-color;
background: @fl-body-muted-color;
color: #fff;
display: inline-block;
vertical-align: middle;
text-align: center;

View File

@ -150,14 +150,14 @@
margin-bottom: 10px;
color: @fl-body-muted-color;
&, & a {
color: @fl-body-muted-color;
}
& > ul {
list-style-type: none;
padding: 0;
margin: 0;
&, & a {
color: @fl-body-muted-color;
}
& > li {
display: inline;
margin-right: 10px;
@ -166,11 +166,16 @@
& .post-user {
margin: 0;
display: inline;
font-weight: bold;
font-size: 15px;
font-weight: normal;
position: relative;
&, & a {
& h3 {
display: inline;
}
& h3, & h3 a {
color: @fl-body-heading-color;
font-weight: bold;
font-size: 15px;
}
& .badges {
@ -182,6 +187,13 @@
position: relative;
}
}
& .user-card {
position: absolute;
top: 100%;
margin-top: 5px;
z-index: @zindex-popover;
}
}
}
.post-body {

View File

@ -7,6 +7,14 @@
&, & a, & .close {
color: @fl-body-hero-color;
}
& h2 {
margin: 0;
font-size: 16px;
font-weight: normal;
line-height: 1.5em;
}
}
.welcome-hero {
& a, & .close {
opacity: 0.5;
}
@ -14,12 +22,6 @@
float: right;
margin-top: -10px;
}
& h2 {
margin: 0;
font-size: 16px;
font-weight: normal;
line-height: 1.5em;
}
& p {
margin: 5px 0 0;
}

View File

@ -170,6 +170,7 @@ body {
}
& .btn-default.active, .open > .dropdown-toggle.btn-default {
background: fadein(@fl-drawer-control-bg, 5%);
color: @fl-drawer-control-color;
}
& .btn-naked {
background: transparent;

View File

@ -0,0 +1,80 @@
.user-card {
.drawer-components();
}
.user-hero {
& .contextual-controls {
float: right;
& .dropdown-toggle .icon-glyph {
display: none;
}
}
}
.user-card-popover {
width: 500px;
padding: 20px;
border-radius: @border-radius-base;
.box-shadow(0 2px 6px @fl-shadow-color);
& .container {
width: auto !important;
padding: 0 !important;
}
& .user-identity {
font-size: 22px;
}
}
.user-profile {
text-align: left;
padding-left: 130px;
max-width: 800px;
& .user-identity {
display: inline;
vertical-align: middle;
}
& .avatar {
.avatar-size(96px);
float: left;
margin-left: -130px;
border: 4px solid #fff;
}
& .badges {
margin-left: 10px;
}
& .user-bio {
margin-top: 15px;
padding: 10px 10px 1px;
margin: 5px -10px -5px;
&.editable:not(.editing) {
cursor: text;
&:hover {
border: 1px dashed rgba(255, 255, 255, 0.5);
border-radius: @border-radius-base;
padding: 9px 9px 0;
}
}
&, & textarea {
font-size: 14px;
}
& textarea {
padding: 10px;
margin: -10px -10px 0;
font-size: 14px;
}
}
& .user-info {
margin: 15px 0 0;
padding: 0;
list-style: none;
font-size: 12px;
& > li {
display: inline-block;
margin-right: 15px;
}
}
}

View File

@ -0,0 +1,29 @@
<div class="container">
{{#if controls}}
{{ui/dropdown-button items=controls class="contextual-controls" menuClass="pull-right" buttonClass=controlsButtonClass}}
{{/if}}
<div class="user-profile">
<h2 class="user-identity">
{{#link-to "user" user}}{{user-avatar user}}{{user-name user}}{{/link-to}}
</h2>
{{ui/item-list items=user.badges class="badges user-badges"}}
{{#if showBio}}
<div class="user-bio {{if bioEditable "editable"}} {{if editingBio "editing"}}" {{action "editBio"}}>
{{#if editingBio}}
{{textarea value=user.bio class="form-control"}}
{{else}}
{{#if user.bioHtml}}
{{{user.bioHtml}}}
{{else if bioEditable}}
<p>Write something about yourself...</p>
{{/if}}
{{/if}}
</div>
{{/if}}
{{ui/item-list items=info class="user-info"}}
</div>
</div>

View File

@ -0,0 +1,9 @@
{{user/user-card user=model class="hero user-hero" editable=true controlsButtonClass="btn btn-default"}}
<div class="container">
<nav class="user-nav">
{{ui/item-list items=view.sidebar}}
</nav>
{{outlet}}
</div>

View File

@ -0,0 +1,7 @@
import Ember from 'ember';
import HasItemLists from 'flarum/mixins/has-item-lists';
export default Ember.View.extend(HasItemLists, {
itemLists: ['sidebar']
});