UX: Improve the way users set timers for `TopicStatusUpdate`.
This commit is contained in:
parent
0f2e2ea175
commit
5c39c8f24b
|
@ -11,12 +11,14 @@ export default Ember.Component.extend(bufferedRender({
|
|||
buildBuffer(buffer) {
|
||||
const nameProperty = this.get('nameProperty');
|
||||
const none = this.get('none');
|
||||
let noneValue = null;
|
||||
|
||||
// Add none option if required
|
||||
if (typeof none === "string") {
|
||||
buffer.push('<option value="">' + I18n.t(none) + "</option>");
|
||||
} else if (typeof none === "object") {
|
||||
buffer.push("<option value=\"\">" + Em.get(none, nameProperty) + "</option>");
|
||||
noneValue = Em.get(none, this.get('valueAttribute'));
|
||||
buffer.push(`<option value="${noneValue}">${Em.get(none, nameProperty)}</option>`);
|
||||
}
|
||||
|
||||
let selected = this.get('value');
|
||||
|
@ -47,7 +49,7 @@ export default Ember.Component.extend(bufferedRender({
|
|||
});
|
||||
}
|
||||
|
||||
if (!selectedFound) {
|
||||
if (!selectedFound && !noneValue) {
|
||||
if (none) {
|
||||
this.set('value', null);
|
||||
} else {
|
||||
|
@ -89,7 +91,8 @@ export default Ember.Component.extend(bufferedRender({
|
|||
|
||||
const $elem = this.$();
|
||||
const caps = this.capabilities;
|
||||
const minimumResultsForSearch = (caps && caps.isIOS) ? -1 : 5;
|
||||
const minimumResultsForSearch = this.get('minimumResultsForSearch') || ((caps && caps.isIOS) ? -1 : 5);
|
||||
|
||||
if (!this.get("selectionTemplate") && this.get("selectionIcon")) {
|
||||
this.selectionTemplate = (item) => {
|
||||
let name = Em.get(item, 'text');
|
||||
|
@ -97,13 +100,22 @@ export default Ember.Component.extend(bufferedRender({
|
|||
return `<i class='fa fa-${this.get("selectionIcon")}'></i>${name}`;
|
||||
};
|
||||
}
|
||||
$elem.select2({
|
||||
formatResult: this.comboTemplate,
|
||||
formatSelection: this.selectionTemplate,
|
||||
|
||||
const options = {
|
||||
minimumResultsForSearch,
|
||||
width: this.get('width') || 'resolve',
|
||||
allowClear: true
|
||||
});
|
||||
};
|
||||
|
||||
if (this.comboTemplate) {
|
||||
options.formatResult = this.comboTemplate.bind(this);
|
||||
}
|
||||
|
||||
if (this.selectionTemplate) {
|
||||
options.formatSelection = this.selectionTemplate.bind(this);
|
||||
}
|
||||
|
||||
$elem.select2(options);
|
||||
|
||||
const castInteger = this.get('castInteger');
|
||||
$elem.on("change", e => {
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import Combobox from 'discourse-common/components/combo-box';
|
||||
import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-status-update';
|
||||
|
||||
const LATER_TODAY = 'later_today';
|
||||
const TOMORROW = 'tomorrow';
|
||||
const LATER_THIS_WEEK = 'later_this_week';
|
||||
const THIS_WEEKEND = 'this_weekend';
|
||||
const NEXT_WEEK = 'next_week';
|
||||
export const PICK_DATE_AND_TIME = 'pick_date_and_time';
|
||||
export const SET_BASED_ON_LAST_POST = 'set_based_on_last_post';
|
||||
|
||||
export const FORMAT = 'YYYY-MM-DD HH:mm';
|
||||
|
||||
export default Combobox.extend({
|
||||
classNames: ['auto-update-input-selector'],
|
||||
isCustom: Ember.computed.equal("value", PICK_DATE_AND_TIME),
|
||||
|
||||
@computed()
|
||||
content() {
|
||||
const selections = [];
|
||||
const now = moment();
|
||||
const canScheduleToday = (24 - now.hour()) > 6;
|
||||
const day = now.day();
|
||||
|
||||
if (canScheduleToday) {
|
||||
selections.push({
|
||||
id: LATER_TODAY,
|
||||
name: I18n.t('topic.auto_update_input.later_today')
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: TOMORROW,
|
||||
name: I18n.t('topic.auto_update_input.tomorrow')
|
||||
});
|
||||
|
||||
if (!canScheduleToday && day < 4) {
|
||||
selections.push({
|
||||
id: LATER_THIS_WEEK,
|
||||
name: I18n.t('topic.auto_update_input.later_this_week')
|
||||
});
|
||||
}
|
||||
|
||||
if (day < 5) {
|
||||
selections.push({
|
||||
id: THIS_WEEKEND,
|
||||
name: I18n.t('topic.auto_update_input.this_weekend')
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (day !== 7) {
|
||||
selections.push({
|
||||
id: NEXT_WEEK,
|
||||
name: I18n.t('topic.auto_update_input.next_week')
|
||||
});
|
||||
}
|
||||
|
||||
selections.push({
|
||||
id: PICK_DATE_AND_TIME,
|
||||
name: I18n.t('topic.auto_update_input.pick_date_and_time')
|
||||
});
|
||||
|
||||
if (this.get('statusType') === CLOSE_STATUS_TYPE) {
|
||||
selections.push({
|
||||
id: SET_BASED_ON_LAST_POST,
|
||||
name: I18n.t('topic.auto_update_input.set_based_on_last_post')
|
||||
});
|
||||
}
|
||||
|
||||
return selections;
|
||||
},
|
||||
|
||||
@observes('value')
|
||||
_updateInput() {
|
||||
if (this.get('isCustom')) return;
|
||||
let input = null;
|
||||
const { time } = this.get('updateAt');
|
||||
|
||||
if (time && !Ember.isEmpty(this.get('value'))) {
|
||||
input = time.format(FORMAT);
|
||||
}
|
||||
|
||||
this.set('input', input);
|
||||
},
|
||||
|
||||
@computed('value')
|
||||
updateAt(value) {
|
||||
return this._updateAt(value);
|
||||
},
|
||||
|
||||
comboTemplate(state) {
|
||||
return this._format(state);
|
||||
},
|
||||
|
||||
selectionTemplate(state) {
|
||||
return this._format(state);
|
||||
},
|
||||
|
||||
_format(state) {
|
||||
let { time, icon } = this._updateAt(state.id);
|
||||
let icons;
|
||||
|
||||
if (icon) {
|
||||
icons = icon.split(',').map(i => {
|
||||
return `<i class='fa fa-${i}'/>`;
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
if (time) {
|
||||
if (state.id === LATER_TODAY) {
|
||||
time = time.format('hh:mm a');
|
||||
} else {
|
||||
time = time.format('ddd, hh:mm a');
|
||||
}
|
||||
}
|
||||
|
||||
let output = "";
|
||||
|
||||
if (!Ember.isEmpty(icons)) {
|
||||
output += `<span class='auto-update-input-selector-icons'>${icons}</span>`;
|
||||
}
|
||||
|
||||
output += `<span>${state.text}</span>`;
|
||||
|
||||
if (time) {
|
||||
output += `<span class='auto-update-input-selector-datetime'>${time}</span>`;
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
_updateAt(selection) {
|
||||
let time = moment();
|
||||
let icon;
|
||||
const timeOfDay = this.get('statusType') !== CLOSE_STATUS_TYPE ? 8 : 18;
|
||||
|
||||
switch(selection) {
|
||||
case LATER_TODAY:
|
||||
time = time.hour(18).minute(0);
|
||||
icon = 'desktop';
|
||||
break;
|
||||
case TOMORROW:
|
||||
time = time.add(1, 'day').hour(timeOfDay).minute(0);
|
||||
icon = 'sun-o';
|
||||
break;
|
||||
case LATER_THIS_WEEK:
|
||||
time = time.add(2, 'day').hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case THIS_WEEKEND:
|
||||
time = time.day(6).hour(timeOfDay).minute(0);
|
||||
icon = 'bed';
|
||||
break;
|
||||
case NEXT_WEEK:
|
||||
time = time.add(1, 'week').day(1).hour(timeOfDay).minute(0);
|
||||
icon = 'briefcase';
|
||||
break;
|
||||
case PICK_DATE_AND_TIME:
|
||||
time = null;
|
||||
icon = 'calendar-plus-o';
|
||||
break;
|
||||
case SET_BASED_ON_LAST_POST:
|
||||
time = null;
|
||||
icon = 'clock-o';
|
||||
break;
|
||||
}
|
||||
|
||||
return { time, icon };
|
||||
},
|
||||
});
|
|
@ -1,47 +1,92 @@
|
|||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import {
|
||||
FORMAT,
|
||||
PICK_DATE_AND_TIME,
|
||||
SET_BASED_ON_LAST_POST
|
||||
} from "discourse/components/auto-update-input-selector";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
limited: false,
|
||||
selection: null,
|
||||
date: null,
|
||||
time: null,
|
||||
isCustom: Ember.computed.equal('selection', PICK_DATE_AND_TIME),
|
||||
isBasedOnLastPost: Ember.computed.equal('selection', SET_BASED_ON_LAST_POST),
|
||||
|
||||
didInsertElement() {
|
||||
init() {
|
||||
this._super();
|
||||
this._updateInputValid();
|
||||
},
|
||||
|
||||
@computed("limited")
|
||||
inputUnitsKey(limited) {
|
||||
return limited ? "topic.auto_update_input.limited.units" : "topic.auto_update_input.all.units";
|
||||
},
|
||||
const input = this.get('input');
|
||||
|
||||
@computed("limited")
|
||||
inputExamplesKey(limited) {
|
||||
return limited ? "topic.auto_update_input.limited.examples" : "topic.auto_update_input.all.examples";
|
||||
},
|
||||
|
||||
@observes("input", "limited")
|
||||
_updateInputValid() {
|
||||
this.set(
|
||||
"inputValid", this._isInputValid(this.get("input"), this.get("limited"))
|
||||
);
|
||||
},
|
||||
|
||||
_isInputValid(input, limited) {
|
||||
const t = (input || "").toString().trim();
|
||||
|
||||
if (t.length === 0) {
|
||||
return true;
|
||||
// "empty" is always valid
|
||||
} else if (limited) {
|
||||
// only # of hours in limited mode
|
||||
return t.match(/^(\d+\.)?\d+$/);
|
||||
} else {
|
||||
if (t.match(/^\d{4}-\d{1,2}-\d{1,2}(?: \d{1,2}:\d{2}(\s?[AP]M)?){0,1}$/i)) {
|
||||
// timestamp must be in the future
|
||||
return moment(t).isAfter();
|
||||
if (input) {
|
||||
if (this.get('basedOnLastPost')) {
|
||||
this.set('selection', SET_BASED_ON_LAST_POST);
|
||||
} else {
|
||||
// either # of hours or absolute time
|
||||
return (t.match(/^(\d+\.)?\d+$/) || t.match(/^\d{1,2}:\d{2}(\s?[AP]M)?$/i)) !== null;
|
||||
this.set('selection', PICK_DATE_AND_TIME);
|
||||
const datetime = moment(input);
|
||||
this.set('date', datetime.toDate());
|
||||
this.set('time', datetime.format("HH:mm"));
|
||||
this._updateInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@observes("date", "time")
|
||||
_updateInput() {
|
||||
const date = moment(this.get('date')).format("YYYY-MM-DD");
|
||||
const time = (this.get('time') && ` ${this.get('time')}`) || '';
|
||||
this.set('input', moment(`${date}${time}`).format(FORMAT));
|
||||
},
|
||||
|
||||
@observes("isBasedOnLastPost")
|
||||
_updateBasedOnLastPost() {
|
||||
this.set('basedOnLastPost', this.get('isBasedOnLastPost'));
|
||||
},
|
||||
|
||||
@computed("input", "isBasedOnLastPost")
|
||||
duration(input, isBasedOnLastPost) {
|
||||
const now = moment();
|
||||
|
||||
if (isBasedOnLastPost) {
|
||||
return parseFloat(input);
|
||||
} else {
|
||||
return moment(input) - now;
|
||||
}
|
||||
},
|
||||
|
||||
@computed("input", "isBasedOnLastPost")
|
||||
executeAt(input, isBasedOnLastPost) {
|
||||
if (isBasedOnLastPost) {
|
||||
return moment().add(input, 'hours').format(FORMAT);
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
},
|
||||
|
||||
@computed("statusType", "input", "isCustom", "date", "time", "willCloseImmediately")
|
||||
showTopicStatusInfo(statusType, input, isCustom, date, time, willCloseImmediately) {
|
||||
if (!statusType || willCloseImmediately) return false;
|
||||
|
||||
if (isCustom) {
|
||||
return date || time;
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
},
|
||||
|
||||
@computed('isBasedOnLastPost', 'input', 'lastPostedAt')
|
||||
willCloseImmediately(isBasedOnLastPost, input, lastPostedAt) {
|
||||
if (isBasedOnLastPost && input) {
|
||||
let closeDate = moment(lastPostedAt);
|
||||
closeDate = closeDate.add(input, 'hours');
|
||||
return closeDate < moment();
|
||||
}
|
||||
},
|
||||
|
||||
@computed('isBasedOnLastPost', 'lastPostedAt')
|
||||
willCloseI18n(isBasedOnLastPost, lastPostedAt) {
|
||||
if (isBasedOnLastPost) {
|
||||
const diff = Math.round((new Date() - new Date(lastPostedAt)) / (1000*60*60));
|
||||
return I18n.t('topic.auto_close_immediate', { count: diff });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import ComboboxView from 'discourse-common/components/combo-box';
|
||||
import Combobox from 'discourse-common/components/combo-box';
|
||||
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
||||
import PermissionType from 'discourse/models/permission-type';
|
||||
import Category from 'discourse/models/category';
|
||||
|
||||
export default ComboboxView.extend({
|
||||
export default Combobox.extend({
|
||||
classNames: ['combobox category-combobox'],
|
||||
dataAttributes: ['id', 'description_text'],
|
||||
overrideWidths: true,
|
||||
|
|
|
@ -110,9 +110,9 @@ export default Ember.Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
@computed('composer.title')
|
||||
isAbsoluteUrl() {
|
||||
return this.get('composer.titleLength') > 0 && /^(https?:)?\/\/[\w\.\-]+/i.test(this.get('composer.title'));
|
||||
@computed('composer.title', 'composer.titleLength')
|
||||
isAbsoluteUrl(title, titleLength) {
|
||||
return titleLength > 0 && /^(https?:)?\/\/[\w\.\-]+/i.test(title);
|
||||
},
|
||||
|
||||
bodyIsDefault() {
|
||||
|
|
|
@ -5,7 +5,8 @@ export default DatePicker.extend({
|
|||
|
||||
_opts() {
|
||||
return {
|
||||
defaultDate: moment().add(1, "day").toDate(),
|
||||
defaultDate: this.get('defaultDate') || moment().add(1, "day").toDate(),
|
||||
setDefaultDate: !!this.get('defaultDate'),
|
||||
minDate: new Date(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* global Pikaday:true */
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import { default as computed, on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Em.Component.extend({
|
||||
classNames: ["date-picker-wrapper"],
|
||||
|
@ -39,6 +39,11 @@ export default Em.Component.extend({
|
|||
this._picker = null;
|
||||
},
|
||||
|
||||
@computed()
|
||||
placeholder() {
|
||||
return I18n.t("dates.placeholder");
|
||||
},
|
||||
|
||||
_opts() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -2,21 +2,21 @@ import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
|||
import Category from 'discourse/models/category';
|
||||
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
elementId: 'topic-status-info',
|
||||
classNames: ['topic-status-info'],
|
||||
delayedRerender: null,
|
||||
|
||||
rerenderTriggers: [
|
||||
'topic.topic_status_update',
|
||||
'topic.topic_status_update.execute_at',
|
||||
'topic.topic_status_update.based_on_last_post',
|
||||
'topic.topic_status_update.duration',
|
||||
'topic.topic_status_update.category_id',
|
||||
'statusType',
|
||||
'executeAt',
|
||||
'basedOnLastPost',
|
||||
'duration',
|
||||
'categoryId',
|
||||
],
|
||||
|
||||
buildBuffer(buffer) {
|
||||
if (!this.get('topic.topic_status_update.execute_at')) return;
|
||||
if (!this.get('executeAt')) return;
|
||||
|
||||
let statusUpdateAt = moment(this.get('topic.topic_status_update.execute_at'));
|
||||
let statusUpdateAt = moment(this.get('executeAt'));
|
||||
if (statusUpdateAt < new Date()) return;
|
||||
|
||||
let duration = moment.duration(statusUpdateAt - moment());
|
||||
|
@ -33,7 +33,7 @@ export default Ember.Component.extend(bufferedRender({
|
|||
rerenderDelay = 60000;
|
||||
}
|
||||
|
||||
let autoCloseHours = this.get("topic.topic_status_update.duration") || 0;
|
||||
let autoCloseHours = this.get("duration") || 0;
|
||||
|
||||
buffer.push('<h3><i class="fa fa-clock-o"></i> ');
|
||||
|
||||
|
@ -42,7 +42,7 @@ export default Ember.Component.extend(bufferedRender({
|
|||
duration: moment.duration(autoCloseHours, "hours").humanize(),
|
||||
};
|
||||
|
||||
const categoryId = this.get('topic.topic_status_update.category_id');
|
||||
const categoryId = this.get('categoryId');
|
||||
|
||||
if (categoryId) {
|
||||
const category = Category.findById(categoryId);
|
||||
|
@ -67,9 +67,9 @@ export default Ember.Component.extend(bufferedRender({
|
|||
},
|
||||
|
||||
_noticeKey() {
|
||||
const statusType = this.get('topic.topic_status_update.status_type');
|
||||
const statusType = this.get('statusType');
|
||||
|
||||
if (this.get("topic.topic_status_update.based_on_last_post")) {
|
||||
if (this.get("basedOnLastPost")) {
|
||||
return `topic.status_update_notice.auto_${statusType}_based_on_last_post`;
|
||||
} else {
|
||||
return `topic.status_update_notice.auto_${statusType}`;
|
||||
|
|
|
@ -3,16 +3,11 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
|||
import TopicStatusUpdate from 'discourse/models/topic-status-update';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
const CLOSE_STATUS_TYPE = 'close';
|
||||
export const CLOSE_STATUS_TYPE = 'close';
|
||||
const OPEN_STATUS_TYPE = 'open';
|
||||
const PUBLISH_TO_CATEGORY_STATUS_TYPE = 'publish_to_category';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
closeStatusType: CLOSE_STATUS_TYPE,
|
||||
openStatusType: OPEN_STATUS_TYPE,
|
||||
publishToCategoryStatusType: PUBLISH_TO_CATEGORY_STATUS_TYPE,
|
||||
updateTimeValid: null,
|
||||
updateTimeInvalid: Em.computed.not('updateTimeValid'),
|
||||
loading: false,
|
||||
updateTime: null,
|
||||
topicStatusUpdate: Ember.computed.alias("model.topic_status_update"),
|
||||
|
@ -21,42 +16,18 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
autoClose: Ember.computed.equal('selection', CLOSE_STATUS_TYPE),
|
||||
publishToCategory: Ember.computed.equal('selection', PUBLISH_TO_CATEGORY_STATUS_TYPE),
|
||||
|
||||
@computed('autoClose', 'updateTime')
|
||||
disableAutoClose(autoClose, updateTime) {
|
||||
return updateTime && !autoClose;
|
||||
@computed("model.closed")
|
||||
statusUpdates(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') }
|
||||
];
|
||||
},
|
||||
|
||||
@computed('autoOpen', 'updateTime')
|
||||
disableAutoOpen(autoOpen, updateTime) {
|
||||
return updateTime && !autoOpen;
|
||||
},
|
||||
|
||||
@computed('publishToCatgory', 'updateTime')
|
||||
disablePublishToCategory(publishToCatgory, updateTime) {
|
||||
return updateTime && !publishToCatgory;
|
||||
},
|
||||
|
||||
@computed('topicStatusUpdate.based_on_last_post', 'updateTime', 'model.last_posted_at')
|
||||
willCloseImmediately(basedOnLastPost, updateTime, lastPostedAt) {
|
||||
if (!basedOnLastPost) {
|
||||
return false;
|
||||
}
|
||||
const closeDate = new Date(lastPostedAt);
|
||||
closeDate.setHours(closeDate.getHours() + updateTime);
|
||||
return closeDate < new Date();
|
||||
},
|
||||
|
||||
@computed('topicStatusUpdate.based_on_last_post', 'model.last_posted_at')
|
||||
willCloseI18n(basedOnLastPost, lastPostedAt) {
|
||||
if (basedOnLastPost) {
|
||||
const diff = Math.round((new Date() - new Date(lastPostedAt)) / (1000*60*60));
|
||||
return I18n.t('topic.auto_close_immediate', { count: diff });
|
||||
}
|
||||
},
|
||||
|
||||
@computed('updateTime', 'updateTimeInvalid', 'loading')
|
||||
saveDisabled(updateTime, updateTimeInvalid, loading) {
|
||||
return Ember.isEmpty(updateTime) || updateTimeInvalid || loading;
|
||||
@computed('updateTime', 'loading')
|
||||
saveDisabled(updateTime, loading) {
|
||||
return Ember.isEmpty(updateTime) || loading;
|
||||
},
|
||||
|
||||
@computed("model.visible")
|
||||
|
@ -66,29 +37,31 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
|
||||
@observes("topicStatusUpdate.execute_at", "topicStatusUpdate.duration")
|
||||
_setUpdateTime() {
|
||||
if (!this.get('topicStatusUpdate.execute_at')) return;
|
||||
|
||||
let time = null;
|
||||
|
||||
if (this.get("topicStatusUpdate.based_on_last_post")) {
|
||||
time = this.get("topicStatusUpdate.duration");
|
||||
} else if (this.get("topicStatusUpdate.execute_at")) {
|
||||
const closeTime = new Date(this.get("topicStatusUpdate.execute_at"));
|
||||
const closeTime = moment(this.get('topicStatusUpdate.execute_at'));
|
||||
|
||||
if (closeTime > new Date()) {
|
||||
time = moment(closeTime).format("YYYY-MM-DD HH:mm");
|
||||
if (closeTime > moment()) {
|
||||
time = closeTime.format("YYYY-MM-DD HH:mm");
|
||||
}
|
||||
}
|
||||
|
||||
this.set("updateTime", time);
|
||||
},
|
||||
|
||||
_setStatusUpdate(time, status_type) {
|
||||
_setStatusUpdate(time, statusType) {
|
||||
this.set('loading', true);
|
||||
|
||||
TopicStatusUpdate.updateStatus(
|
||||
this.get('model.id'),
|
||||
time,
|
||||
this.get('topicStatusUpdate.based_on_last_post'),
|
||||
status_type,
|
||||
statusType,
|
||||
this.get('categoryId')
|
||||
).then(result => {
|
||||
if (time) {
|
||||
|
@ -102,8 +75,11 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
|
||||
this.set('model.closed', result.closed);
|
||||
} else {
|
||||
this.set('topicStatusUpdate', Ember.Object.create({}));
|
||||
this.set('selection', null);
|
||||
this.setProperties({
|
||||
topicStatusUpdate: Ember.Object.create({}),
|
||||
selection: null,
|
||||
updateTime: null
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
popupAjaxError(error);
|
||||
|
|
|
@ -54,7 +54,7 @@ const TopicRoute = Discourse.Route.extend({
|
|||
const model = this.modelFor('topic');
|
||||
model.set('topic_status_update', Ember.Object.create(model.get('topic_status_update')));
|
||||
showModal('edit-topic-status-update', { model });
|
||||
this.controllerFor('modal').set('modalClass', 'topic-close-modal');
|
||||
this.controllerFor('modal').set('modalClass', 'edit-topic-status-update-modal');
|
||||
},
|
||||
|
||||
showChangeTimestamp() {
|
||||
|
|
|
@ -1,24 +1,52 @@
|
|||
<div class="auto-update-input">
|
||||
<div class="control-group">
|
||||
<label>
|
||||
{{i18n inputLabelKey}}
|
||||
{{text-field value=input}}
|
||||
{{i18n inputUnitsKey}}
|
||||
</label>
|
||||
<label>{{i18n "topic.topic_status_update.when"}}</label>
|
||||
|
||||
{{#if inputExamplesKey}}
|
||||
<div class="examples">
|
||||
{{i18n inputExamplesKey}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{auto-update-input-selector
|
||||
valueAttribute="id"
|
||||
minimumResultsForSearch=-1
|
||||
statusType=statusType
|
||||
value=selection
|
||||
input=input
|
||||
width="50%"
|
||||
none="topic.auto_update_input.none"}}
|
||||
</div>
|
||||
|
||||
{{#unless hideBasedOnLastPost}}
|
||||
{{#if isCustom}}
|
||||
<div class="control-group">
|
||||
{{fa-icon "calendar"}} {{date-picker-future value=date defaultDate=date}}
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
{{fa-icon "clock-o"}}
|
||||
{{input type="time" value=time}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if isBasedOnLastPost}}
|
||||
<div class="control-group">
|
||||
<label>
|
||||
{{input type="checkbox" checked=basedOnLastPost}}
|
||||
{{i18n 'topic.auto_close.based_on_last_post'}}
|
||||
{{i18n 'topic.topic_status_update.num_of_hours'}}
|
||||
{{text-field value=input type="number"}}
|
||||
</label>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#if willCloseImmediately}}
|
||||
<div class="warning">
|
||||
{{fa-icon "warning"}}
|
||||
{{willCloseI18n}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showTopicStatusInfo}}
|
||||
<div class="alert alert-info">
|
||||
{{topic-status-info
|
||||
statusType=statusType
|
||||
executeAt=executeAt
|
||||
basedOnLastPost=basedOnLastPost
|
||||
duration=duration
|
||||
categoryId=categoryId}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<input type="text" class="date-picker">
|
||||
{{input type="text" class="date-picker" placeholder=placeholder}}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
<section class='field'>
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.auto_close.label'
|
||||
input=category.auto_close_hours
|
||||
basedOnLastPost=category.auto_close_based_on_last_post
|
||||
inputExamplesKey=''
|
||||
limited=true}}
|
||||
<div class="control-group">
|
||||
<label>
|
||||
{{i18n 'topic.auto_close.label'}}
|
||||
{{text-field value=category.auto_close_hours type="number"}}
|
||||
</label>
|
||||
|
||||
<label>
|
||||
{{input type="checkbox" checked=category.auto_close_based_on_last_post}}
|
||||
{{i18n 'topic.auto_close.based_on_last_post'}}
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class='field'>
|
||||
|
|
|
@ -1,59 +1,14 @@
|
|||
<form>
|
||||
{{#d-modal-body title="topic.topic_status_update.title" autoFocus="false"}}
|
||||
<div class="radios">
|
||||
{{radio-button
|
||||
disabled=disableAutoClose
|
||||
name="auto-close"
|
||||
id="auto-close"
|
||||
value=closeStatusType
|
||||
selection=selection}}
|
||||
|
||||
<label class="radio" for="auto-close">
|
||||
{{fa-icon "clock-o"}} {{fa-icon "lock"}}
|
||||
|
||||
{{#if model.closed}}
|
||||
{{i18n 'topic.temp_open.title'}}
|
||||
{{else}}
|
||||
{{i18n 'topic.auto_close.title'}}
|
||||
{{/if}}
|
||||
</label>
|
||||
|
||||
{{radio-button
|
||||
disabled=disableAutoOpen
|
||||
name="auto-reopen"
|
||||
id="auto-reopen"
|
||||
value=openStatusType
|
||||
selection=selection}}
|
||||
|
||||
<label class="radio" for="auto-reopen">
|
||||
{{fa-icon "clock-o"}} {{fa-icon "unlock"}}
|
||||
|
||||
{{#if model.closed}}
|
||||
{{i18n 'topic.auto_reopen.title'}}
|
||||
{{else}}
|
||||
{{i18n 'topic.temp_close.title'}}
|
||||
{{/if}}
|
||||
</label>
|
||||
|
||||
{{radio-button
|
||||
disabled=disablePublishToCategory
|
||||
name="publish-to-category"
|
||||
id="publish-to-category"
|
||||
value=publishToCategoryStatusType
|
||||
selection=selection}}
|
||||
|
||||
<label class="radio" for="publish-to-category">
|
||||
{{fa-icon "clock-o"}} {{i18n 'topic.publish_to_category.title'}}
|
||||
</label>
|
||||
<div class="control-group">
|
||||
{{combo-box content=statusUpdates value=selection width="50%"}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{#if autoOpen}}
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
hideBasedOnLastPost=true
|
||||
statusType=selection
|
||||
basedOnLastPost=false}}
|
||||
{{else if publishToCategory}}
|
||||
<div class="control-group">
|
||||
|
@ -62,25 +17,16 @@
|
|||
</div>
|
||||
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
hideBasedOnLastPost=true
|
||||
statusType=selection
|
||||
categoryId=categoryId
|
||||
basedOnLastPost=false}}
|
||||
{{else if autoClose}}
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
limited=topicStatusUpdate.based_on_last_post
|
||||
basedOnLastPost=topicStatusUpdate.based_on_last_post}}
|
||||
|
||||
{{#if willCloseImmediately}}
|
||||
<div class="warning">
|
||||
{{fa-icon "warning"}}
|
||||
{{willCloseI18n}}
|
||||
</div>
|
||||
{{/if}}
|
||||
statusType=selection
|
||||
basedOnLastPost=topicStatusUpdate.based_on_last_post
|
||||
lastPostedAt=model.last_posted_at}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/d-modal-body}}
|
||||
|
|
|
@ -174,7 +174,13 @@
|
|||
{{#conditional-loading-spinner condition=model.postStream.loadingFilter}}
|
||||
{{#if loadedAllPosts}}
|
||||
|
||||
{{topic-status-info topic=model}}
|
||||
{{topic-status-info
|
||||
statusType=model.topic_status_update.status_type
|
||||
executeAt=model.topic_status_update.execute_at
|
||||
basedOnLastPost=model.topic_status_update.based_on_last_post
|
||||
duration=model.topic_status_update.duration
|
||||
categoryId=model.topic_status_update.category_id}}
|
||||
|
||||
{{#if session.showSignupCta}}
|
||||
{{! replace "Log In to Reply" with the infobox }}
|
||||
{{signup-cta}}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
.edit-topic-status-update-modal {
|
||||
.modal-body {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
input.date-picker, input[type="time"] {
|
||||
width: 150px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn.pull-right {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.auto-update-input {
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
margin: 0 -15px -15px -15px;
|
||||
}
|
||||
|
||||
.pika-single {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.topic-status-info {
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
h3 {
|
||||
font-weight: normal;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
.topic-close-modal {
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.radios {
|
||||
padding-bottom: 20px;
|
||||
display: inline-block;
|
||||
|
||||
input[type='radio'] {
|
||||
vertical-align: middle;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 0 10px 0px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn.pull-right {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.auto-update-input {
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
.auto-update-input-selector-datetime {
|
||||
float: right;
|
||||
color: lighten($primary, 40%);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.auto-update-input-selector-icons {
|
||||
margin-right: 10px;
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
.show-topic-admin,
|
||||
#topic-progress,
|
||||
.quote-controls,
|
||||
#topic-status-info,
|
||||
.topic-status-info,
|
||||
div.lazyYT,
|
||||
.post-info.edits,
|
||||
.post-action,
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
#topic-status-info {
|
||||
.topic-status-info {
|
||||
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
||||
padding-top: 10px;
|
||||
height: 20px;
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
clear: both;
|
||||
}
|
||||
|
||||
#topic-status-info {
|
||||
.topic-status-info {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ en:
|
|||
other: "%{count} years later"
|
||||
previous_month: 'Previous Month'
|
||||
next_month: 'Next Month'
|
||||
placeholder: Pick a date
|
||||
share:
|
||||
topic: 'share a link to this topic'
|
||||
post: 'post #%{postNumber}'
|
||||
|
@ -1484,16 +1485,19 @@ en:
|
|||
topic_status_update:
|
||||
title: "Set Topic Timer"
|
||||
save: "Set Timer"
|
||||
time: "Time:"
|
||||
num_of_hours: "Number of hours:"
|
||||
remove: "Remove Timer"
|
||||
publish_to: "Publish To:"
|
||||
when: "When:"
|
||||
auto_update_input:
|
||||
limited:
|
||||
units: "(# of hours)"
|
||||
examples: 'Enter number of hours (24).'
|
||||
all:
|
||||
units: ""
|
||||
examples: 'Enter number of hours (24), absolute time (17:30) or timestamp (2013-11-22 14:00).'
|
||||
none: ""
|
||||
later_today: "Later today"
|
||||
tomorrow: "Tomorrow"
|
||||
later_this_week: "Later this week"
|
||||
this_weekend: "This weekend"
|
||||
next_week: "Next week"
|
||||
pick_date_and_time: "Pick date and time"
|
||||
set_based_on_last_post: "Close based on last post"
|
||||
publish_to_category:
|
||||
title: "Schedule Publishing"
|
||||
temp_open:
|
||||
|
@ -1504,7 +1508,7 @@ en:
|
|||
title: "Close Temporarily"
|
||||
auto_close:
|
||||
title: "Auto-Close Topic"
|
||||
label: "Auto-close topic time:"
|
||||
label: "Auto-close topic hours:"
|
||||
error: "Please enter a valid value."
|
||||
based_on_last_post: "Don't close until the last post in the topic is at least this old."
|
||||
|
||||
|
@ -2010,8 +2014,6 @@ en:
|
|||
security: "Security"
|
||||
special_warning: "Warning: This category is a pre-seeded category and the security settings cannot be edited. If you do not wish to use this category, delete it instead of repurposing it."
|
||||
images: "Images"
|
||||
auto_close_label: "Auto-close topics after:"
|
||||
auto_close_units: "hours"
|
||||
email_in: "Custom incoming email address:"
|
||||
email_in_allow_strangers: "Accept emails from anonymous users with no accounts"
|
||||
email_in_disabled: "Posting new topics via email is disabled in the Site Settings. To enable posting new topics via email, "
|
||||
|
|
|
@ -56,3 +56,19 @@ componentTest('with none', {
|
|||
assert.equal(this.$("select option:eq(2)").text(), 'trout');
|
||||
}
|
||||
});
|
||||
|
||||
componentTest('with Object none', {
|
||||
template: '{{combo-box content=items none=none value=value selected="something"}}',
|
||||
setup() {
|
||||
this.set('none', { id: 'something', name: 'none' });
|
||||
this.set('items', ['evil', 'trout', 'hat']);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.get('value'), 'something');
|
||||
assert.equal(this.$("select option:eq(0)").text(), 'none');
|
||||
assert.equal(this.$("select option:eq(0)").val(), 'something');
|
||||
assert.equal(this.$("select option:eq(1)").text(), 'evil');
|
||||
assert.equal(this.$("select option:eq(2)").text(), 'trout');
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue