From 1eb64151f6cbf1bf5545435f7bd0fa48686a4780 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 5 Jul 2016 15:16:32 -0400 Subject: [PATCH] User interface for watching first post --- .../components/notifications-button.js.es6 | 4 +- .../bulk-notification-level.js.es6 | 26 ++++++------- .../discourse/lib/notification-levels.js.es6 | 37 ++++++++++--------- .../mixins/bulk-topic-selection.js.es6 | 2 +- .../widgets/topic-notifications-button.js.es6 | 4 +- app/controllers/email_controller.rb | 6 +-- app/models/category_user.rb | 11 +++++- config/locales/client.en.yml | 9 +++++ spec/controllers/email_controller_spec.rb | 33 +++++++++++++++++ 9 files changed, 91 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/discourse/components/notifications-button.js.es6 b/app/assets/javascripts/discourse/components/notifications-button.js.es6 index 998ce74e3..8a3ffda21 100644 --- a/app/assets/javascripts/discourse/components/notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/components/notifications-button.js.es6 @@ -1,5 +1,5 @@ import DropdownButton from 'discourse/components/dropdown-button'; -import { all, buttonDetails } from 'discourse/lib/notification-levels'; +import { allLevels, buttonDetails } from 'discourse/lib/notification-levels'; import { iconHTML } from 'discourse/helpers/fa-icon'; import computed from 'ember-addons/ember-computed-decorators'; @@ -16,7 +16,7 @@ export default DropdownButton.extend({ const prefix = this.get('i18nPrefix'); const postfix = this.get('i18nPostfix'); - return all.map(l => { + return allLevels.map(l => { const start = `${prefix}.${l.key}${postfix}`; return { id: l.id, diff --git a/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 b/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 index 206e53e5c..d4a3ebbd2 100644 --- a/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 +++ b/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 @@ -1,26 +1,26 @@ -import NotificationLevels from 'discourse/lib/notification-levels'; +import computed from 'ember-addons/ember-computed-decorators'; +import { topicLevels } from 'discourse/lib/notification-levels'; // Support for changing the notification level of various topics -export default Em.Controller.extend({ +export default Ember.Controller.extend({ needs: ['topic-bulk-actions'], notificationLevelId: null, - notificationLevels: function() { - var result = []; - Object.keys(NotificationLevels).forEach(function(k) { - result.push({ - id: NotificationLevels[k].toString(), - name: I18n.t('topic.notifications.' + k.toLowerCase() + ".title"), - description: I18n.t('topic.notifications.' + k.toLowerCase() + ".description") - }); + @computed + notificationLevels() { + return topicLevels.map(level => { + return { + id: level.id.toString(), + name: I18n.t(`topic.notifications.${level.key}.title`), + description: I18n.t(`topic.notifications.${level.key}.description`) + }; }); - return result; - }.property(), + }, disabled: Em.computed.empty("notificationLevelId"), actions: { - changeNotificationLevel: function() { + changeNotificationLevel() { this.get('controllers.topic-bulk-actions').performAndRefresh({ type: 'change_notification_level', notification_level_id: this.get('notificationLevelId') diff --git a/app/assets/javascripts/discourse/lib/notification-levels.js.es6 b/app/assets/javascripts/discourse/lib/notification-levels.js.es6 index e2173e560..d27981974 100644 --- a/app/assets/javascripts/discourse/lib/notification-levels.js.es6 +++ b/app/assets/javascripts/discourse/lib/notification-levels.js.es6 @@ -1,24 +1,25 @@ -const NotificationLevels = { - WATCHING: 3, - TRACKING: 2, - REGULAR: 1, - MUTED: 0 -}; -export default NotificationLevels; +const MUTED = 0; +const REGULAR = 1; +const TRACKING = 2; +const WATCHING = 3; +const WATCHING_FIRST_POST = 4; + +export const NotificationLevels = { WATCHING_FIRST_POST, WATCHING, TRACKING, REGULAR, MUTED }; export function buttonDetails(level) { switch(level) { - case NotificationLevels.WATCHING: - return { id: NotificationLevels.WATCHING, key: 'watching', icon: 'exclamation-circle' }; - case NotificationLevels.TRACKING: - return { id: NotificationLevels.TRACKING, key: 'tracking', icon: 'circle' }; - case NotificationLevels.MUTED: - return { id: NotificationLevels.MUTED, key: 'muted', icon: 'times-circle' }; + case WATCHING_FIRST_POST: + return { id: WATCHING_FIRST_POST, key: 'watching_first_post', icon: 'dot-circle-o' }; + case WATCHING: + return { id: WATCHING, key: 'watching', icon: 'exclamation-circle' }; + case TRACKING: + return { id: TRACKING, key: 'tracking', icon: 'circle' }; + case MUTED: + return { id: MUTED, key: 'muted', icon: 'times-circle' }; default: - return { id: NotificationLevels.REGULAR, key: 'regular', icon: 'circle-o' }; + return { id: REGULAR, key: 'regular', icon: 'circle-o' }; } } -export const all = [ NotificationLevels.WATCHING, - NotificationLevels.TRACKING, - NotificationLevels.MUTED, - NotificationLevels.DEFAULT ].map(buttonDetails); + +export const allLevels = [ WATCHING, TRACKING, WATCHING_FIRST_POST, MUTED, REGULAR ].map(buttonDetails); +export const topicLevels = allLevels.filter(l => l.id !== WATCHING_FIRST_POST); diff --git a/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6 b/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6 index 705f281a3..cf8878c43 100644 --- a/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6 +++ b/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6 @@ -25,7 +25,7 @@ export default Ember.Mixin.create({ operation = { type: 'dismiss_posts' }; } else { operation = { type: 'change_notification_level', - notification_level_id: NotificationLevels.REGULAR }; + notification_level_id: NotificationLevels.REGULAR }; } let promise; diff --git a/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 index de8e538ee..b04e6555c 100644 --- a/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 @@ -1,5 +1,5 @@ import { createWidget } from 'discourse/widgets/widget'; -import { all, buttonDetails } from 'discourse/lib/notification-levels'; +import { topicLevels, buttonDetails } from 'discourse/lib/notification-levels'; import { h } from 'virtual-dom'; import RawHTML from 'discourse/widgets/raw-html'; @@ -63,7 +63,7 @@ export default createWidget('topic-notifications-button', { const result = [ this.buttonFor(details.get('notification_level')) ]; if (state.expanded) { - result.push(h('ul.dropdown-menu', all.map(l => this.attach('notification-option', l)))); + result.push(h('ul.dropdown-menu', topicLevels.map(l => this.attach('notification-option', l)))); } if (attrs.appendReason) { diff --git a/app/controllers/email_controller.rb b/app/controllers/email_controller.rb index 37b169a23..335afc4c6 100644 --- a/app/controllers/email_controller.rb +++ b/app/controllers/email_controller.rb @@ -29,8 +29,8 @@ class EmailController < ApplicationController @watched_count = nil if @topic && @topic.category_id if CategoryUser.exists?(user_id: @user.id, - notification_level: CategoryUser.notification_levels[:watching], - category_id: @topic.category_id) + notification_level: CategoryUser.watching_levels, + category_id: @topic.category_id) @watched_count = TopicUser.joins(:topic) .where(:user => @user, :notification_level => TopicUser.notification_levels[:watching], @@ -74,7 +74,7 @@ class EmailController < ApplicationController CategoryUser.where(user_id: user.id, category_id: topic.category_id, - notification_level: CategoryUser.notification_levels[:watching] + notification_level: CategoryUser.watching_levels ) .destroy_all updated = true diff --git a/app/models/category_user.rb b/app/models/category_user.rb index 7f0ab17a8..44d555500 100644 --- a/app/models/category_user.rb +++ b/app/models/category_user.rb @@ -10,9 +10,16 @@ class CategoryUser < ActiveRecord::Base self.where(user: user, category: category) end - # same for now def self.notification_levels - TopicUser.notification_levels + @notification_levels ||= Enum.new(muted: 0, + regular: 1, + tracking: 2, + watching: 3, + watching_first_post: 4) + end + + def self.watching_levels + [notification_levels[:watching], notification_levels[:watching_first_post]] end %w{watch track}.each do |s| diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index f817e3b7e..911cd8c02 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -408,6 +408,9 @@ en: watching: title: "Watching" description: "You will be notified of every new post in every message, and a count of new replies will be shown." + watching_first_post: + title: "Watching First Post Only" + description: "You will only be notified of the first post in each new topic in this group." tracking: title: "Tracking" description: "You will be notified if someone mentions your @name or replies to you, and a count of new replies will be shown." @@ -1811,6 +1814,9 @@ en: watching: title: "Watching" description: "You will automatically watch all new topics in these categories. You will be notified of every new post in every topic, and a count of new replies will be shown." + watching_first_post: + title: "Watching First Post Only" + description: "You will only be notified of the first post in each new topic in these categories." tracking: title: "Tracking" description: "You will automatically track all new topics in these categories. You will be notified if someone mentions your @name or replies to you, and a count of new replies will be shown." @@ -2132,6 +2138,9 @@ en: watching: title: "Watching" description: "You will automatically watch all new topics in this tag. You will be notified of all new posts and topics, plus the count of unread and new posts will also appear next to the topic." + watching_first_post: + title: "Watching First Post Only" + description: "You will only be notified of the first post in each new topic in this tag." tracking: title: "Tracking" description: "You will automatically track all new topics in this tag. A count of unread and new posts will appear next to the topic." diff --git a/spec/controllers/email_controller_spec.rb b/spec/controllers/email_controller_spec.rb index fe6c88248..03c0df3c3 100644 --- a/spec/controllers/email_controller_spec.rb +++ b/spec/controllers/email_controller_spec.rb @@ -109,6 +109,20 @@ describe EmailController do expect(CategoryUser.find_by(id: cu.id)).to eq(nil) end + + it 'can unwatch first post from category' do + p = Fabricate(:post) + key = UnsubscribeKey.create_key_for(p.user, p) + + cu = CategoryUser.create!(user_id: p.user.id, + category_id: p.topic.category_id, + notification_level: CategoryUser.notification_levels[:watching_first_post]) + + post :perform_unsubscribe, key: key, unwatch_category: "1" + expect(response.status).to eq(302) + + expect(CategoryUser.find_by(id: cu.id)).to eq(nil) + end end context '.unsubscribe' do @@ -197,6 +211,25 @@ describe EmailController do expect(response.body).not_to include("unwatch_category") end + + it 'correctly handles watched first post categories' do + post = Fabricate(:post) + user = post.user + cu = CategoryUser.create!(user_id: user.id, + category_id: post.topic.category_id, + notification_level: CategoryUser.notification_levels[:watching_first_post]) + + + key = UnsubscribeKey.create_key_for(user, post) + get :unsubscribe, key: key + expect(response.body).to include("unwatch_category") + + cu.destroy! + + get :unsubscribe, key: key + expect(response.body).not_to include("unwatch_category") + + end end