From b0557c6692b6e88878bbba965d08bf2985514350 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan <tgx_world@hotmail.com> Date: Thu, 5 Oct 2017 11:48:42 +0800 Subject: [PATCH] UX: Allow users to remove a remind me topic timer. --- .../components/edit-topic-timer-form.js.es6 | 50 +++++++++++ .../controllers/edit-topic-timer.js.es6 | 79 +++++++---------- .../javascripts/discourse/routes/topic.js.es6 | 1 + .../components/edit-topic-timer-form.hbs | 36 ++++++++ .../templates/modal/edit-topic-timer.hbs | 85 +++++++------------ .../javascripts/discourse/templates/topic.hbs | 21 +++-- .../base/edit-topic-status-update-modal.scss | 10 +++ app/assets/stylesheets/desktop/topic.scss | 4 +- app/models/topic.rb | 5 ++ app/serializers/topic_view_serializer.rb | 10 +++ config/locales/client.en.yml | 4 +- spec/models/topic_spec.rb | 16 ++++ 12 files changed, 213 insertions(+), 108 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/edit-topic-timer-form.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/edit-topic-timer-form.hbs diff --git a/app/assets/javascripts/discourse/components/edit-topic-timer-form.js.es6 b/app/assets/javascripts/discourse/components/edit-topic-timer-form.js.es6 new file mode 100644 index 00000000000..421972b834c --- /dev/null +++ b/app/assets/javascripts/discourse/components/edit-topic-timer-form.js.es6 @@ -0,0 +1,50 @@ +import { default as computed, observes, on } from "ember-addons/ember-computed-decorators"; + +import { + PUBLISH_TO_CATEGORY_STATUS_TYPE, + OPEN_STATUS_TYPE, + DELETE_STATUS_TYPE, + REMINDER_TYPE, + CLOSE_STATUS_TYPE +} from 'discourse/controllers/edit-topic-timer'; + +export default Ember.Component.extend({ + selection: Ember.computed.alias('topicTimer.status_type'), + autoOpen: Ember.computed.equal('selection', OPEN_STATUS_TYPE), + autoClose: Ember.computed.equal('selection', CLOSE_STATUS_TYPE), + autoDelete: Ember.computed.equal('selection', DELETE_STATUS_TYPE), + publishToCategory: Ember.computed.equal('selection', PUBLISH_TO_CATEGORY_STATUS_TYPE), + reminder: Ember.computed.equal('selection', REMINDER_TYPE), + showTimeOnly: Ember.computed.or('autoOpen', 'autoDelete', 'reminder'), + + @computed('topicTimer.updateTime', 'loading', 'publishToCategory', 'topicTimer.category_id') + saveDisabled(updateTime, loading, publishToCategory, topicTimerCategoryId) { + return Ember.isEmpty(updateTime) || + loading || + (publishToCategory && !topicTimerCategoryId); + }, + + @computed("topic.visible") + excludeCategoryId(visible) { + if (visible) return this.get('topic.category_id'); + }, + + @on('init') + @observes("topicTimer", "topicTimer.execute_at", "topicTimer.duration") + _setUpdateTime() { + let time = null; + const executeAt = this.get('topicTimer.execute_at'); + + if (executeAt && this.get("topicTimer.based_on_last_post")) { + time = this.get("topicTimer.duration"); + } else if (executeAt) { + const closeTime = moment(executeAt); + + if (closeTime > moment()) { + time = closeTime.format("YYYY-MM-DD HH:mm"); + } + } + + this.set("topicTimer.updateTime", time); + } +}); diff --git a/app/assets/javascripts/discourse/controllers/edit-topic-timer.js.es6 b/app/assets/javascripts/discourse/controllers/edit-topic-timer.js.es6 index fdb66c1a65f..e1a122a37a7 100644 --- a/app/assets/javascripts/discourse/controllers/edit-topic-timer.js.es6 +++ b/app/assets/javascripts/discourse/controllers/edit-topic-timer.js.es6 @@ -1,67 +1,51 @@ -import { default as computed, observes } from "ember-addons/ember-computed-decorators"; +import { default as computed } from "ember-addons/ember-computed-decorators"; import ModalFunctionality from 'discourse/mixins/modal-functionality'; import TopicTimer from 'discourse/models/topic-timer'; import { popupAjaxError } from 'discourse/lib/ajax-error'; export const CLOSE_STATUS_TYPE = 'close'; -const OPEN_STATUS_TYPE = 'open'; +export const OPEN_STATUS_TYPE = 'open'; export const PUBLISH_TO_CATEGORY_STATUS_TYPE = 'publish_to_category'; -const DELETE_STATUS_TYPE = 'delete'; -const REMINDER_TYPE = 'reminder'; +export const DELETE_STATUS_TYPE = 'delete'; +export const REMINDER_TYPE = 'reminder'; export default Ember.Controller.extend(ModalFunctionality, { loading: false, - updateTime: null, - topicTimer: Ember.computed.alias("model.topic_timer"), - selection: Ember.computed.alias('model.topic_timer.status_type'), - autoOpen: Ember.computed.equal('selection', OPEN_STATUS_TYPE), - autoClose: Ember.computed.equal('selection', CLOSE_STATUS_TYPE), - autoDelete: Ember.computed.equal('selection', DELETE_STATUS_TYPE), - publishToCategory: Ember.computed.equal('selection', PUBLISH_TO_CATEGORY_STATUS_TYPE), - reminder: Ember.computed.equal('selection', REMINDER_TYPE), - - showTimeOnly: Ember.computed.or('autoOpen', 'autoDelete', 'reminder'), + isPublic: "true", @computed("model.closed") - timerTypes(closed) { + publicTimerTypes(closed) { return [ { id: CLOSE_STATUS_TYPE, name: I18n.t(closed ? 'topic.temp_open.title' : 'topic.auto_close.title'), }, { id: OPEN_STATUS_TYPE, name: I18n.t(closed ? 'topic.auto_reopen.title' : 'topic.temp_close.title') }, { id: PUBLISH_TO_CATEGORY_STATUS_TYPE, name: I18n.t('topic.publish_to_category.title') }, - { id: DELETE_STATUS_TYPE, name: I18n.t('topic.auto_delete.title') }, + { id: DELETE_STATUS_TYPE, name: I18n.t('topic.auto_delete.title') } + ]; + }, + + @computed() + privateTimerTypes() { + return [ { id: REMINDER_TYPE, name: I18n.t('topic.reminder.title') } ]; }, - @computed('updateTime', 'loading', 'publishToCategory', 'topicTimer.category_id') - saveDisabled(updateTime, loading, publishToCategory, topicTimerCategoryId) { - return Ember.isEmpty(updateTime) || - loading || - (publishToCategory && !topicTimerCategoryId); - }, - - @computed("model.visible") - excludeCategoryId(visible) { - if (visible) return this.get('model.category_id'); - }, - - @observes("topicTimer.execute_at", "topicTimer.duration") - _setUpdateTime() { - if (!this.get('topicTimer.execute_at')) return; - - let time = null; - - if (this.get("topicTimer.based_on_last_post")) { - time = this.get("topicTimer.duration"); - } else if (this.get("topicTimer.execute_at")) { - const closeTime = moment(this.get('topicTimer.execute_at')); - - if (closeTime > moment()) { - time = closeTime.format("YYYY-MM-DD HH:mm"); - } + @computed("isPublic", 'publicTimerTypes', 'privateTimerTypes') + selections(isPublic, publicTimerTypes, privateTimerTypes) { + if (isPublic === 'true') { + return publicTimerTypes; + } else { + return privateTimerTypes; } + }, - this.set("updateTime", time); + @computed('isPublic', 'model.topic_timer', 'model.private_topic_timer') + topicTimer(isPublic, publicTopicTimer, privateTopicTimer) { + if (isPublic === 'true') { + return publicTopicTimer; + } else { + return privateTopicTimer; + } }, _setTimer(time, statusType) { @@ -85,10 +69,11 @@ export default Ember.Controller.extend(ModalFunctionality, { this.set('model.closed', result.closed); } else { + const topicTimer = this.get('isPublic') === 'true' ? 'topic_timer' : 'private_topic_timer'; + this.set(`model.${topicTimer}`, Ember.Object.create({})); + this.setProperties({ - topicTimer: Ember.Object.create({}), selection: null, - updateTime: null }); } }).catch(error => { @@ -98,11 +83,11 @@ export default Ember.Controller.extend(ModalFunctionality, { actions: { saveTimer() { - this._setTimer(this.get("updateTime"), this.get('selection')); + this._setTimer(this.get("topicTimer.updateTime"), this.get('topicTimer.status_type')); }, removeTimer() { - this._setTimer(null, this.get('selection')); + this._setTimer(null, this.get('topicTimer.status_type')); } } }); diff --git a/app/assets/javascripts/discourse/routes/topic.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6 index 4e17be6c6b3..50867225346 100644 --- a/app/assets/javascripts/discourse/routes/topic.js.es6 +++ b/app/assets/javascripts/discourse/routes/topic.js.es6 @@ -54,6 +54,7 @@ const TopicRoute = Discourse.Route.extend({ showTopicStatusUpdate() { const model = this.modelFor('topic'); model.set('topic_timer', Ember.Object.create(model.get('topic_timer'))); + model.set('private_topic_timer', Ember.Object.create(model.get('private_topic_timer'))); showModal('edit-topic-timer', { model }); this.controllerFor('modal').set('modalClass', 'edit-topic-timer-modal'); }, diff --git a/app/assets/javascripts/discourse/templates/components/edit-topic-timer-form.hbs b/app/assets/javascripts/discourse/templates/components/edit-topic-timer-form.hbs new file mode 100644 index 00000000000..e2310800812 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/edit-topic-timer-form.hbs @@ -0,0 +1,36 @@ +<form> + <div class="control-group"> + {{combo-box content=timerTypes value=selection width="50%"}} + </div> + + <div> + {{#if showTimeOnly}} + {{future-date-input + input=topicTimer.updateTime + statusType=selection + includeWeekend=true + basedOnLastPost=false}} + {{else if publishToCategory}} + <div class="control-group"> + <label>{{i18n 'topic.topic_status_update.publish_to'}}</label> + {{category-select-box + value=topicTimer.category_id + excludeCategoryId=excludeCategoryId}} + </div> + + {{future-date-input + input=topicTimer.updateTime + statusType=selection + includeWeekend=true + categoryId=topicTimer.category_id + basedOnLastPost=false}} + {{else if autoClose}} + {{future-date-input + input=topicTimer.updateTime + statusType=selection + includeWeekend=true + basedOnLastPost=topicTimer.based_on_last_post + lastPostedAt=model.last_posted_at}} + {{/if}} + </div> +</form> diff --git a/app/assets/javascripts/discourse/templates/modal/edit-topic-timer.hbs b/app/assets/javascripts/discourse/templates/modal/edit-topic-timer.hbs index dfe6826bdba..4de56698a39 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit-topic-timer.hbs +++ b/app/assets/javascripts/discourse/templates/modal/edit-topic-timer.hbs @@ -1,54 +1,35 @@ -<form> - {{#d-modal-body title="topic.topic_status_update.title" autoFocus="false"}} - <div class="control-group"> - {{combo-box content=timerTypes value=selection width="50%"}} - </div> +{{#d-modal-body title="topic.topic_status_update.title" autoFocus="false"}} + <div class="radios"> + <label for="public-topic-timer"> + {{radio-button id='public-topic-timer' name="topic-timer" value="true" selection=isPublic}} + <b>{{i18n 'topic.topic_status_update.public_timer_types'}}</b> + </label> - <div> - {{#if showTimeOnly}} - {{future-date-input - input=updateTime - statusType=selection - includeWeekend=true - basedOnLastPost=false}} - {{else if publishToCategory}} - <div class="control-group"> - <label>{{i18n 'topic.topic_status_update.publish_to'}}</label> - {{category-select-box - value=topicTimer.category_id - excludeCategoryId=excludeCategoryId}} - </div> - - {{future-date-input - input=updateTime - statusType=selection - includeWeekend=true - categoryId=topicTimer.category_id - basedOnLastPost=false}} - {{else if autoClose}} - {{future-date-input - input=updateTime - statusType=selection - includeWeekend=true - basedOnLastPost=topicTimer.based_on_last_post - lastPostedAt=model.last_posted_at}} - {{/if}} - </div> - {{/d-modal-body}} - - <div class="modal-footer"> - {{d-button class="btn-primary" - disabled=saveDisabled - label="topic.topic_status_update.save" - action="saveTimer"}} - - {{d-modal-cancel close=(action "closeModal")}} - {{conditional-loading-spinner size="small" condition=loading}} - - {{#if topicTimer.execute_at}} - {{d-button class="pull-right btn-danger" - action="removeTimer" - label='topic.topic_status_update.remove'}} - {{/if}} + <label for="private-topic-timer"> + {{radio-button id='private-topic-timer' name="topic-timer" value="false" selection=isPublic}} + <b>{{i18n 'topic.topic_status_update.private_timer_types'}}</b> + </label> </div> -</form> + + {{edit-topic-timer-form + topic=model + topicTimer=topicTimer + timerTypes=selections + updateTime=updateTime + closeModal="closeModal"}} +{{/d-modal-body}} + +<div class="modal-footer"> + {{d-button class="btn-primary" + disabled=saveDisabled + label="topic.topic_status_update.save" + action="saveTimer"}} + + {{conditional-loading-spinner size="small" condition=loading}} + + {{#if topicTimer.execute_at}} + {{d-button class="pull-right btn-danger" + action="removeTimer" + label='topic.topic_status_update.remove'}} + {{/if}} +</div> diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index 4f0fcddacdc..8c9eed6e505 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -199,12 +199,21 @@ </div> {{/if}} - {{topic-timer-info - statusType=model.topic_timer.status_type - executeAt=model.topic_timer.execute_at - basedOnLastPost=model.topic_timer.based_on_last_post - duration=model.topic_timer.duration - categoryId=model.topic_timer.category_id}} + {{#if model.private_topic_timer.execute_at}} + {{topic-timer-info + statusType=model.private_topic_timer.status_type + executeAt=model.private_topic_timer.execute_at + duration=model.private_topic_timer.duration}} + {{/if}} + + {{#if model.topic_timer.execute_at}} + {{topic-timer-info + statusType=model.topic_timer.status_type + executeAt=model.topic_timer.execute_at + basedOnLastPost=model.topic_timer.based_on_last_post + duration=model.topic_timer.duration + categoryId=model.topic_timer.category_id}} + {{/if}} {{#if session.showSignupCta}} {{! replace "Log In to Reply" with the infobox }} diff --git a/app/assets/stylesheets/common/base/edit-topic-status-update-modal.scss b/app/assets/stylesheets/common/base/edit-topic-status-update-modal.scss index 1c17981d7be..3a9424ca478 100644 --- a/app/assets/stylesheets/common/base/edit-topic-status-update-modal.scss +++ b/app/assets/stylesheets/common/base/edit-topic-status-update-modal.scss @@ -8,8 +8,18 @@ text-align: left; } + .radios { + margin-bottom: 10px; + } + label { + vertical-align: middle; display: inline-block; + padding-right: 5px; + + input { + vertical-align: middle; + } } .btn.pull-right { diff --git a/app/assets/stylesheets/desktop/topic.scss b/app/assets/stylesheets/desktop/topic.scss index 8448400be78..98f7ec65fd3 100644 --- a/app/assets/stylesheets/desktop/topic.scss +++ b/app/assets/stylesheets/desktop/topic.scss @@ -69,8 +69,8 @@ } .topic-status-info { - border-top: 1px solid $primary-low; - padding-top: 10px; + border-top: 1px solid $primary-low; + padding: 10px 0px; height: 20px; max-width: 757px; } diff --git a/app/models/topic.rb b/app/models/topic.rb index 161d5ba3615..59822cd2562 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -411,6 +411,7 @@ class Topic < ActiveRecord::Base def reload(options = nil) @post_numbers = nil @public_topic_timer = nil + @private_topic_timer = nil super(options) end @@ -1002,6 +1003,10 @@ SQL @public_topic_timer ||= topic_timers.find_by(deleted_at: nil, public_type: true) end + def private_topic_timer(user) + @private_topic_Timer ||= topic_timers.find_by(deleted_at: nil, public_type: false, user_id: user.id) + end + def delete_topic_timer(status_type, by_user: Discourse.system_user) options = { status_type: status_type } options.merge!(user: by_user) unless TopicTimer.public_types[status_type] diff --git a/app/serializers/topic_view_serializer.rb b/app/serializers/topic_view_serializer.rb index c5ccc917894..df70f3592bd 100644 --- a/app/serializers/topic_view_serializer.rb +++ b/app/serializers/topic_view_serializer.rb @@ -61,6 +61,7 @@ class TopicViewSerializer < ApplicationSerializer :message_archived, :tags, :topic_timer, + :private_topic_timer, :unicode_title, :message_bus_last_id, :participant_count @@ -242,6 +243,15 @@ class TopicViewSerializer < ApplicationSerializer TopicTimerSerializer.new(object.topic.public_topic_timer, root: false) end + def include_private_topic_timer? + scope.user + end + + def private_topic_timer + timer = object.topic.private_topic_timer(scope.user) + TopicTimerSerializer.new(timer, root: false) + end + def tags object.topic.tags.map(&:name) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ecc0e9f5e4f..28f3c24433f 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1555,12 +1555,14 @@ en: deleted: "The topic has been deleted" topic_status_update: - title: "Set Topic Timer" + title: "Topic Timer" save: "Set Timer" num_of_hours: "Number of hours:" remove: "Remove Timer" publish_to: "Publish To:" when: "When:" + public_timer_types: Topic Timers + private_timer_types: User Topic Timers auto_update_input: none: "" later_today: "Later today" diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index ce3ece7a4f4..706b2ee4970 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -1167,6 +1167,22 @@ describe Topic do end end + describe '#private_topic_timer' do + let(:user) { Fabricate(:user) } + + let(:topic_timer) do + Fabricate(:topic_timer, + public_type: false, + user: user, + status_type: TopicTimer.private_types[:reminder] + ) + end + + it 'should return the right record' do + expect(topic_timer.topic.private_topic_timer(user)).to eq(topic_timer) + end + end + describe '#set_or_create_timer' do let(:topic) { Fabricate.build(:topic) }