New Feature: Staff can choose to "Take Action" when flagging to immediately reach hiding

thresholds.
This commit is contained in:
Robin Ward
2013-05-31 17:38:28 -04:00
parent 476ffcc627
commit 545dbfc07e
19 changed files with 194 additions and 135 deletions
@@ -0,0 +1,48 @@
/**
Supports logic for flags in the modal
@class FlagActionTypeController
@extends Discourse.ObjectController
@namespace Discourse
@module Discourse
**/
Discourse.FlagActionTypeController = Discourse.ObjectController.extend({
needs: ['flag'],
message: Em.computed.alias('controllers.flag.message'),
customPlaceholder: function(){
return Em.String.i18n("flagging.custom_placeholder_" + this.get('name_key'));
}.property('name_key'),
formattedName: function(){
return this.get('name').replace("{{username}}", this.get('controllers.flag.username'));
}.property('name'),
selected: function() {
return this.get('model') === this.get('controllers.flag.selected');
}.property('controllers.flag.selected'),
showMessageInput: Em.computed.and('is_custom_flag', 'selected'),
showDescription: Em.computed.not('showMessageInput'),
customMessageLengthClasses: function() {
return (this.get('message.length') < Discourse.PostActionType.MIN_MESSAGE_LENGTH) ? "too-short" : "ok"
}.property('message.length'),
customMessageLength: function() {
var len = this.get('message.length') || 0;
var minLen = Discourse.PostActionType.MIN_MESSAGE_LENGTH;
if (len === 0) {
return Em.String.i18n("flagging.custom_message.at_least", { n: minLen });
} else if (len < minLen) {
return Em.String.i18n("flagging.custom_message.more", { n: minLen - len });
} else {
return Em.String.i18n("flagging.custom_message.left", {
n: Discourse.PostActionType.MAX_MESSAGE_LENGTH - len
});
}
}.property('message.length')
});
@@ -9,70 +9,58 @@
**/
Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
// trick to bind user / post to flag
boundFlags: function() {
var _this = this;
var original = this.get('flagsAvailable');
if(original){
return $.map(original, function(v){
var b = Discourse.BoundPostActionType.create(v);
b.set('post', _this.get('model'));
return b;
});
}
}.property('flagsAvailable.@each'),
changePostActionType: function(action) {
if (this.get('postActionTypeId') === action.id) return false;
this.get('boundFlags').setEach('selected', false);
action.set('selected', true);
this.set('postActionTypeId', action.id);
this.set('isCustomFlag', action.is_custom_flag);
this.set('selected', action);
return false;
},
showSubmit: function() {
if (this.get('postActionTypeId')) {
if (this.get('isCustomFlag')) {
var m = this.get('selected.message');
return m && m.length >= 10 && m.length <= 500;
} else {
return true;
}
submitEnabled: function() {
var selected = this.get('selected');
if (!selected) return false;
if (selected.get('is_custom_flag')) {
var len = this.get('message.length') || 0;
return len >= Discourse.PostActionType.MIN_MESSAGE_LENGTH &&
len <= Discourse.PostActionType.MAX_MESSAGE_LENGTH;
}
return false;
}.property('isCustomFlag', 'selected.customMessageLength', 'postActionTypeId'),
return true;
}.property('selected.is_custom_flag', 'message.length'),
submitDisabled: Em.computed.not('submitEnabled'),
// Staff accounts can "take action"
canTakeAction: function() {
// We can only take actions on non-custom flags
if (this.get('selected.is_custom_flag')) return false;
return Discourse.User.current('staff');
}.property('selected.is_custom_flag'),
submitText: function(){
var action = this.get('selected');
if (this.get('selected.is_custom_flag')) {
return Em.String.i18n("flagging.notify_action");
} else {
return Em.String.i18n("flagging.action");
}
}.property('selected'),
}.property('selected.is_custom_flag'),
createFlag: function() {
var _this = this;
takeAction: function() {
this.createFlag({takeAction: true})
this.set('hidden', true);
},
var action = this.get('selected');
var postAction = this.get('actionByName.' + (action.get('name_key')));
createFlag: function(opts) {
var flagController = this;
var postAction = this.get('actionByName.' + this.get('selected.name_key'));
var params = this.get('selected.is_custom_flag') ? {message: this.get('message')} : {}
var actionType = Discourse.Site.instance().postActionTypeById(this.get('postActionTypeId'));
if (postAction) {
postAction.act({
message: action.get('message')
}).then(function() {
return $('#discourse-modal').modal('hide');
}, function(errors) {
return _this.displayErrors(errors);
});
}
return false;
if (opts) params = $.extend(params, opts);
postAction.act(params).then(function() {
flagController.closeModal();
}, function(errors) {
flagController.displayErrors(errors);
});
}
});
@@ -8,6 +8,16 @@
**/
Discourse.ModalController = Discourse.Controller.extend({
/**
Close the modal.
@method closeModal
**/
closeModal: function() {
// Currently uses jQuery to hide it.
$('#discourse-modal').modal('hide');
}
});
@@ -21,6 +21,16 @@ Discourse.ModalFunctionality = Em.Mixin.create({
message: message,
messageClass: messageClass
}));
},
/**
Close the modal.
@method closeModal
**/
closeModal: function() {
// Currently uses jQuery to hide it.
this.get('controllers.modal').closeModal();
}
});
@@ -37,6 +37,8 @@ Discourse.ActionSummary = Discourse.Model.extend({
// Perform this action
act: function(opts) {
if (!opts) opts = {};
var action = this.get('actionType.name_key');
// Mark it as acted
@@ -63,7 +65,8 @@ Discourse.ActionSummary = Discourse.Model.extend({
data: {
id: this.get('post.id'),
post_action_type_id: this.get('id'),
message: (opts ? opts.message : void 0) || ""
message: opts.message,
take_action: opts.takeAction
}
}).then(null, function (error) {
actionSummary.removeAction();
@@ -6,31 +6,9 @@
@namespace Discourse
@module Discourse
**/
Discourse.PostActionType = Discourse.Model.extend({
});
Discourse.PostActionType = Discourse.Model.extend({});
Discourse.BoundPostActionType = Discourse.PostActionType.extend({
customPlaceholder: function(){
return Em.String.i18n("flagging.custom_placeholder_" + this.get('name_key'));
}.property('name_key'),
formattedName: function(){
return this.get('name').replace("{{username}}", this.get('post.username'));
}.property('name'),
messageChanged: function() {
var len, message, minLen, _ref;
minLen = 10;
len = ((_ref = this.get('message')) ? _ref.length : void 0) || 0;
this.set("customMessageLengthClasses", "too-short custom-message-length");
if (len === 0) {
message = Em.String.i18n("flagging.custom_message.at_least", { n: minLen });
} else if (len < minLen) {
message = Em.String.i18n("flagging.custom_message.more", { n: minLen - len });
} else {
message = Em.String.i18n("flagging.custom_message.left", { n: 500 - len });
this.set("customMessageLengthClasses", "ok custom-message-length");
}
this.set("customMessageLength", message);
}.observes("message")
});
Discourse.PostActionType.reopenClass({
MIN_MESSAGE_LENGTH: 10,
MAX_MESSAGE_LENGTH: 500
})
@@ -13,9 +13,7 @@ Discourse.TopicRoute = Discourse.Route.extend({
showFlags: function(post) {
Discourse.Route.showModal(this, 'flag', post);
this.controllerFor('flag').setProperties({
postActionTypeId: null
});
this.controllerFor('flag').setProperties({ selected: null });
},
showAutoClose: function() {
@@ -1,8 +1,8 @@
<div class="modal-body">
<div class="modal-body">
<form>
{{view Discourse.ArchetypeOptionsView archetypeBinding="view.archetype"}}
</form>
</div>
<div class="modal-footer">
<button class='btn btn-primary' data-dismiss="modal">{{i18n post.archetypes.save}}</button>
</div>
<button class='btn btn-primary' {{action closeModal}}>{{i18n post.archetypes.save}}</button>
</div>
@@ -1,36 +1,30 @@
<div class="modal-body flag-modal">
{{#if flagsAvailable}}
<form>
{{#each boundFlags}}
<div class='controls'>
<label class='radio'>
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
{{#if is_custom_flag}}
{{#unless selected}}
<div class='description'>{{{description}}}</div>
{{/unless}}
{{else}}
{{#if description}}
<div class='description'>{{{description}}}</div>
{{/if}}
{{/if}}
</label>
{{#if is_custom_flag}}
{{#if selected}}
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
<div {{bindAttr class="customMessageLengthClasses"}}>{{customMessageLength}}</div>
{{/if}}
<form>
{{#each flagsAvailable itemController="flagActionType"}}
<div class='controls'>
<label class='radio'>
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
{{#if showDescription}}
<div class='description'>{{{description}}}</div>
{{/if}}
</div>
{{/each}}
</form>
{{else}}
{{i18n flagging.cant}}
</label>
{{#if showMessageInput}}
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
<div {{bindAttr class=":custom-message-length customMessageLengthClasses"}}>{{customMessageLength}}</div>
{{/if}}
</div>
{{else}}
{{i18n flagging.cant}}
{{/each}}
</form>
</div>
<div class="modal-footer">
<button class='btn btn-primary' {{action createFlag}} {{bindAttr disabled="submitDisabled"}}>{{submitText}}</button>
{{#if canTakeAction}}
<button class='btn btn-danger' {{action takeAction}} {{bindAttr disabled="submitDisabled"}}>{{i18n flagging.take_action}}</button>
{{/if}}
</div>
{{#if showSubmit}}
<div class="modal-footer">
<button class='btn btn-primary' {{action createFlag}}>{{submitText}}</button>
</div>
{{/if}}
@@ -17,7 +17,7 @@
</div>
<div class="modal-footer">
{{#if finished}}
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
<button class='btn btn-primary' {{action closeModal}}>{{i18n close}}</button>
{{else}}
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action createInvite}}>{{buttonTitle}}</button>
{{/if}}
@@ -17,7 +17,7 @@
</div>
<div class="modal-footer">
{{#if finished}}
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
<button class='btn btn-primary' {{action closeModal}}>{{i18n close}}</button>
{{else}}
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action invite}}>{{buttonTitle}}</button>
{{/if}}
@@ -1,5 +1,5 @@
<div class="modal-header">
<a class="close" data-dismiss="modal"><i class='icon-remove icon'></i></a>
<a class="close" {{action closeModal}}><i class='icon-remove icon'></i></a>
<h3>{{title}}</h3>
</div>
<div id='modal-alert'></div>
@@ -7,5 +7,5 @@
{{/if}}
</div>
<div class="modal-footer">
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
<button class='btn btn-primary' {{action closeModal}}>{{i18n close}}</button>
</div>