diff --git a/app/assets/javascripts/discourse/models/user_action.js b/app/assets/javascripts/discourse/models/user_action.js
index 3b584f059b4..7a781f43aca 100644
--- a/app/assets/javascripts/discourse/models/user_action.js
+++ b/app/assets/javascripts/discourse/models/user_action.js
@@ -102,9 +102,8 @@ Discourse.UserAction = Discourse.Model.extend({
presentName: Em.computed.any('name', 'username'),
targetDisplayName: Em.computed.any('target_name', 'target_username'),
actingDisplayName: Em.computed.any('acting_name', 'acting_username'),
-
-
targetUserUrl: Discourse.computed.url('target_username', '/users/%@'),
+
usernameLower: function() {
return this.get('username').toLowerCase();
}.property('username'),
@@ -122,6 +121,7 @@ Discourse.UserAction = Discourse.Model.extend({
replyType: Em.computed.equal('action_type', UserActionTypes.replies),
postType: Em.computed.equal('action_type', UserActionTypes.posts),
topicType: Em.computed.equal('action_type', UserActionTypes.topics),
+ bookmarkType: Em.computed.equal('action_type', UserActionTypes.bookmarks),
messageSentType: Em.computed.equal('action_type', UserActionTypes.messages_sent),
messageReceivedType: Em.computed.equal('action_type', UserActionTypes.messages_received),
mentionType: Em.computed.equal('action_type', UserActionTypes.mentions),
@@ -168,7 +168,11 @@ Discourse.UserAction = Discourse.Model.extend({
});
}
return rval;
- }.property("childGroups"),
+ }.property("childGroups",
+ "childGroups.likes.items", "childGroups.likes.items.@each",
+ "childGroups.stars.items", "childGroups.stars.items.@each",
+ "childGroups.edits.items", "childGroups.edits.items.@each",
+ "childGroups.bookmarks.items", "childGroups.bookmarks.items.@each"),
switchToActing: function() {
this.setProperties({
@@ -193,7 +197,6 @@ Discourse.UserAction.reopenClass({
var current;
if (Discourse.UserAction.TO_COLLAPSE.indexOf(item.action_type) >= 0) {
current = Discourse.UserAction.create(item);
- current.setProperties({action_type: null, description: null});
item.switchToActing();
current.addChild(item);
} else {
@@ -217,11 +220,13 @@ Discourse.UserAction.reopenClass({
TYPES: UserActionTypes,
TYPES_INVERTED: InvertedActionTypes,
- TO_COLLAPSE: [UserActionTypes.likes_given,
- UserActionTypes.likes_received,
- UserActionTypes.starred,
- UserActionTypes.edits,
- UserActionTypes.bookmarks],
+ TO_COLLAPSE: [
+ UserActionTypes.likes_given,
+ UserActionTypes.likes_received,
+ UserActionTypes.starred,
+ UserActionTypes.edits,
+ UserActionTypes.bookmarks
+ ],
TO_SHOW: [
UserActionTypes.likes_given,
@@ -234,6 +239,3 @@ Discourse.UserAction.reopenClass({
]
});
-
-
-
diff --git a/app/assets/javascripts/discourse/models/user_stream.js b/app/assets/javascripts/discourse/models/user_stream.js
index 6a72c7ef88b..0ea56a6d0e4 100644
--- a/app/assets/javascripts/discourse/models/user_stream.js
+++ b/app/assets/javascripts/discourse/models/user_stream.js
@@ -36,6 +36,30 @@ Discourse.UserStream = Discourse.Model.extend({
return this.findItems();
},
+ remove: function(userAction) {
+ // 1) remove the user action from the child groups
+ this.get("content").forEach(function (ua) {
+ ["likes", "stars", "edits", "bookmarks"].forEach(function (group) {
+ var items = ua.get("childGroups." + group + ".items");
+ if (items) {
+ items.removeObject(userAction);
+ }
+ });
+ });
+
+ // 2) remove the parents that have no children
+ var content = this.get("content").filter(function (ua) {
+ return ["likes", "stars", "edits", "bookmarks"].any(function (group) {
+ return ua.get("childGroups." + group + ".items.length") > 0;
+ });
+ });
+
+ this.setProperties({
+ content: content,
+ itemsLoaded: content.length
+ });
+ },
+
findItems: function() {
var userStream = this;
if(this.get('loading')) { return Ember.RSVP.reject(); }
diff --git a/app/assets/javascripts/discourse/routes/user_activity_route.js b/app/assets/javascripts/discourse/routes/user_activity_route.js
index e5c9644110c..d1c86b547ac 100644
--- a/app/assets/javascripts/discourse/routes/user_activity_route.js
+++ b/app/assets/javascripts/discourse/routes/user_activity_route.js
@@ -36,4 +36,4 @@ Discourse.UserActivityRoute = Discourse.Route.extend({
});
-Discourse.UserPrivateMessagesRoute = Discourse.UserActivityRoute.extend({});
\ No newline at end of file
+Discourse.UserPrivateMessagesRoute = Discourse.UserActivityRoute.extend({});
diff --git a/app/assets/javascripts/discourse/routes/user_activity_stream_route.js b/app/assets/javascripts/discourse/routes/user_activity_stream_route.js
index 56a333b0201..b26e9e705cb 100644
--- a/app/assets/javascripts/discourse/routes/user_activity_stream_route.js
+++ b/app/assets/javascripts/discourse/routes/user_activity_stream_route.js
@@ -24,6 +24,25 @@ Discourse.UserActivityStreamRoute = Discourse.Route.extend({
this.controllerFor('user_activity').set('userActionType', this.get('userActionType'));
this.controllerFor('user').set('indexStream', !this.get('userActionType'));
+ },
+
+ actions: {
+
+ removeBookmark: function(userAction) {
+ var self = this;
+ Discourse.ajax("/posts/by_number/" + userAction.topic_id + "/" + userAction.post_number + "/bookmarks/remove", { type: "PUT" })
+ .then(function() {
+ // remove the user action from the stream
+ self.modelFor("user").get("stream").remove(userAction);
+ // update the counts
+ self.modelFor("user").get("stats").forEach(function (stat) {
+ if (stat.get("action_type") === userAction.action_type) {
+ stat.decrementProperty("count");
+ }
+ })
+ });
+ },
+
}
});
diff --git a/app/assets/javascripts/discourse/templates/user/stream.js.handlebars b/app/assets/javascripts/discourse/templates/user/stream.js.handlebars
index 4f3972ef299..2a80eaea293 100644
--- a/app/assets/javascripts/discourse/templates/user/stream.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/user/stream.js.handlebars
@@ -8,13 +8,17 @@
{{unbound descriptionHtml}}
-
- {{{unbound excerpt}}}
-
+ {{{unbound excerpt}}}
{{#groupedEach children}}
{{#groupedEach items}}
+ {{#if bookmarkType}}
+
+ {{/if}}
{{avatar this imageSize="tiny" extraClasses="actor" ignoreTitle="true"}}
{{#if edit_reason}} —
{{unbound edit_reason}}{{/if}}
{{/groupedEach}}
diff --git a/app/assets/javascripts/discourse/views/user/activity_filter_view.js b/app/assets/javascripts/discourse/views/user/activity_filter_view.js
index 439f75f5ace..985e2527967 100644
--- a/app/assets/javascripts/discourse/views/user/activity_filter_view.js
+++ b/app/assets/javascripts/discourse/views/user/activity_filter_view.js
@@ -10,7 +10,7 @@ Discourse.ActivityFilterView = Ember.Component.extend({
tagName: 'li',
classNameBindings: ['active', 'noGlyph'],
- shouldRerender: Discourse.View.renderIfChanged('count'),
+ shouldRerender: Discourse.View.renderIfChanged('content.count', 'count'),
noGlyph: Em.computed.empty('icon'),
active: function() {
@@ -23,11 +23,10 @@ Discourse.ActivityFilterView = Ember.Component.extend({
}.property('userActionType', 'indexStream'),
activityCount: function() {
- return this.get('content.count') || this.get('count');
+ return this.get('content.count') || this.get('count') || 0;
}.property('content.count', 'count'),
typeKey: function() {
-
var actionType = this.get('content.action_type');
if (actionType === Discourse.UserAction.TYPES.messages_received) { return ""; }
@@ -52,23 +51,17 @@ Discourse.ActivityFilterView = Ember.Component.extend({
if (icon) {
buffer.push("
");
}
-
buffer.push(this.get('description') + "
(" + this.get('activityCount') + ")");
buffer.push("
");
},
- icon: function(){
- switch(parseInt(this.get('content.action_type'),10)) {
- case Discourse.UserAction.TYPES.likes_received:
- return "heart";
- case Discourse.UserAction.TYPES.bookmarks:
- return "bookmark";
- case Discourse.UserAction.TYPES.edits:
- return "pencil";
- case Discourse.UserAction.TYPES.replies:
- return "reply";
- case Discourse.UserAction.TYPES.starred:
- return "star";
+ icon: function() {
+ switch(parseInt(this.get('content.action_type'), 10)) {
+ case Discourse.UserAction.TYPES.likes_received: return "heart";
+ case Discourse.UserAction.TYPES.bookmarks: return "bookmark";
+ case Discourse.UserAction.TYPES.edits: return "pencil";
+ case Discourse.UserAction.TYPES.replies: return "reply";
+ case Discourse.UserAction.TYPES.starred: return "star";
}
}.property("content.action_type")
diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss
index a52fe7c1805..17ea377dd7f 100644
--- a/app/assets/stylesheets/desktop/user.scss
+++ b/app/assets/stylesheets/desktop/user.scss
@@ -344,5 +344,9 @@
background-color: scale-color($highlight, $lightness: 25%);
padding: 3px 5px 5px 5px;
}
+ .remove-bookmark {
+ float: right;
+ margin-top: -4px;
+ }
}
}
diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss
index 1fff6649a47..9acae27ef6d 100644
--- a/app/assets/stylesheets/mobile/user.scss
+++ b/app/assets/stylesheets/mobile/user.scss
@@ -249,5 +249,9 @@
background-color: scale-color($highlight, $lightness: 50%);
padding: 3px 5px 5px 5px;
}
+ .remove-bookmark {
+ float: right !important;
+ margin-top: -8px;
+ }
}
}
diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index 83943160975..68abb60c4e0 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -122,6 +122,14 @@ class PostsController < ApplicationController
display_post(post)
end
+ def remove_bookmark_by_number
+ if current_user
+ post = find_post_from_params_by_number
+ PostAction.remove_act(current_user, post, PostActionType.types[:bookmark])
+ end
+ render nothing: true
+ end
+
def reply_history
post = find_post_from_params
render_serialized(post.reply_history, PostSerializer)
diff --git a/app/controllers/user_actions_controller.rb b/app/controllers/user_actions_controller.rb
index 9b617546071..2c61e211b71 100644
--- a/app/controllers/user_actions_controller.rb
+++ b/app/controllers/user_actions_controller.rb
@@ -1,4 +1,5 @@
class UserActionsController < ApplicationController
+
def index
params.require(:username)
params.permit(:filter, :offset)
@@ -24,9 +25,4 @@ class UserActionsController < ApplicationController
render json: UserAction.stream_item(params[:id], guardian)
end
- def private_messages
- # todo
- end
-
-
end
diff --git a/app/models/user_action.rb b/app/models/user_action.rb
index 0c19f8c93ee..b52df902ceb 100644
--- a/app/models/user_action.rb
+++ b/app/models/user_action.rb
@@ -103,6 +103,7 @@ SQL
builder = SqlBuilder.new("
SELECT
+ a.id,
t.title, a.action_type, a.created_at, t.id topic_id,
a.user_id AS target_user_id, au.name AS target_name, au.username AS target_username,
coalesce(p.post_number, 1) post_number,
diff --git a/app/serializers/user_action_serializer.rb b/app/serializers/user_action_serializer.rb
index 4e7629e4a82..7fed4913633 100644
--- a/app/serializers/user_action_serializer.rb
+++ b/app/serializers/user_action_serializer.rb
@@ -68,8 +68,12 @@ class UserActionSerializer < ApplicationSerializer
object.post_type == Post.types[:moderator_action]
end
- def edit_reason
- object.edit_reason if object.action_type == UserAction::EDIT
+ def include_reply_to_post_number?
+ object.action_type == UserAction::REPLY
+ end
+
+ def include_edit_reason?
+ object.action_type == UserAction::EDIT
end
private
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 1baf4b00a44..77a1a8112e2 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -146,6 +146,7 @@ en:
created: "you've bookmarked this post"
not_bookmarked: "you've read this post; click to bookmark it"
last_read: "this is the last post you've read; click to bookmark it"
+ remove: "Remove Bookmark"
new_topics_inserted: "{{count}} new topics."
show_new_topics: "Click to show."
diff --git a/config/routes.rb b/config/routes.rb
index 9e92d084720..a66953b547e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -214,6 +214,7 @@ Discourse::Application.routes.draw do
post "uploads" => "uploads#create"
get "posts/by_number/:topic_id/:post_number" => "posts#by_number"
+ put "posts/by_number/:topic_id/:post_number/bookmarks/remove" => "posts#remove_bookmark_by_number"
get "posts/:id/reply-history" => "posts#reply_history"
resources :groups do
diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb
index 4b5474e5856..ec3941454b3 100644
--- a/spec/controllers/posts_controller_spec.rb
+++ b/spec/controllers/posts_controller_spec.rb
@@ -288,9 +288,13 @@ describe PostsController do
let(:post) { Fabricate(:post, user: log_in) }
it "raises an error if the user doesn't have permission to see the post" do
- Guardian.any_instance.expects(:can_see?).with(post).returns(false)
+ Guardian.any_instance.expects(:can_see?).with(post).returns(false).twice
+
xhr :put, :bookmark, post_id: post.id, bookmarked: 'true'
response.should be_forbidden
+
+ xhr :put, :remove_bookmark_by_number, topic_id: post.topic_id, post_number: post.post_number
+ response.should be_forbidden
end
it 'creates a bookmark' do
@@ -303,6 +307,11 @@ describe PostsController do
xhr :put, :bookmark, post_id: post.id
end
+ it 'removes a bookmark using the topic_id and the post_number' do
+ PostAction.expects(:remove_act).with(post.user, post, PostActionType.types[:bookmark])
+ xhr :put, :remove_bookmark_by_number, topic_id: post.topic_id, post_number: post.post_number
+ end
+
end
end