mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 22:43:33 +08:00
FEATURE: bookmark topic button
This commit is contained in:
@ -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);
|
||||||
|
@ -18,15 +18,13 @@ 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
|
||||||
'm w': 'div.notification-options li[data-id="3"] a', // mark topic as watching
|
'm w': 'div.notification-options li[data-id="3"] a', // mark topic as watching
|
||||||
'x r': '#dismiss-new,#dismiss-new-top,#dismiss-posts,#dismiss-posts-top', // dismiss new/posts
|
'x r': '#dismiss-new,#dismiss-new-top,#dismiss-posts,#dismiss-posts-top', // dismiss new/posts
|
||||||
'x t': '#dismiss-topics,#dismiss-topics-top', //dismiss topics
|
'x t': '#dismiss-topics,#dismiss-topics-top', // dismiss topics
|
||||||
'.': '.alert.alert-info.clickable', // show incoming/updated topics
|
'.': '.alert.alert-info.clickable', // show incoming/updated topics
|
||||||
'n': '#user-notifications', // open notifications menu
|
'n': '#user-notifications', // open notifications menu
|
||||||
'o,enter': '.topic-list tr.selected a.title', // open selected topic
|
'o,enter': '.topic-list tr.selected a.title', // open selected topic
|
||||||
@ -36,7 +34,7 @@ var PATH_BINDINGS = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
FUNCTION_BINDINGS = {
|
FUNCTION_BINDINGS = {
|
||||||
'c': 'createTopic', // create new topic
|
'c': 'createTopic', // create new topic
|
||||||
'home': 'goToFirstPost',
|
'home': 'goToFirstPost',
|
||||||
'#': 'toggleProgress',
|
'#': 'toggleProgress',
|
||||||
'end': 'goToLastPost',
|
'end': 'goToLastPost',
|
||||||
@ -47,11 +45,11 @@ var PATH_BINDINGS = {
|
|||||||
'k': 'selectUp',
|
'k': 'selectUp',
|
||||||
'u': 'goBack',
|
'u': 'goBack',
|
||||||
'/': 'showSearch',
|
'/': 'showSearch',
|
||||||
'=': 'showSiteMap', // open site map menu
|
'=': 'showSiteMap', // open site map menu
|
||||||
'p': 'showCurrentUser', // open current user menu
|
'p': 'showCurrentUser', // open current user menu
|
||||||
'ctrl+f': 'showBuiltinSearch',
|
'ctrl+f': 'showBuiltinSearch',
|
||||||
'command+f': 'showBuiltinSearch',
|
'command+f': 'showBuiltinSearch',
|
||||||
'?': 'showHelpModal', // open keyboard shortcut help
|
'?': 'showHelpModal', // open keyboard shortcut help
|
||||||
'q': 'quoteReply'
|
'q': 'quoteReply'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
@ -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>");
|
||||||
|
}
|
||||||
|
});
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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'
|
||||||
|
@ -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+/}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user