FEATURE: bookmark topic button

This commit is contained in:
Régis Hanol
2015-01-12 12:10:15 +01:00
parent a9ef92f3d8
commit c681b353f2
14 changed files with 103 additions and 48 deletions

View File

@ -213,8 +213,11 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
alert(I18n.t("bookmarks.not_bookmarked")); alert(I18n.t("bookmarks.not_bookmarked"));
return; return;
} }
post.toggleProperty('bookmarked'); if (post) {
return false; return post.toggleBookmark();
} else {
return this.get("model").toggleBookmark();
}
}, },
jumpTop: function() { jumpTop: function() {
@ -547,15 +550,13 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
// Receive notifications for this topic // Receive notifications for this topic
subscribe: function() { subscribe: function() {
// Unsubscribe before subscribing again // Unsubscribe before subscribing again
this.unsubscribe(); this.unsubscribe();
var bus = Discourse.MessageBus;
var topicController = this; var topicController = this;
bus.subscribe("/topic/" + (this.get('id')), function(data) { Discourse.MessageBus.subscribe("/topic/" + this.get('id'), function(data) {
var topic = topicController.get('model'); var topic = topicController.get('model');
if (data.notification_level_change) { if (data.notification_level_change) {
topic.set('details.notification_level', data.notification_level_change); topic.set('details.notification_level', data.notification_level_change);
topic.set('details.notifications_reason_id', data.notifications_reason_id); topic.set('details.notifications_reason_id', data.notifications_reason_id);

View File

@ -18,9 +18,7 @@ var PATH_BINDINGS = {
}, },
CLICK_BINDINGS = { CLICK_BINDINGS = {
// star topic 'f': '#topic-footer-buttons button.bookmark', // bookmark topic
'f': '#topic-footer-buttons button.star, .topic-list tr.topic-list-item.selected a.star',
'm m': 'div.notification-options li[data-id="0"] a', // mark topic as muted 'm m': 'div.notification-options li[data-id="0"] a', // mark topic as muted
'm r': 'div.notification-options li[data-id="1"] a', // mark topic as regular 'm r': 'div.notification-options li[data-id="1"] a', // mark topic as regular
'm t': 'div.notification-options li[data-id="2"] a', // mark topic as tracking 'm t': 'div.notification-options li[data-id="2"] a', // mark topic as tracking

View File

@ -68,17 +68,6 @@ Discourse.Post = Discourse.Model.extend({
return this.get("user_id") === Discourse.User.currentProp("id") || Discourse.User.currentProp('staff'); return this.get("user_id") === Discourse.User.currentProp("id") || Discourse.User.currentProp('staff');
}.property("user_id"), }.property("user_id"),
bookmarkedChanged: function() {
Discourse.Post.bookmark(this.get('id'), this.get('bookmarked'))
.then(null, function (error) {
if (error && error.responseText) {
bootbox.alert($.parseJSON(error.responseText).errors[0]);
} else {
bootbox.alert(I18n.t('generic_error'));
}
});
}.observes('bookmarked'),
wikiChanged: function() { wikiChanged: function() {
var data = { wiki: this.get("wiki") }; var data = { wiki: this.get("wiki") };
this._updatePost("wiki", data); this._updatePost("wiki", data);
@ -421,6 +410,28 @@ Discourse.Post = Discourse.Model.extend({
unhide: function () { unhide: function () {
return Discourse.ajax("/posts/" + this.get("id") + "/unhide", { type: "PUT" }); return Discourse.ajax("/posts/" + this.get("id") + "/unhide", { type: "PUT" });
},
toggleBookmark: function() {
var self = this;
this.toggleProperty("bookmarked");
if (this.get("post_number") === 1) { this.toggleProperty("topic.bookmarked"); }
return Discourse.ajax("/posts/" + this.get("id") + "/bookmark", {
type: 'PUT',
data: { bookmarked: this.get("bookmarked") }
}).then(null, function (error) {
self.toggleProperty("bookmarked");
if (this.get("post_number") === 1) { this.toggleProperty("topic.bookmarked"); }
if (error && error.responseText) {
bootbox.alert($.parseJSON(error.responseText).errors[0]);
} else {
bootbox.alert(I18n.t('generic_error'));
}
});
} }
}); });
@ -491,10 +502,6 @@ Discourse.Post.reopenClass({
return Discourse.ajax("/posts/" + postId + ".json").then(function (result) { return Discourse.ajax("/posts/" + postId + ".json").then(function (result) {
return Discourse.Post.create(result); return Discourse.Post.create(result);
}); });
},
bookmark: function(postId, bookmarked) {
return Discourse.ajax("/posts/" + postId + "/bookmark", { type: 'PUT', data: { bookmarked: bookmarked } });
} }
}); });

View File

@ -181,14 +181,17 @@ Discourse.Topic = Discourse.Model.extend({
}.property('word_count'), }.property('word_count'),
toggleBookmark: function() { toggleBookmark: function() {
var topic = this; var self = this, firstPost = this.get("postStream.posts")[0];
topic.toggleProperty('bookmarked');
return Discourse.ajax({ this.toggleProperty('bookmarked');
url: "" + (this.get('url')) + "/bookmark", if (this.get("postStream.firstPostPresent")) { firstPost.toggleProperty("bookmarked"); }
return Discourse.ajax('/t/' + this.get('id') + '/bookmark', {
type: 'PUT', type: 'PUT',
data: { bookmarked: topic.get('bookmarked') ? true : false } data: { bookmarked: self.get('bookmarked') }
}).then(null, function (error) { }).then(null, function (error) {
topic.toggleProperty('bookmarked'); self.toggleProperty('bookmarked');
if (self.get("postStream.firstPostPresent")) { firstPost.toggleProperty('bookmarked'); }
if (error && error.responseText) { if (error && error.responseText) {
bootbox.alert($.parseJSON(error.responseText).errors); bootbox.alert($.parseJSON(error.responseText).errors);

View File

@ -36,7 +36,7 @@
<div class="span6"> <div class="span6">
<h4>{{i18n 'keyboard_shortcuts_help.actions.title'}}</h4> <h4>{{i18n 'keyboard_shortcuts_help.actions.title'}}</h4>
<ul> <ul>
<li>{{{i18n 'keyboard_shortcuts_help.actions.star'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.actions.bookmark_topic'}}}</li>
<li>{{{i18n 'keyboard_shortcuts_help.actions.pin_unpin_topic'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.actions.pin_unpin_topic'}}}</li>
<li>{{{i18n 'keyboard_shortcuts_help.actions.share_topic'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.actions.share_topic'}}}</li>
<li>{{{i18n 'keyboard_shortcuts_help.actions.share_post'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.actions.share_post'}}}</li>

View File

@ -0,0 +1,19 @@
import ButtonView from 'discourse/views/button';
export default ButtonView.extend({
classNames: ['bookmark'],
textKey: 'bookmarked.title',
helpKeyBinding: 'controller.bookmarkTooltipKey',
attributeBindings: ['disabled'],
rerenderTriggers: ['controller.bookmarked'],
click: function() {
this.get('controller').send('toggleBookmark');
},
renderIcon: function(buffer) {
var className = this.get("controller.bookmarked") ? "fa-bookmark" : "fa-bookmark-o";
buffer.push("<i class='fa " + className + "'></i>");
}
});

View File

@ -1,6 +1,7 @@
import TopicAdminMenuButton from 'discourse/views/topic-admin-menu-button'; import TopicAdminMenuButton from 'discourse/views/topic-admin-menu-button';
import LoginReplyButton from 'discourse/views/login-reply-button'; import LoginReplyButton from 'discourse/views/login-reply-button';
import FlagTopicButton from 'discourse/views/flag-topic-button'; import FlagTopicButton from 'discourse/views/flag-topic-button';
import BookmarkButton from 'discourse/views/bookmark-button';
import ShareButton from 'discourse/views/share-button'; import ShareButton from 'discourse/views/share-button';
import InviteReplyButton from 'discourse/views/invite-reply-button'; import InviteReplyButton from 'discourse/views/invite-reply-button';
import ReplyButton from 'discourse/views/reply-button'; import ReplyButton from 'discourse/views/reply-button';
@ -29,6 +30,7 @@ export default DiscourseContainerView.extend({
if (this.get('topic.details.can_invite_to')) { if (this.get('topic.details.can_invite_to')) {
this.attachViewClass(InviteReplyButton); this.attachViewClass(InviteReplyButton);
} }
this.attachViewClass(BookmarkButton);
this.attachViewClass(ShareButton); this.attachViewClass(ShareButton);
if (this.get('topic.details.can_flag_topic')) { if (this.get('topic.details.can_flag_topic')) {
this.attachViewClass(FlagTopicButton); this.attachViewClass(FlagTopicButton);

View File

@ -248,13 +248,13 @@ class PostsController < ApplicationController
def bookmark def bookmark
post = find_post_from_params post = find_post_from_params
if current_user
if params[:bookmarked] == "true" if params[:bookmarked] == "true"
PostAction.act(current_user, post, PostActionType.types[:bookmark]) PostAction.act(current_user, post, PostActionType.types[:bookmark])
else else
PostAction.remove_act(current_user, post, PostActionType.types[:bookmark]) PostAction.remove_act(current_user, post, PostActionType.types[:bookmark])
end end
end
render nothing: true render nothing: true
end end

View File

@ -25,7 +25,8 @@ class TopicsController < ApplicationController
:autoclose, :autoclose,
:bulk, :bulk,
:reset_new, :reset_new,
:change_post_owners] :change_post_owners,
:bookmark]
before_filter :consider_user_for_promotion, only: :show before_filter :consider_user_for_promotion, only: :show
@ -212,6 +213,19 @@ class TopicsController < ApplicationController
render nothing: true render nothing: true
end end
def bookmark
topic = Topic.find_by(id: params[:topic_id])
first_post = topic.ordered_posts.first
if params[:bookmarked] == "true"
PostAction.act(current_user, first_post, PostActionType.types[:bookmark])
else
PostAction.remove_act(current_user, first_post, PostActionType.types[:bookmark])
end
render nothing: true
end
def destroy def destroy
topic = Topic.find_by(id: params[:id]) topic = Topic.find_by(id: params[:id])
guardian.ensure_can_delete!(topic) guardian.ensure_can_delete!(topic)

View File

@ -7,8 +7,14 @@ class PostActionType < ActiveRecord::Base
end end
def types def types
@types ||= Enum.new(:bookmark, :like, :off_topic, :inappropriate, :vote, @types ||= Enum.new(:bookmark,
:notify_user, :notify_moderators, :spam) :like,
:off_topic,
:inappropriate,
:vote,
:notify_user,
:notify_moderators,
:spam)
end end
def auto_action_flag_types def auto_action_flag_types

View File

@ -280,7 +280,6 @@ SQL
end end
def self.ensure_consistency!(topic_id=nil) def self.ensure_consistency!(topic_id=nil)
update_post_action_cache update_post_action_cache
# TODO this needs some reworking, when we mark stuff skipped # TODO this needs some reworking, when we mark stuff skipped

View File

@ -42,7 +42,8 @@ class TopicViewSerializer < ApplicationSerializer
:actions_summary, :actions_summary,
:expandable_first_post, :expandable_first_post,
:is_warning, :is_warning,
:chunk_size :chunk_size,
:bookmarked
# Define a delegator for each attribute of the topic we want # Define a delegator for each attribute of the topic we want
attributes(*topic_attributes) attributes(*topic_attributes)
@ -203,4 +204,8 @@ class TopicViewSerializer < ApplicationSerializer
object.topic.expandable_first_post? object.topic.expandable_first_post?
end end
def bookmarked
object.topic_user.try(:bookmarked)
end
end end

View File

@ -2229,7 +2229,7 @@ en:
dismiss_topics: '<b>x</b>, <b>t</b> Dismiss Topics' dismiss_topics: '<b>x</b>, <b>t</b> Dismiss Topics'
actions: actions:
title: 'Actions' title: 'Actions'
star: '<b>f</b> Star topic' bookmark_topic: '<b>f</b> Bookmark topic'
pin_unpin_topic: '<b>shift</b>+<b>p</b> Pin/Unpin topic' pin_unpin_topic: '<b>shift</b>+<b>p</b> Pin/Unpin topic'
share_topic: '<b>shift</b>+<b>s</b> Share topic' share_topic: '<b>shift</b>+<b>s</b> Share topic'
share_post: '<b>s</b> Share post' share_post: '<b>s</b> Share post'

View File

@ -414,6 +414,7 @@ Discourse::Application.routes.draw do
post "t/:topic_id/merge-topic" => "topics#merge_topic", constraints: {topic_id: /\d+/} post "t/:topic_id/merge-topic" => "topics#merge_topic", constraints: {topic_id: /\d+/}
post "t/:topic_id/change-owner" => "topics#change_post_owners", constraints: {topic_id: /\d+/} post "t/:topic_id/change-owner" => "topics#change_post_owners", constraints: {topic_id: /\d+/}
delete "t/:topic_id/timings" => "topics#destroy_timings", constraints: {topic_id: /\d+/} delete "t/:topic_id/timings" => "topics#destroy_timings", constraints: {topic_id: /\d+/}
put "t/:topic_id/bookmark" => "topics#bookmark", constraints: {topic_id: /\d+/}
post "t/:topic_id/notifications" => "topics#set_notifications" , constraints: {topic_id: /\d+/} post "t/:topic_id/notifications" => "topics#set_notifications" , constraints: {topic_id: /\d+/}