1
0
mirror of https://github.com/flarum/core.git synced 2025-08-06 16:36:47 +02:00

Add user activity system

This commit is contained in:
Toby Zerner
2015-03-17 17:06:12 +10:30
parent 5055377eb1
commit 3880ce70f0
27 changed files with 508 additions and 72 deletions

View File

@@ -7,6 +7,13 @@ import AlertMessage from 'flarum/components/ui/alert-message';
export default JsonApiAdapter.extend({
host: config.apiURL,
pathForType: function(type) {
if (type == 'activity') {
return type;
}
return this._super(type);
},
ajaxError: function(jqXHR) {
var errors = this._super(jqXHR);

View File

@@ -0,0 +1,12 @@
import Ember from 'ember';
import FadeIn from 'flarum/mixins/fade-in';
export default Ember.Component.extend(FadeIn, {
layoutName: 'components/user/activity-item',
tagName: 'li',
componentName: Ember.computed('activity.type', function() {
return 'user/activity-'+this.get('activity.type');
})
});

View File

@@ -0,0 +1,9 @@
import Ember from 'ember';
export default Ember.Component.extend({
layoutName: 'components/user/activity-post',
isFirstPost: Ember.computed('activity.post.number', function() {
return this.get('activity.post.number') === 1;
})
});

View File

@@ -0,0 +1,71 @@
import Ember from 'ember';
export default Ember.Controller.extend({
needs: ['user'],
queryParams: ['filter'],
filter: '',
resultsLoading: false,
moreResults: true,
loadCount: 10,
getResults: function(start) {
var type;
switch (this.get('filter')) {
case 'discussions':
type = 'discussion';
break;
case 'posts':
type = 'post';
break;
}
var controller = this;
return this.store.find('activity', {
users: this.get('controllers.user.model.id'),
type: type,
start: start,
count: this.get('loadCount')
}).then(function(results) {
controller.set('moreResults', results.get('length') >= controller.get('loadCount'));
return results;
});
},
paramsDidChange: Ember.observer('filter', function() {
if (this.get('model') && !this.get('resultsLoading')) {
Ember.run.once(this, this.loadResults);
}
}),
loadResults: function() {
this.send('loadResults');
},
actions: {
loadResults: function() {
var controller = this;
controller.get('model').set('content', []);
controller.set('resultsLoading', true);
controller.getResults().then(function(results) {
controller
.set('resultsLoading', false)
.set('meta', results.get('meta'))
.set('model.content', results);
});
},
loadMore: function() {
var controller = this;
this.set('resultsLoading', true);
this.getResults(this.get('model.length')).then(function(results) {
controller.get('model.content').addObjects(results);
controller.set('meta', results.get('meta'));
controller.set('resultsLoading', false);
});
},
}
});

View File

@@ -0,0 +1,11 @@
import DS from 'ember-data';
export default DS.Model.extend({
type: DS.attr('string'),
content: DS.attr('string'),
time: DS.attr('date'),
user: DS.belongsTo('user'),
sender: DS.belongsTo('user'),
post: DS.belongsTo('post')
});

View File

@@ -14,8 +14,6 @@ Router.map(function() {
this.resource('user', {path: '/u/:username'}, function() {
this.route('activity', {path: '/'});
this.route('discussions');
this.route('posts');
this.route('edit');
});

View File

@@ -0,0 +1,12 @@
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return Ember.RSVP.resolve(Ember.ArrayProxy.create());
},
setupController: function(controller, model) {
controller.set('model', model);
controller.send('loadResults');
}
});

View File

@@ -95,3 +95,76 @@
}
}
}
.user-content .loading-indicator {
height: 46px;
}
.user-activity {
border-left: 3px solid @fl-body-secondary-color;
list-style: none;
margin: 0 0 0 16px;
padding: 0;
& > li {
margin-bottom: 30px;
padding-left: 32px;
}
& .activity-icon {
.avatar-size(32px);
float: left;
margin-left: -49px;
.box-shadow(0 0 0 3px #fff);
margin-top: -5px;
}
}
.activity-info {
color: @fl-body-muted-color;
margin-bottom: 10px;
& strong {
margin-right: 5px;
}
}
.activity-content {
display: block;
padding: 20px;
background: @fl-body-secondary-color;
border-radius: @border-radius-base;
color: @fl-body-muted-color;
&, &:hover {
text-decoration: none;
}
& .discussion-summary {
margin: -20px 0;
padding-left: 0;
& .author {
display: none;
}
}
}
.activity-post {
overflow: hidden;
& .title {
margin: 0 0 10px;
font-size: 14px;
font-weight: bold;
&, & a {
color: @fl-body-heading-color;
}
}
&:hover .title {
text-decoration: underline;
}
& .body {
color: @fl-body-muted-color;
line-height: 1.7em;
& :last-child {
margin-bottom: 0;
}
}
}

View File

@@ -0,0 +1 @@
{{component componentName activity=activity}}

View File

@@ -0,0 +1,6 @@
{{user-avatar activity.user class="activity-icon"}}
<div class="activity-info">
<strong>Joined the forum</strong>
{{human-time activity.time}}
</div>

View File

@@ -0,0 +1,21 @@
{{user-avatar activity.post.user class="activity-icon"}}
<div class="activity-info">
<strong>{{if isFirstPost "Started a discussion" "Posted a reply"}}</strong>
{{human-time activity.time}}
</div>
{{#if isFirstPost}}
<div class="activity-content activity-discussion">
{{index/discussion-listing discussion=activity.post.discussion}}
</div>
{{else}}
{{#link-to "discussion" activity.post.discussion (query-params start=activity.post.number) class="activity-content activity-post"}}
<h3 class="title">
{{activity.post.discussion.title}}
</h3>
<div class="body">
{{{activity.post.contentHtml}}}
</div>
{{/link-to}}
{{/if}}

View File

@@ -5,5 +5,7 @@
{{ui/item-list items=view.sidebar}}
</nav>
{{outlet}}
<div class="offset-content user-content">
{{outlet}}
</div>
</div>

View File

@@ -0,0 +1,13 @@
<ul class="user-activity">
{{#each activity in model}}
{{user/activity-item activity=activity}}
{{/each}}
</ul>
{{#if resultsLoading}}
{{ui/loading-indicator size="small"}}
{{else if moreResults}}
<div class="load-more">
{{ui/action-button class="control-loadMore btn btn-default" action="loadMore" label="Load More"}}
</div>
{{/if}}

View File

@@ -9,6 +9,22 @@ var precompileTemplate = Ember.Handlebars.compile;
export default Ember.View.extend(HasItemLists, {
itemLists: ['sidebar'],
didInsertElement: function() {
// Affix the sidebar so that when the user scrolls down it will stick
// to the top of their viewport.
var $sidebar = this.$('.user-nav');
$sidebar.find('> ul').affix({
offset: {
top: function () {
return $sidebar.offset().top - $('#header').outerHeight(true) - parseInt($sidebar.css('margin-top'));
},
bottom: function () {
return (this.bottom = $('#footer').outerHeight(true));
}
}
});
},
populateSidebar: function(items) {
var nav = this.populateItemList('nav');
items.pushObjectWithTag(DropdownSelect.extend({items: nav, listItemClass: 'title-control'}), 'nav');
@@ -18,7 +34,7 @@ export default Ember.View.extend(HasItemLists, {
items.pushObjectWithTag(NavItem.extend({
label: 'Activity',
icon: 'user',
layout: precompileTemplate('{{#link-to "user.activity"}}{{fa-icon icon}} {{label}}{{/link-to}}')
layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="")}}{{fa-icon icon}} {{label}}{{/link-to}}')
}), 'activity');
items.pushObjectWithTag(NavItem.extend({
@@ -26,7 +42,7 @@ export default Ember.View.extend(HasItemLists, {
icon: 'reorder',
badge: Ember.computed.alias('user.discussionsCount'),
user: this.get('controller.model'),
layout: precompileTemplate('{{#link-to "user.discussions"}}{{fa-icon icon}} {{label}} <span class="count">{{badge}}</span>{{/link-to}}')
layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="discussions")}}{{fa-icon icon}} {{label}} <span class="count">{{badge}}</span>{{/link-to}}')
}), 'discussions');
items.pushObjectWithTag(NavItem.extend({
@@ -34,7 +50,7 @@ export default Ember.View.extend(HasItemLists, {
icon: 'comment-o',
badge: Ember.computed.alias('user.commentsCount'),
user: this.get('controller.model'),
layout: precompileTemplate('{{#link-to "user.posts"}}{{fa-icon icon}} {{label}} <span class="count">{{badge}}</span>{{/link-to}}')
layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="posts")}}{{fa-icon icon}} {{label}} <span class="count">{{badge}}</span>{{/link-to}}')
}), 'posts');
}
});