Skip to content

Commit

Permalink
FEATURE: offer to unwatch categories when unwatching category
Browse files Browse the repository at this point in the history
  • Loading branch information
SamSaffron committed Jun 28, 2016
1 parent 3e07658 commit 1411eed
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 2 deletions.
29 changes: 28 additions & 1 deletion app/assets/javascripts/discourse/controllers/preferences.js.es6
Expand Up @@ -2,6 +2,7 @@ import { setting } from 'discourse/lib/computed';
import CanCheckEmails from 'discourse/mixins/can-check-emails';
import { popupAjaxError } from 'discourse/lib/ajax-error';
import computed from "ember-addons/ember-computed-decorators";
import { categoryBadgeHTML } from "discourse/helpers/category-link";

export default Ember.Controller.extend(CanCheckEmails, {

Expand Down Expand Up @@ -134,6 +135,25 @@ export default Ember.Controller.extend(CanCheckEmails, {
this.set('saved', false);

const model = this.get('model');


// watched status changes warn user
const changedWatch = model.changedCategoryNotifications("watched");

if (changedWatch.remove.length > 0 && !this.get("warnedRemoveWatch")) {
var categories = Discourse.Category.findByIds(changedWatch.remove).map((cat) => {
return categoryBadgeHTML(cat);
}).join(" ");
bootbox.confirm(I18n.t('user.warn_unwatch.message', {categories: categories}),
I18n.t('user.warn_unwatch.no_value', {count: changedWatch.remove.length}), I18n.t('user.warn_unwatch.yes_value'),
(yes)=>{
this.set('unwatchCategoryTopics', yes ? changedWatch.remove : false);
this.send('save');
});
this.set("warnedRemoveWatch", true);
return;
}

const userFields = this.get('userFields');

// Update the user fields
Expand All @@ -148,12 +168,19 @@ export default Ember.Controller.extend(CanCheckEmails, {

// Cook the bio for preview
model.set('name', this.get('newNameInput'));
return model.save().then(() => {
var options = {};
if (this.get('warnedRemoveWatch') && this.get('unwatchCategoryTopics')) {
options["unwatchCategoryTopics"] = this.get("unwatchCategoryTopics");
}

return model.save(options).then(() => {
if (Discourse.User.currentProp('id') === model.get('id')) {
Discourse.User.currentProp('name', model.get('name'));
}
model.set('bio_cooked', Discourse.Markdown.cook(Discourse.Markdown.sanitize(model.get('bio_raw'))));
this.set('saved', true);
this.set("unwatchTopics", false);
this.set('warnedRemoveWatch', false);
}).catch(popupAjaxError);
},

Expand Down
21 changes: 20 additions & 1 deletion app/assets/javascripts/discourse/models/user.js.es6
Expand Up @@ -141,7 +141,7 @@ const User = RestModel.extend({
return Discourse.User.create(this.getProperties(Object.keys(this)));
},

save() {
save(options) {
const data = this.getProperties(
'bio_raw',
'website',
Expand Down Expand Up @@ -177,8 +177,12 @@ const User = RestModel.extend({
data[s] = this.get(`user_option.${s}`);
});

var updatedState = {};

['muted','watched','tracked'].forEach(s => {
let cats = this.get(s + 'Categories').map(c => c.get('id'));
updatedState[s + '_category_ids'] = cats;

// HACK: denote lack of categories
if (cats.length === 0) { cats = [-1]; }
data[s + '_category_ids'] = cats;
Expand All @@ -188,6 +192,10 @@ const User = RestModel.extend({
data['edit_history_public'] = this.get('user_option.edit_history_public');
}

if (options && options.unwatchCategoryTopics) {
data.unwatch_category_topics = options.unwatchCategoryTopics;
}

// TODO: We can remove this when migrated fully to rest model.
this.set('isSaving', true);
return Discourse.ajax(`/users/${this.get('username_lower')}`, {
Expand All @@ -197,6 +205,7 @@ const User = RestModel.extend({
this.set('bio_excerpt', result.user.bio_excerpt);
const userProps = Em.getProperties(this.get('user_option'),'enable_quoting', 'external_links_in_new_tab', 'dynamic_favicon');
Discourse.User.current().setProperties(userProps);
this.setProperties(updatedState);
}).finally(() => {
this.set('isSaving', false);
});
Expand Down Expand Up @@ -352,6 +361,16 @@ const User = RestModel.extend({
this.set("watchedCategories", Discourse.Category.findByIds(this.watched_category_ids));
},

changedCategoryNotifications: function(type) {
const ids = this.get(type + "Categories").map(c => c.id);
const oldIds = this.get(type + "_category_ids");

return {
add: _.difference(ids, oldIds),
remove: _.difference(oldIds, ids),
}
},

@computed("can_delete_account", "reply_count", "topic_count")
canDeleteAccount(canDeleteAccount, replyCount, topicCount) {
return !Discourse.SiteSettings.enable_sso && canDeleteAccount && ((replyCount || 0) + (topicCount || 0)) <= 1;
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/users_controller.rb
Expand Up @@ -89,6 +89,10 @@ def update
user = fetch_user_from_params
guardian.ensure_can_edit!(user)

if params[:unwatch_category_topics]
TopicUser.unwatch_categories!(user, params[:unwatch_category_topics])
end

if params[:user_fields].present?
params[:custom_fields] = {} unless params[:custom_fields].present?

Expand Down
26 changes: 26 additions & 0 deletions app/models/topic_user.rb
Expand Up @@ -59,6 +59,32 @@ def auto_watch(user_id, topic_id)
topic_user.save
end

def unwatch_categories!(user, category_ids)

track_threshold = user.user_option.auto_track_topics_after_msecs

sql = <<SQL
UPDATE topic_users tu
SET notification_level = CASE
WHEN t.user_id = :user_id THEN :watching
WHEN total_msecs_viewed > :track_threshold AND :track_threshold >= 0 THEN :tracking
ELSE :regular
end
FROM topics t
WHERE t.id = tu.topic_id AND tu.notification_level <> :muted AND category_id IN (:category_ids) AND tu.user_id = :user_id
SQL

exec_sql(sql,
watching: notification_levels[:watching],
tracking: notification_levels[:tracking],
regular: notification_levels[:regular],
muted: notification_levels[:muted],
category_ids: category_ids,
user_id: user.id,
track_threshold: track_threshold
)
end

# Find the information specific to a user in a forum topic
def lookup_for(user, topics)
# If the user isn't logged in, there's no last read posts
Expand Down
7 changes: 7 additions & 0 deletions config/locales/client.en.yml
Expand Up @@ -574,6 +574,13 @@ en:
failed_to_move: "Failed to move selected messages (perhaps your network is down)"
select_all: "Select All"

warn_unwatch:
message: "Also stop watching previously watched topics in {{categories}}?"
yes_value: "Yes, unwatch topics"
no_value:
one: "No, only unwatch category"
other: "No, only unwatch categories"

change_password:
success: "(email sent)"
in_progress: "(sending email)"
Expand Down
28 changes: 28 additions & 0 deletions spec/models/topic_user_spec.rb
Expand Up @@ -2,6 +2,34 @@

describe TopicUser do

describe "#unwatch_categories!" do
it "correctly unwatches categories" do

op_topic = Fabricate(:topic)
another_topic = Fabricate(:topic)
tracked_topic = Fabricate(:topic)

user = op_topic.user
watching = TopicUser.notification_levels[:watching]
regular = TopicUser.notification_levels[:regular]
tracking = TopicUser.notification_levels[:tracking]

TopicUser.change(user.id, op_topic, notification_level: watching)
TopicUser.change(user.id, another_topic, notification_level: watching)
TopicUser.change(user.id, tracked_topic, notification_level: watching, total_msecs_viewed: SiteSetting.default_other_auto_track_topics_after_msecs + 1)

TopicUser.unwatch_categories!(user, [Fabricate(:category).id, Fabricate(:category).id])
expect(TopicUser.get(another_topic, user).notification_level).to eq(watching)

TopicUser.unwatch_categories!(user, [op_topic.category_id])

expect(TopicUser.get(op_topic, user).notification_level).to eq(watching)
expect(TopicUser.get(another_topic, user).notification_level).to eq(regular)
expect(TopicUser.get(tracked_topic, user).notification_level).to eq(tracking)
end

end

describe '#notification_levels' do
context "verify enum sequence" do
before do
Expand Down

0 comments on commit 1411eed

Please sign in to comment.