FIX: Small actions should show descriptions on the user stream

This commit is contained in:
Robin Ward 2015-07-31 14:22:28 -04:00
parent d71301e406
commit cf91bca0cd
12 changed files with 110 additions and 74 deletions

View File

@ -13,27 +13,22 @@ const icons = {
'visible.disabled': 'eye-slash'
};
export function actionDescription(actionCode, createdAt) {
return function() {
const ac = this.get(actionCode);
if (actionCode) {
const dt = new Date(this.get(createdAt));
const when = Discourse.Formatter.relativeAge(dt, {format: 'medium-with-ago'});
return I18n.t(`action_codes.${ac}`, {when}).htmlSafe();
}
}.property(actionCode, createdAt);
}
export default Ember.Component.extend({
layoutName: 'components/small-action', // needed because `time-gap` inherits from this
classNames: ['small-action'],
description: function() {
const actionCode = this.get('actionCode');
if (actionCode) {
const dt = new Date(this.get('post.created_at'));
const when = Discourse.Formatter.relativeAge(dt, {format: 'medium-with-ago'});
var result = I18n.t(`action_codes.${actionCode}`, {when});
var cooked = this.get('post.cooked');
result = "<p>" + result + "</p>";
if (!Em.isEmpty(cooked)) {
result += "<div class='custom-message'>" + cooked + "</div>";
}
return result;
}
}.property('actionCode', 'post.created_at', 'post.cooked'),
description: actionDescription('actionCode', 'post.created_at'),
icon: function() {
return icons[this.get('actionCode')] || 'exclamation';

View File

@ -0,0 +1,7 @@
import { actionDescription } from 'discourse/components/small-action';
export default Ember.Component.extend({
classNameBindings: [':item', 'item.hidden', 'item.deleted', 'moderatorAction'],
moderatorAction: Discourse.computed.propertyEqual('item.post_type', 'site.post_types.moderator_action'),
actionDescription: actionDescription('item.action_code', 'item.created_at')
});

View File

@ -5,7 +5,7 @@ export default Ember.ObjectController.extend({
_showFooter: function() {
var showFooter;
if (this.get("userActionType")) {
var stat = _.find(this.get("model.stats"), { action_type: this.get("userActionType") });
const stat = _.find(this.get("model.stats"), { action_type: this.get("userActionType") });
showFooter = stat && stat.count <= this.get("model.stream.itemsLoaded");
} else {
showFooter = this.get("model.statsCountNonPM") <= this.get("model.stream.itemsLoaded");

View File

@ -11,5 +11,8 @@
{{avatar post imageSize="small"}}
</a>
{{/if}}
{{{description}}}
<p>{{description}}</p>
{{#if post.cooked}}
<div class='custom-message'>{{{post.cooked}}}</div>
{{/if}}
</div>

View File

@ -0,0 +1,32 @@
<div class='clearfix info'>
<a href={{item.userUrl}} data-user-card={{item.username}} class='avatar-link'><div class='avatar-wrapper'>{{avatar item imageSize="large" extraClasses="actor" ignoreTitle="true"}}</div></a>
<span class='time'>{{format-date item.created_at}}</span>
{{topic-status topic=item disableActions=true}}
<span class="title">
<a href={{item.postUrl}}>{{{item.title}}}</a>
</span>
<div class="category">{{category-link item.category}}</div>
</div>
{{#if actionDescription}}
<p class='excerpt'>{{actionDescription}}</p>
{{/if}}
<p class='excerpt'>{{{item.excerpt}}}</p>
{{#each item.children as |child|}}
<div class='child-actions'>
<i class="icon {{child.icon}}"></i>
{{#each child.items as |grandChild|}}
{{#if grandChild.removableBookmark}}
<button class="btn btn-default remove-bookmark" {{action "removeBookmark" grandChild}}>
{{fa-icon 'times'}} {{i18n "bookmarks.remove"}}
</button>
{{else}}
<a href={{grandChild.userUrl}} data-user-card={{grandChild.username}} class='avatar-link'><div class='avatar-wrapper'>{{avatar grandChild imageSize="tiny" extraClasses="actor" ignoreTitle="true"}}</div></a>
{{#if grandChild.edit_reason}} &mdash; <span class="edit-reason">{{grandChild.edit_reason}}</span>{{/if}}
{{/if}}
{{/each}}
</div>
{{/each}}

View File

@ -1,29 +1,3 @@
{{#each item in model.content}}
<div {{bind-attr class=":item item.hidden item.deleted item.moderator_action"}}>
<div class='clearfix info'>
<a href="{{unbound item.userUrl}}" data-user-card="{{unbound item.username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar item imageSize="large" extraClasses="actor" ignoreTitle="true"}}</div></a>
<span class='time'>{{format-date item.created_at}}</span>
{{topic-status topic=item disableActions=true}}
<span class="title">
<a href="{{unbound item.postUrl}}">{{{unbound item.title}}}</a>
</span>
<div class="category">{{category-link item.category}}</div>
</div>
<p class='excerpt'>{{{unbound item.excerpt}}}</p>
{{#each child in item.children}}
<div class='child-actions'>
<i class="icon {{unbound child.icon}}"></i>
{{#each grandChild in child.items}}
{{#if grandChild.removableBookmark}}
<button class="btn btn-default remove-bookmark" {{action "removeBookmark" grandChild}}>
{{fa-icon 'times'}} {{i18n "bookmarks.remove"}}
</button>
{{else}}
<a href="{{unbound grandChild.userUrl}}" data-user-card="{{unbound grandChild.username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar grandChild imageSize="tiny" extraClasses="actor" ignoreTitle="true"}}</div></a>
{{#if grandChild.edit_reason}} &mdash; <span class="edit-reason">{{unbound grandChild.edit_reason}}</span>{{/if}}
{{/if}}
{{/each}}
</div>
{{/each}}
</div>
{{#each model.content as |item|}}
{{stream-item item=item}}
{{/each}}

View File

@ -0,0 +1,27 @@
import LoadMore from "discourse/mixins/load-more";
export default Ember.View.extend(LoadMore, {
loading: false,
eyelineSelector: '.user-stream .item',
classNames: ['user-stream'],
_scrollTopOnModelChange: function() {
Em.run.schedule('afterRender', function() {
$(document).scrollTop(0);
});
}.observes('controller.model.user.id'),
actions: {
loadMore() {
const self = this;
if (this.get('loading')) { return; }
this.set('loading', true);
const stream = this.get('controller.model');
stream.findItems().then(function() {
self.set('loading', false);
self.get('eyeline').flushRest();
});
}
}
});

View File

@ -12,12 +12,12 @@ export default Ember.View.extend(LoadMore, {
}.observes('controller.model.user.id'),
actions: {
loadMore: function() {
var self = this;
loadMore() {
const self = this;
if (this.get('loading')) { return; }
this.set('loading', true);
var stream = this.get('controller.model');
const stream = this.get('controller.model');
stream.findItems().then(function() {
self.set('loading', false);
self.get('eyeline').flushRest();

View File

@ -24,7 +24,7 @@ class UserActionsController < ApplicationController
UserAction.stream(opts)
end
render_serialized(stream, UserActionSerializer, root: "user_actions")
render_serialized(stream, UserActionSerializer, root: 'user_actions')
end
def show

View File

@ -154,6 +154,7 @@ SQL
CASE WHEN coalesce(p.deleted_at, p2.deleted_at, t.deleted_at) IS NULL THEN false ELSE true END deleted,
p.hidden,
p.post_type,
p.action_code,
p.edit_reason,
t.category_id
FROM user_actions as a

View File

@ -29,11 +29,11 @@ class UserActionObserver < ActiveRecord::Observer
return unless action && post && user && post.id
row = {
action_type: action,
user_id: user.id,
acting_user_id: acting_user_id || post.user_id,
target_topic_id: post.topic_id,
target_post_id: post.id
action_type: action,
user_id: user.id,
acting_user_id: acting_user_id || post.user_id,
target_topic_id: post.topic_id,
target_post_id: post.id
}
if post.deleted_at.nil?
@ -48,12 +48,12 @@ class UserActionObserver < ActiveRecord::Observer
return if model.is_first_post?
row = {
action_type: UserAction::REPLY,
user_id: model.user_id,
acting_user_id: model.user_id,
target_post_id: model.id,
target_topic_id: model.topic_id,
created_at: model.created_at
action_type: UserAction::REPLY,
user_id: model.user_id,
acting_user_id: model.user_id,
target_post_id: model.id,
target_topic_id: model.topic_id,
created_at: model.created_at
}
rows = [row]
@ -79,12 +79,12 @@ class UserActionObserver < ActiveRecord::Observer
def log_topic(model)
row = {
action_type: model.archetype == Archetype.private_message ? UserAction::NEW_PRIVATE_MESSAGE : UserAction::NEW_TOPIC,
user_id: model.user_id,
acting_user_id: model.user_id,
target_topic_id: model.id,
target_post_id: -1,
created_at: model.created_at
action_type: model.archetype == Archetype.private_message ? UserAction::NEW_PRIVATE_MESSAGE : UserAction::NEW_TOPIC,
user_id: model.user_id,
acting_user_id: model.user_id,
target_topic_id: model.id,
target_post_id: -1,
created_at: model.created_at
}
rows = [row]

View File

@ -22,7 +22,8 @@ class UserActionSerializer < ApplicationSerializer
:title,
:deleted,
:hidden,
:moderator_action,
:post_type,
:action_code,
:edit_reason,
:category_id,
:uploaded_avatar_id,
@ -32,7 +33,7 @@ class UserActionSerializer < ApplicationSerializer
def excerpt
cooked = object.cooked || PrettyText.cook(object.raw)
PrettyText.excerpt(cooked, 300, { keep_emojis: true }) if cooked
PrettyText.excerpt(cooked, 300, keep_emojis: true) if cooked
end
def avatar_template
@ -67,10 +68,6 @@ class UserActionSerializer < ApplicationSerializer
object.title.present?
end
def moderator_action
object.post_type == Post.types[:moderator_action] || object.post_type == Post.types[:small_action]
end
def include_reply_to_post_number?
object.action_type == UserAction::REPLY
end