mirror of
https://github.com/flarum/core.git
synced 2025-07-28 20:20:34 +02:00
Implement user hover cards and basic profile pages
This commit is contained in:
@@ -6,7 +6,22 @@ var precompileTemplate = Ember.Handlebars.compile;
|
|||||||
Component for the username/avatar in a post header.
|
Component for the username/avatar in a post header.
|
||||||
*/
|
*/
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
tagName: 'h3',
|
|
||||||
classNames: ['post-user'],
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
62
ember/app/components/user/user-card.js
Normal file
62
ember/app/components/user/user-card.js
Normal 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');
|
||||||
|
}
|
||||||
|
});
|
5
ember/app/controllers/user.js
Normal file
5
ember/app/controllers/user.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
|
||||||
|
});
|
@@ -10,6 +10,8 @@ export default DS.Model.extend(HasItemLists, {
|
|||||||
email: DS.attr('string'),
|
email: DS.attr('string'),
|
||||||
password: DS.attr('string'),
|
password: DS.attr('string'),
|
||||||
avatarUrl: DS.attr('string'),
|
avatarUrl: DS.attr('string'),
|
||||||
|
bio: DS.attr('string'),
|
||||||
|
bioHtml: DS.attr('string'),
|
||||||
|
|
||||||
groups: DS.hasMany('group'),
|
groups: DS.hasMany('group'),
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ var Router = Ember.Router.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Router.map(function() {
|
Router.map(function() {
|
||||||
|
|
||||||
this.resource('index', {path: '/'}, function() {
|
this.resource('index', {path: '/'}, function() {
|
||||||
this.resource('discussion', {path: '/:id/:slug'}, function() {
|
this.resource('discussion', {path: '/:id/:slug'}, function() {
|
||||||
this.route('near', {path: '/:near'});
|
this.route('near', {path: '/:near'});
|
||||||
@@ -16,9 +15,10 @@ Router.map(function() {
|
|||||||
this.resource('user', {path: '/u/:username'}, function() {
|
this.resource('user', {path: '/u/:username'}, function() {
|
||||||
this.route('activity');
|
this.route('activity');
|
||||||
this.route('posts');
|
this.route('posts');
|
||||||
this.resource('preferences');
|
this.route('edit');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resource('settings');
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Router;
|
export default Router;
|
||||||
|
7
ember/app/routes/user.js
Normal file
7
ember/app/routes/user.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model: function(params) {
|
||||||
|
return this.store.find('user', params.username);
|
||||||
|
}
|
||||||
|
});
|
@@ -33,5 +33,6 @@
|
|||||||
|
|
||||||
@import "@{flarum-base}index.less";
|
@import "@{flarum-base}index.less";
|
||||||
@import "@{flarum-base}discussion.less";
|
@import "@{flarum-base}discussion.less";
|
||||||
|
@import "@{flarum-base}user.less";
|
||||||
@import "@{flarum-base}login.less";
|
@import "@{flarum-base}login.less";
|
||||||
@import "@{flarum-base}signup.less";
|
@import "@{flarum-base}signup.less";
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
.avatar-size(@size) {
|
.avatar-size(@size) {
|
||||||
width: @size;
|
width: @size;
|
||||||
height: @size;
|
height: @size;
|
||||||
border-radius: @size / 2;
|
border-radius: @size;
|
||||||
font-size: @size / 2;
|
font-size: @size / 2;
|
||||||
line-height: @size;
|
line-height: @size;
|
||||||
}
|
}
|
||||||
.avatar {
|
.avatar {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
box-sizing: content-box;
|
||||||
color: @fl-body-bg;
|
color: @fl-body-bg;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@@ -20,7 +20,8 @@
|
|||||||
.badge {
|
.badge {
|
||||||
.badge-size(24px);
|
.badge-size(24px);
|
||||||
border: 2px solid @fl-body-bg;
|
border: 2px solid @fl-body-bg;
|
||||||
background: @fl-body-secondary-color;
|
background: @fl-body-muted-color;
|
||||||
|
color: #fff;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@@ -150,14 +150,14 @@
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
color: @fl-body-muted-color;
|
color: @fl-body-muted-color;
|
||||||
|
|
||||||
|
&, & a {
|
||||||
|
color: @fl-body-muted-color;
|
||||||
|
}
|
||||||
& > ul {
|
& > ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
&, & a {
|
|
||||||
color: @fl-body-muted-color;
|
|
||||||
}
|
|
||||||
& > li {
|
& > li {
|
||||||
display: inline;
|
display: inline;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
@@ -166,11 +166,16 @@
|
|||||||
& .post-user {
|
& .post-user {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: inline;
|
display: inline;
|
||||||
font-weight: bold;
|
font-weight: normal;
|
||||||
font-size: 15px;
|
position: relative;
|
||||||
|
|
||||||
&, & a {
|
& h3 {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
& h3, & h3 a {
|
||||||
color: @fl-body-heading-color;
|
color: @fl-body-heading-color;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .badges {
|
& .badges {
|
||||||
@@ -182,6 +187,13 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .user-card {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
margin-top: 5px;
|
||||||
|
z-index: @zindex-popover;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.post-body {
|
.post-body {
|
||||||
|
@@ -7,6 +7,14 @@
|
|||||||
&, & a, & .close {
|
&, & a, & .close {
|
||||||
color: @fl-body-hero-color;
|
color: @fl-body-hero-color;
|
||||||
}
|
}
|
||||||
|
& h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.welcome-hero {
|
||||||
& a, & .close {
|
& a, & .close {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
@@ -14,12 +22,6 @@
|
|||||||
float: right;
|
float: right;
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
& h2 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
& p {
|
& p {
|
||||||
margin: 5px 0 0;
|
margin: 5px 0 0;
|
||||||
}
|
}
|
||||||
|
@@ -170,6 +170,7 @@ body {
|
|||||||
}
|
}
|
||||||
& .btn-default.active, .open > .dropdown-toggle.btn-default {
|
& .btn-default.active, .open > .dropdown-toggle.btn-default {
|
||||||
background: fadein(@fl-drawer-control-bg, 5%);
|
background: fadein(@fl-drawer-control-bg, 5%);
|
||||||
|
color: @fl-drawer-control-color;
|
||||||
}
|
}
|
||||||
& .btn-naked {
|
& .btn-naked {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
80
ember/app/styles/flarum/user.less
Normal file
80
ember/app/styles/flarum/user.less
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
ember/app/templates/components/user/user-card.hbs
Normal file
29
ember/app/templates/components/user/user-card.hbs
Normal 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>
|
9
ember/app/templates/user.hbs
Normal file
9
ember/app/templates/user.hbs
Normal 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>
|
7
ember/app/views/user.js
Normal file
7
ember/app/views/user.js
Normal 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']
|
||||||
|
});
|
Reference in New Issue
Block a user