displays the number of characters left when editing the topic title

This commit is contained in:
Régis Hanol 2013-03-15 12:56:14 +01:00
parent 2a5cbd8072
commit aef014f439
6 changed files with 54 additions and 57 deletions
app/assets/javascripts/discourse
config/locales

@ -19,12 +19,13 @@ Discourse.ComposerController = Discourse.Controller.extend({
return this.get('content').importQuote(); return this.get('content').importQuote();
}, },
resetDraftStatus: function() {
this.get('content').resetDraftStatus();
},
appendText: function(text) { appendText: function(text) {
var c; var c = this.get('content');
c = this.get('content'); if (c) return c.appendText(text);
if (c) {
return c.appendText(text);
}
}, },
save: function(force) { save: function(force) {
@ -95,16 +96,11 @@ Discourse.ComposerController = Discourse.Controller.extend({
}, },
checkReplyLength: function() { checkReplyLength: function() {
if (this.present('content.reply')) { this.set('hasReply', this.present('content.reply'));
this.set('hasReply', true);
} else {
this.set('hasReply', false);
}
}, },
saveDraft: function() { saveDraft: function() {
var model; var model = this.get('content');
model = this.get('content');
if (model) model.saveDraft(); if (model) model.saveDraft();
}, },

@ -28,7 +28,7 @@ Discourse.Composer = Discourse.Model.extend({
this._super(); this._super();
var val = Discourse.KeyValueStore.get('composer.showPreview') || 'true'; var val = Discourse.KeyValueStore.get('composer.showPreview') || 'true';
this.set('showPreview', val === 'true'); this.set('showPreview', val === 'true');
return this.set('archetypeId', Discourse.get('site.default_archetype')); this.set('archetypeId', Discourse.get('site.default_archetype'));
}, },
creatingTopic: (function() { creatingTopic: (function() {
@ -142,10 +142,7 @@ Discourse.Composer = Discourse.Model.extend({
}).property('action', 'post', 'topic', 'topic.title'), }).property('action', 'post', 'topic', 'topic.title'),
toggleText: (function() { toggleText: (function() {
if (this.get('showPreview')) { return this.get('showPreview') ? Em.String.i18n('composer.hide_preview') : Em.String.i18n('composer.show_preview');
return Em.String.i18n('composer.hide_preview');
}
return Em.String.i18n('composer.show_preview');
}).property('showPreview'), }).property('showPreview'),
hidePreview: (function() { hidePreview: (function() {
@ -159,14 +156,11 @@ Discourse.Composer = Discourse.Model.extend({
if (this.get('loading')) return true; if (this.get('loading')) return true;
// Title is required on new posts // Title is required on new posts
if (this.get('creatingTopic')) { if (this.get('creatingTopic') && this.get('titleLength') < Discourse.SiteSettings.min_topic_title_length) return true;
if (this.blank('title')) return true;
if (this.get('title').trim().length < Discourse.SiteSettings.min_topic_title_length) return true;
}
// Otherwise just reply is required // Otherwise just reply is required
if (this.blank('reply')) return true; if (this.get('replyLength') < Discourse.SiteSettings.min_post_length) return true;
if (this.get('reply').trim().length < Discourse.SiteSettings.min_post_length) return true;
return false; return false;
}).property('reply', 'title', 'creatingTopic', 'loading'), }).property('reply', 'title', 'creatingTopic', 'loading'),
@ -438,6 +432,7 @@ Discourse.Composer = Discourse.Model.extend({
saveDraft: function() { saveDraft: function() {
if (this.get('disableDrafts')) return; if (this.get('disableDrafts')) return;
if (!this.get('reply')) return; if (!this.get('reply')) return;
if (this.get('titleLength') < Discourse.SiteSettings.min_topic_title_length) return;
if (this.get('replyLength') < Discourse.SiteSettings.min_post_length) return; if (this.get('replyLength') < Discourse.SiteSettings.min_post_length) return;
var data = { var data = {
@ -457,28 +452,38 @@ Discourse.Composer = Discourse.Model.extend({
return Discourse.Draft.save(this.get('draftKey'), this.get('draftSequence'), data).then((function() { return Discourse.Draft.save(this.get('draftKey'), this.get('draftSequence'), data).then((function() {
composer.set('draftStatus', Em.String.i18n('composer.saved_draft_tip')); composer.set('draftStatus', Em.String.i18n('composer.saved_draft_tip'));
}), (function() { }), (function() {
composer.set('draftStatus', 'drafts offline'); composer.set('draftStatus', Em.String.i18n('composer.drafts_offline'));
})); }));
}, },
resetDraftStatus: (function() { resetDraftStatus: (function() {
var len = Discourse.SiteSettings.min_post_length, // 'title' is focused
replyLength = this.get('replyLength'); if ($('#reply-title').is(':focus')) {
var titleDiff = Discourse.SiteSettings.min_topic_title_length - this.get('titleLength');
if (replyLength === 0) { if (titleDiff > 0) {
this.set('draftStatus', Em.String.i18n('composer.min_length.at_least', { n: len })); return this.set('draftStatus', Em.String.i18n('composer.min_length.need_more_for_title', { n: titleDiff }));
} else if (replyLength < len) { }
this.set('draftStatus', Em.String.i18n('composer.min_length.more', { n: len - replyLength })); // 'reply' is focused
} else { } else if ($('#wmd-input').is(':focus')) {
this.set('draftStatus', null); var replyDiff = Discourse.SiteSettings.min_post_length - this.get('replyLength');
if (replyDiff > 0) {
return this.set('draftStatus', Em.String.i18n('composer.min_length.need_more_for_reply', { n: replyDiff }));
}
} }
// hide the counters if the currently focused text field is OK
this.set('draftStatus', null);
}).observes('reply', 'title'), }).observes('reply', 'title'),
blank: function(prop) { /**
var p = this.get(prop); Computes the length of the title minus non-significant whitespaces
return !(p && p.length > 0);
}, @property titleLength
**/
titleLength: function() {
var title = this.get('title') || "";
return title.replace(/\s+/img, " ").trim().length;
}.property('title'),
/** /**
Computes the length of the reply minus the quote(s) and non-significant whitespaces Computes the length of the reply minus the quote(s) and non-significant whitespaces
@ -486,8 +491,7 @@ Discourse.Composer = Discourse.Model.extend({
@property replyLength @property replyLength
**/ **/
replyLength: function() { replyLength: function() {
var reply = this.get('reply'); var reply = this.get('reply') || "";
if(!reply) reply = "";
while (Discourse.BBCode.QUOTE_REGEXP.test(reply)) { reply = reply.replace(Discourse.BBCode.QUOTE_REGEXP, ""); } while (Discourse.BBCode.QUOTE_REGEXP.test(reply)) { reply = reply.replace(Discourse.BBCode.QUOTE_REGEXP, ""); }
return reply.replace(/\s+/img, " ").trim().length; return reply.replace(/\s+/img, " ").trim().length;
}.property('reply') }.property('reply')
@ -547,5 +551,3 @@ Discourse.Composer.reopenClass({
// Draft key // Draft key
REPLY_AS_NEW_TOPIC_KEY: REPLY_AS_NEW_TOPIC_KEY REPLY_AS_NEW_TOPIC_KEY: REPLY_AS_NEW_TOPIC_KEY
}); });

@ -17,7 +17,6 @@
{{#if content.editTitle}} {{#if content.editTitle}}
<div class='form-element clearfix'> <div class='form-element clearfix'>
{{#if content.creatingPrivateMessage}} {{#if content.creatingPrivateMessage}}
{{view Discourse.TextField id="private-message-users" class="span8" placeholderKey="composer.users_placeholder"}} {{view Discourse.TextField id="private-message-users" class="span8" placeholderKey="composer.users_placeholder"}}
{{/if}} {{/if}}
@ -31,7 +30,6 @@
</div> </div>
{{/if}} {{/if}}
<div class='wmd-controls'> <div class='wmd-controls'>
<div class='textarea-wrapper'> <div class='textarea-wrapper'>
<div class='wmd-button-bar' id='wmd-button-bar'></div> <div class='wmd-button-bar' id='wmd-button-bar'></div>
@ -43,7 +41,6 @@
{{#if Discourse.currentUser}} {{#if Discourse.currentUser}}
<a href="#" {{action togglePreview target="controller"}} class='toggle-preview'>{{{content.toggleText}}}</a> <a href="#" {{action togglePreview target="controller"}} class='toggle-preview'>{{{content.toggleText}}}</a>
<div class='saving-draft'></div> <div class='saving-draft'></div>
{{/if}} {{/if}}
</div> </div>
@ -51,7 +48,6 @@
<div class='submit-panel'> <div class='submit-panel'>
<button {{action save target="controller"}} tabindex="3" {{bindAttr disabled="content.cantSubmitPost"}} class='btn btn-primary create'>{{view.content.saveText}}</button> <button {{action save target="controller"}} tabindex="3" {{bindAttr disabled="content.cantSubmitPost"}} class='btn btn-primary create'>{{view.content.saveText}}</button>
<a href='#' {{action cancel target="controller"}} class='cancel' tabindex="4">{{i18n cancel}}</a> <a href='#' {{action cancel target="controller"}} class='cancel' tabindex="4">{{i18n cancel}}</a>
{{#if view.loadingImage}} {{#if view.loadingImage}}
<div id="image-uploading"> <div id="image-uploading">
{{i18n image_selector.uploading_image}} {{view.uploadProgress}}% <a {{action cancelUpload target="view"}}>{{i18n cancel}}</a> {{i18n image_selector.uploading_image}} {{view.uploadProgress}}% <a {{action cancelUpload target="view"}}>{{i18n cancel}}</a>

@ -22,8 +22,7 @@ Discourse.ComposerView = Discourse.View.extend({
educationClosed: null, educationClosed: null,
composeState: (function() { composeState: (function() {
var state; var state = this.get('content.composeState');
state = this.get('content.composeState');
if (!state) { if (!state) {
state = Discourse.Composer.CLOSED; state = Discourse.Composer.CLOSED;
} }
@ -103,8 +102,7 @@ Discourse.ComposerView = Discourse.View.extend({
}).property('content.composeState', 'content.reply', 'educationClosed', 'educationContents'), }).property('content.composeState', 'content.reply', 'educationClosed', 'educationContents'),
newUserEducationVisibilityChanged: (function() { newUserEducationVisibilityChanged: (function() {
var $panel; var $panel = $('#new-user-education');
$panel = $('#new-user-education');
if (this.get('newUserEducationVisible')) { if (this.get('newUserEducationVisible')) {
return $panel.slideDown('fast'); return $panel.slideDown('fast');
} else { } else {
@ -116,6 +114,11 @@ Discourse.ComposerView = Discourse.View.extend({
$('#new-user-education').css('bottom', sizePx); $('#new-user-education').css('bottom', sizePx);
}, },
focusIn: (function() {
var controller = this.get('controller');
if(controller) controller.resetDraftStatus();
}),
resize: (function() { resize: (function() {
// this still needs to wait on animations, need a clean way to do that // this still needs to wait on animations, need a clean way to do that
var _this = this; var _this = this;
@ -130,15 +133,13 @@ Discourse.ComposerView = Discourse.View.extend({
}).observes('content.composeState'), }).observes('content.composeState'),
keyUp: function(e) { keyUp: function(e) {
var controller; var controller = this.get('controller');
controller = this.get('controller');
controller.checkReplyLength(); controller.checkReplyLength();
if (e.which === 27) controller.hitEsc(); if (e.which === 27) controller.hitEsc();
}, },
didInsertElement: function() { didInsertElement: function() {
var replyControl; var replyControl = $('#reply-control');
replyControl = $('#reply-control');
replyControl.DivResizer({ replyControl.DivResizer({
resize: this.resize, resize: this.resize,
onDrag: this.moveNewUserEducation onDrag: this.moveNewUserEducation

@ -267,10 +267,11 @@ en:
saving_draft_tip: "saving" saving_draft_tip: "saving"
saved_draft_tip: "saved" saved_draft_tip: "saved"
saved_local_draft_tip: "saved locally" saved_local_draft_tip: "saved locally"
drafts_offline: "drafts offline"
min_length: min_length:
at_least: "enter at least {{n}} characters" need_more_for_title: "{{n}} to go for the title"
more: "{{n}} to go..." need_more_for_reply: "{{n}} to go for the reply"
save_edit: "Save Edit" save_edit: "Save Edit"
reply_original: "Reply on Original Topic" reply_original: "Reply on Original Topic"

@ -271,10 +271,11 @@ fr:
saving_draft_tip: "sauvegarde..." saving_draft_tip: "sauvegarde..."
saved_draft_tip: "sauvegardé" saved_draft_tip: "sauvegardé"
saved_local_draft_tip: "sauvegardé en local" saved_local_draft_tip: "sauvegardé en local"
drafts_offline: "sauvegardé hors ligne"
min_length: min_length:
at_least: "Saisir au moins {{n}} caractères" need_more_for_title: "{{n}} caractères restant pour le titre"
more: "{{n}} restants..." need_more_for_reply: "{{n}} caractères restant pour le message"
save_edit: "Sauvegarder la modification" save_edit: "Sauvegarder la modification"
reply_original: Répondre à la discussion initiale reply_original: Répondre à la discussion initiale