diff --git a/app/assets/javascripts/discourse/components/notifications-button.js.es6 b/app/assets/javascripts/discourse/components/notifications-button.js.es6 index 7c329af4e68..998ce74e30f 100644 --- a/app/assets/javascripts/discourse/components/notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/components/notifications-button.js.es6 @@ -1,73 +1,49 @@ import DropdownButton from 'discourse/components/dropdown-button'; -import NotificationLevels from 'discourse/lib/notification-levels'; +import { all, buttonDetails } from 'discourse/lib/notification-levels'; +import { iconHTML } from 'discourse/helpers/fa-icon'; +import computed from 'ember-addons/ember-computed-decorators'; -const NotificationsButton = DropdownButton.extend({ +export default DropdownButton.extend({ classNames: ['notification-options'], title: '', buttonIncludesText: true, activeItem: Em.computed.alias('notificationLevel'), i18nPrefix: '', i18nPostfix: '', - watchingClasses: 'fa fa-exclamation-circle watching', - trackingClasses: 'fa fa-circle tracking', - mutedClasses: 'fa fa-times-circle muted', - regularClasses: 'fa fa-circle-o regular', - options: function() { - return [['WATCHING', 'watching', this.watchingClasses], - ['TRACKING', 'tracking', this.trackingClasses], - ['REGULAR', 'regular', this.regularClasses], - ['MUTED', 'muted', this.mutedClasses]]; - }.property(), + @computed + dropDownContent() { + const prefix = this.get('i18nPrefix'); + const postfix = this.get('i18nPostfix'); - dropDownContent: function() { - const contents = [], - prefix = this.get('i18nPrefix'), - postfix = this.get('i18nPostfix'); - - _.each(this.get('options'), function(pair) { - if (postfix === '_pm' && pair[1] === 'regular') { return; } - contents.push({ - id: NotificationLevels[pair[0]], - title: I18n.t(prefix + '.' + pair[1] + postfix + '.title'), - description: I18n.t(prefix + '.' + pair[1] + postfix + '.description'), - styleClasses: pair[2] - }); + return all.map(l => { + const start = `${prefix}.${l.key}${postfix}`; + return { + id: l.id, + title: I18n.t(`${start}.title`), + description: I18n.t(`${start}.description`), + styleClasses: `${l.key} fa fa-${l.icon}` + }; }); + }, - return contents; - }.property(), + @computed('notificationLevel') + text(notificationLevel) { + const details = buttonDetails(notificationLevel); + const { key } = details; + const icon = iconHTML(details.icon, { class: key }); - text: function() { - const self = this, - prefix = this.get('i18nPrefix'), - postfix = this.get('i18nPostfix'); - - const key = (function() { - switch (this.get('notificationLevel')) { - case NotificationLevels.WATCHING: return 'watching'; - case NotificationLevels.TRACKING: return 'tracking'; - case NotificationLevels.MUTED: return 'muted'; - default: return 'regular'; - } - }).call(this); - - const icon = (function() { - switch (key) { - case 'watching': return ' '; - case 'tracking': return ' '; - case 'muted': return ' '; - default: return ' '; - } - })(); - return icon + ( this.get('buttonIncludesText') ? I18n.t(prefix + '.' + key + postfix + ".title") : '') + ""; - }.property('notificationLevel'), + if (this.get('buttonIncludesText')) { + const prefix = this.get('i18nPrefix'); + const postfix = this.get('i18nPostfix'); + const text = I18n.t(`${prefix}.${key}${postfix}.title`); + return `${icon} ${text}`; + } else { + return `${icon} `; + } + }, clicked(/* id */) { // sub-class needs to implement this } - }); - -export default NotificationsButton; -export { NotificationLevels }; diff --git a/app/assets/javascripts/discourse/lib/notification-levels.js.es6 b/app/assets/javascripts/discourse/lib/notification-levels.js.es6 index f6830ec7d30..e2173e560b9 100644 --- a/app/assets/javascripts/discourse/lib/notification-levels.js.es6 +++ b/app/assets/javascripts/discourse/lib/notification-levels.js.es6 @@ -1,6 +1,24 @@ -export default { +const NotificationLevels = { WATCHING: 3, TRACKING: 2, REGULAR: 1, MUTED: 0 }; +export default NotificationLevels; + +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' }; + default: + return { id: NotificationLevels.REGULAR, key: 'regular', icon: 'circle-o' }; + } +} +export const all = [ NotificationLevels.WATCHING, + NotificationLevels.TRACKING, + NotificationLevels.MUTED, + NotificationLevels.DEFAULT ].map(buttonDetails); diff --git a/app/assets/javascripts/discourse/widgets/button.js.es6 b/app/assets/javascripts/discourse/widgets/button.js.es6 index e9fd29d70de..8b8ae46dc1d 100644 --- a/app/assets/javascripts/discourse/widgets/button.js.es6 +++ b/app/assets/javascripts/discourse/widgets/button.js.es6 @@ -30,12 +30,11 @@ export default createWidget('button', { html(attrs) { const contents = []; - const left = !attrs.iconRight; - if (attrs.icon && left) { contents.push(iconNode(attrs.icon)); } + if (attrs.icon && left) { contents.push(iconNode(attrs.icon, { class: attrs.iconClass })); } if (attrs.label) { contents.push(I18n.t(attrs.label, attrs.labelOptions)); } if (attrs.contents) { contents.push(attrs.contents); } - if (attrs.icon && !left) { contents.push(iconNode(attrs.icon)); } + if (attrs.icon && !left) { contents.push(iconNode(attrs.icon, { class: attrs.iconClass })); } return contents; }, diff --git a/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 new file mode 100644 index 00000000000..c9bc98642d5 --- /dev/null +++ b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 @@ -0,0 +1,69 @@ +import { createWidget } from 'discourse/widgets/widget'; +import { all, buttonDetails } from 'discourse/lib/notification-levels'; +import { h } from 'virtual-dom'; + +createWidget('notification-option', { + buildKey: attrs => `topic-notifications-button-${attrs.id}`, + tagName: 'li', + + html(attrs) { + return h('a', [ + h('span.icon', { className: `fa fa-${attrs.icon} ${attrs.key}`}), + h('div', [ + h('span.title', I18n.t(`topic.notifications.${attrs.key}.title`)), + h('span', I18n.t(`topic.notifications.${attrs.key}.description`)), + ]) + ]); + }, + + click() { + this.sendWidgetAction('notificationLevelChanged', this.attrs.id); + } +}); + +export default createWidget('topic-notifications-button', { + tagName: 'span.btn-group.notification-options', + buildKey: () => `topic-notifications-button`, + + defaultState() { + return { expanded: false }; + }, + + buildClasses(attrs, state) { + if (state.expanded) { return "open"; } + }, + + buttonFor(level) { + const details = buttonDetails(level); + return this.attach('button', { + className: `btn no-text`, + icon: details.icon, + action: 'toggleDropdown', + iconClass: details.key + }); + }, + + html(attrs, state) { + const result = [ this.buttonFor(attrs.topic.get('details.notification_level')) ]; + if (state.expanded) { + result.push(h('ul.dropdown-menu', all.map(l => this.attach('notification-option', l)))); + } + + return result; + }, + + toggleDropdown() { + this.state.expanded = !this.state.expanded; + }, + + clickOutside() { + if (this.state.expanded) { + this.sendWidgetAction('toggleDropdown'); + } + }, + + notificationLevelChanged(id) { + this.state.expanded = false; + return this.attrs.topic.get('details').updateNotifications(id); + } +}); diff --git a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 index 5d1a4a2e175..9061c60ca25 100644 --- a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 @@ -225,7 +225,10 @@ export default createWidget('topic-timeline', { const { currentUser } = this; if (currentUser && currentUser.get('canManageTopic')) { - controls.push(this.attach('topic-admin-menu-button', { topic })); + if (currentUser.get('canManageTopic')) { + controls.push(this.attach('topic-admin-menu-button', { topic })); + } + controls.push(this.attach('topic-notifications-button', { topic })); } return [ h('div.timeline-controls', controls),