From cf91bca0cd79f4589f1787e2c0a72e37606e471e Mon Sep 17 00:00:00 2001 From: Robin Ward <robin.ward@gmail.com> Date: Fri, 31 Jul 2015 14:22:28 -0400 Subject: [PATCH] FIX: Small actions should show descriptions on the user stream --- .../discourse/components/small-action.js.es6 | 29 +++++++--------- .../discourse/components/stream-item.js.es6 | 7 ++++ .../controllers/user-activity.js.es6 | 2 +- .../templates/components/small-action.hbs | 5 ++- .../templates/components/stream-item.hbs | 32 +++++++++++++++++ .../discourse/templates/user/stream.hbs | 30 ++-------------- .../views/user-activity-stream.js.es6 | 27 +++++++++++++++ .../discourse/views/user-stream.js.es6 | 6 ++-- app/controllers/user_actions_controller.rb | 2 +- app/models/user_action.rb | 1 + app/models/user_action_observer.rb | 34 +++++++++---------- app/serializers/user_action_serializer.rb | 9 ++--- 12 files changed, 110 insertions(+), 74 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/stream-item.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/stream-item.hbs create mode 100644 app/assets/javascripts/discourse/views/user-activity-stream.js.es6 diff --git a/app/assets/javascripts/discourse/components/small-action.js.es6 b/app/assets/javascripts/discourse/components/small-action.js.es6 index 50e42f19ab5..977b408c807 100644 --- a/app/assets/javascripts/discourse/components/small-action.js.es6 +++ b/app/assets/javascripts/discourse/components/small-action.js.es6 @@ -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'; diff --git a/app/assets/javascripts/discourse/components/stream-item.js.es6 b/app/assets/javascripts/discourse/components/stream-item.js.es6 new file mode 100644 index 00000000000..ef7b03ad5c3 --- /dev/null +++ b/app/assets/javascripts/discourse/components/stream-item.js.es6 @@ -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') +}); diff --git a/app/assets/javascripts/discourse/controllers/user-activity.js.es6 b/app/assets/javascripts/discourse/controllers/user-activity.js.es6 index 6113cedff3d..7e4ec2db92f 100644 --- a/app/assets/javascripts/discourse/controllers/user-activity.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-activity.js.es6 @@ -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"); diff --git a/app/assets/javascripts/discourse/templates/components/small-action.hbs b/app/assets/javascripts/discourse/templates/components/small-action.hbs index b05c6580884..091f0e07df2 100644 --- a/app/assets/javascripts/discourse/templates/components/small-action.hbs +++ b/app/assets/javascripts/discourse/templates/components/small-action.hbs @@ -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> diff --git a/app/assets/javascripts/discourse/templates/components/stream-item.hbs b/app/assets/javascripts/discourse/templates/components/stream-item.hbs new file mode 100644 index 00000000000..8dea1ccb131 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/stream-item.hbs @@ -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}} — <span class="edit-reason">{{grandChild.edit_reason}}</span>{{/if}} + {{/if}} + {{/each}} + </div> +{{/each}} diff --git a/app/assets/javascripts/discourse/templates/user/stream.hbs b/app/assets/javascripts/discourse/templates/user/stream.hbs index 96e96c83768..4f03bef896c 100644 --- a/app/assets/javascripts/discourse/templates/user/stream.hbs +++ b/app/assets/javascripts/discourse/templates/user/stream.hbs @@ -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}} — <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}} diff --git a/app/assets/javascripts/discourse/views/user-activity-stream.js.es6 b/app/assets/javascripts/discourse/views/user-activity-stream.js.es6 new file mode 100644 index 00000000000..86fbaefd8f2 --- /dev/null +++ b/app/assets/javascripts/discourse/views/user-activity-stream.js.es6 @@ -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(); + }); + } + } +}); diff --git a/app/assets/javascripts/discourse/views/user-stream.js.es6 b/app/assets/javascripts/discourse/views/user-stream.js.es6 index 367c089ed03..86fbaefd8f2 100644 --- a/app/assets/javascripts/discourse/views/user-stream.js.es6 +++ b/app/assets/javascripts/discourse/views/user-stream.js.es6 @@ -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(); diff --git a/app/controllers/user_actions_controller.rb b/app/controllers/user_actions_controller.rb index e4d4079fec0..c20f9c8d23d 100644 --- a/app/controllers/user_actions_controller.rb +++ b/app/controllers/user_actions_controller.rb @@ -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 diff --git a/app/models/user_action.rb b/app/models/user_action.rb index 0d4c661d100..9b72a755dea 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -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 diff --git a/app/models/user_action_observer.rb b/app/models/user_action_observer.rb index 665a1efdfce..34e6fcabedc 100644 --- a/app/models/user_action_observer.rb +++ b/app/models/user_action_observer.rb @@ -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] diff --git a/app/serializers/user_action_serializer.rb b/app/serializers/user_action_serializer.rb index f6dcf2b9fb3..8b393996392 100644 --- a/app/serializers/user_action_serializer.rb +++ b/app/serializers/user_action_serializer.rb @@ -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