2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-09-06 10:50:21 +08:00

UX: Allow users to remove a remind me topic timer.

This commit is contained in:
Guo Xiang Tan 2017-10-05 11:48:42 +08:00
parent f5a2ed99b0
commit b0557c6692
12 changed files with 213 additions and 108 deletions

View file

@ -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);
}
});

View file

@ -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 ModalFunctionality from 'discourse/mixins/modal-functionality';
import TopicTimer from 'discourse/models/topic-timer'; import TopicTimer from 'discourse/models/topic-timer';
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
export const CLOSE_STATUS_TYPE = 'close'; 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'; export const PUBLISH_TO_CATEGORY_STATUS_TYPE = 'publish_to_category';
const DELETE_STATUS_TYPE = 'delete'; export const DELETE_STATUS_TYPE = 'delete';
const REMINDER_TYPE = 'reminder'; export const REMINDER_TYPE = 'reminder';
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
loading: false, loading: false,
updateTime: null, isPublic: "true",
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'),
@computed("model.closed") @computed("model.closed")
timerTypes(closed) { publicTimerTypes(closed) {
return [ return [
{ id: CLOSE_STATUS_TYPE, name: I18n.t(closed ? 'topic.temp_open.title' : 'topic.auto_close.title'), }, { 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: 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: 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') } { id: REMINDER_TYPE, name: I18n.t('topic.reminder.title') }
]; ];
}, },
@computed('updateTime', 'loading', 'publishToCategory', 'topicTimer.category_id') @computed("isPublic", 'publicTimerTypes', 'privateTimerTypes')
saveDisabled(updateTime, loading, publishToCategory, topicTimerCategoryId) { selections(isPublic, publicTimerTypes, privateTimerTypes) {
return Ember.isEmpty(updateTime) || if (isPublic === 'true') {
loading || return publicTimerTypes;
(publishToCategory && !topicTimerCategoryId); } else {
}, return privateTimerTypes;
@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");
}
} }
},
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) { _setTimer(time, statusType) {
@ -85,10 +69,11 @@ export default Ember.Controller.extend(ModalFunctionality, {
this.set('model.closed', result.closed); this.set('model.closed', result.closed);
} else { } else {
const topicTimer = this.get('isPublic') === 'true' ? 'topic_timer' : 'private_topic_timer';
this.set(`model.${topicTimer}`, Ember.Object.create({}));
this.setProperties({ this.setProperties({
topicTimer: Ember.Object.create({}),
selection: null, selection: null,
updateTime: null
}); });
} }
}).catch(error => { }).catch(error => {
@ -98,11 +83,11 @@ export default Ember.Controller.extend(ModalFunctionality, {
actions: { actions: {
saveTimer() { saveTimer() {
this._setTimer(this.get("updateTime"), this.get('selection')); this._setTimer(this.get("topicTimer.updateTime"), this.get('topicTimer.status_type'));
}, },
removeTimer() { removeTimer() {
this._setTimer(null, this.get('selection')); this._setTimer(null, this.get('topicTimer.status_type'));
} }
} }
}); });

View file

@ -54,6 +54,7 @@ const TopicRoute = Discourse.Route.extend({
showTopicStatusUpdate() { showTopicStatusUpdate() {
const model = this.modelFor('topic'); const model = this.modelFor('topic');
model.set('topic_timer', Ember.Object.create(model.get('topic_timer'))); 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 }); showModal('edit-topic-timer', { model });
this.controllerFor('modal').set('modalClass', 'edit-topic-timer-modal'); this.controllerFor('modal').set('modalClass', 'edit-topic-timer-modal');
}, },

View file

@ -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>

View file

@ -1,54 +1,35 @@
<form> {{#d-modal-body title="topic.topic_status_update.title" autoFocus="false"}}
{{#d-modal-body title="topic.topic_status_update.title" autoFocus="false"}} <div class="radios">
<div class="control-group"> <label for="public-topic-timer">
{{combo-box content=timerTypes value=selection width="50%"}} {{radio-button id='public-topic-timer' name="topic-timer" value="true" selection=isPublic}}
</div> <b>{{i18n 'topic.topic_status_update.public_timer_types'}}</b>
</label>
<div> <label for="private-topic-timer">
{{#if showTimeOnly}} {{radio-button id='private-topic-timer' name="topic-timer" value="false" selection=isPublic}}
{{future-date-input <b>{{i18n 'topic.topic_status_update.private_timer_types'}}</b>
input=updateTime </label>
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}}
</div> </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>

View file

@ -199,12 +199,21 @@
</div> </div>
{{/if}} {{/if}}
{{topic-timer-info {{#if model.private_topic_timer.execute_at}}
statusType=model.topic_timer.status_type {{topic-timer-info
executeAt=model.topic_timer.execute_at statusType=model.private_topic_timer.status_type
basedOnLastPost=model.topic_timer.based_on_last_post executeAt=model.private_topic_timer.execute_at
duration=model.topic_timer.duration duration=model.private_topic_timer.duration}}
categoryId=model.topic_timer.category_id}} {{/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}} {{#if session.showSignupCta}}
{{! replace "Log In to Reply" with the infobox }} {{! replace "Log In to Reply" with the infobox }}

View file

@ -8,8 +8,18 @@
text-align: left; text-align: left;
} }
.radios {
margin-bottom: 10px;
}
label { label {
vertical-align: middle;
display: inline-block; display: inline-block;
padding-right: 5px;
input {
vertical-align: middle;
}
} }
.btn.pull-right { .btn.pull-right {

View file

@ -70,7 +70,7 @@
.topic-status-info { .topic-status-info {
border-top: 1px solid $primary-low; border-top: 1px solid $primary-low;
padding-top: 10px; padding: 10px 0px;
height: 20px; height: 20px;
max-width: 757px; max-width: 757px;
} }

View file

@ -411,6 +411,7 @@ class Topic < ActiveRecord::Base
def reload(options = nil) def reload(options = nil)
@post_numbers = nil @post_numbers = nil
@public_topic_timer = nil @public_topic_timer = nil
@private_topic_timer = nil
super(options) super(options)
end end
@ -1002,6 +1003,10 @@ SQL
@public_topic_timer ||= topic_timers.find_by(deleted_at: nil, public_type: true) @public_topic_timer ||= topic_timers.find_by(deleted_at: nil, public_type: true)
end 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) def delete_topic_timer(status_type, by_user: Discourse.system_user)
options = { status_type: status_type } options = { status_type: status_type }
options.merge!(user: by_user) unless TopicTimer.public_types[status_type] options.merge!(user: by_user) unless TopicTimer.public_types[status_type]

View file

@ -61,6 +61,7 @@ class TopicViewSerializer < ApplicationSerializer
:message_archived, :message_archived,
:tags, :tags,
:topic_timer, :topic_timer,
:private_topic_timer,
:unicode_title, :unicode_title,
:message_bus_last_id, :message_bus_last_id,
:participant_count :participant_count
@ -242,6 +243,15 @@ class TopicViewSerializer < ApplicationSerializer
TopicTimerSerializer.new(object.topic.public_topic_timer, root: false) TopicTimerSerializer.new(object.topic.public_topic_timer, root: false)
end 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 def tags
object.topic.tags.map(&:name) object.topic.tags.map(&:name)
end end

View file

@ -1555,12 +1555,14 @@ en:
deleted: "The topic has been deleted" deleted: "The topic has been deleted"
topic_status_update: topic_status_update:
title: "Set Topic Timer" title: "Topic Timer"
save: "Set Timer" save: "Set Timer"
num_of_hours: "Number of hours:" num_of_hours: "Number of hours:"
remove: "Remove Timer" remove: "Remove Timer"
publish_to: "Publish To:" publish_to: "Publish To:"
when: "When:" when: "When:"
public_timer_types: Topic Timers
private_timer_types: User Topic Timers
auto_update_input: auto_update_input:
none: "" none: ""
later_today: "Later today" later_today: "Later today"

View file

@ -1167,6 +1167,22 @@ describe Topic do
end end
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 describe '#set_or_create_timer' do
let(:topic) { Fabricate.build(:topic) } let(:topic) { Fabricate.build(:topic) }