From aef014f4399d7df80856460d187a1b87a447522f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= <regis@hanol.fr> Date: Fri, 15 Mar 2013 12:56:14 +0100 Subject: [PATCH] displays the number of characters left when editing the topic title --- .../controllers/composer_controller.js | 20 +++---- .../javascripts/discourse/models/composer.js | 60 ++++++++++--------- .../templates/composer.js.handlebars | 4 -- .../discourse/views/composer_view.js | 17 +++--- config/locales/client.en.yml | 5 +- config/locales/client.fr.yml | 5 +- 6 files changed, 54 insertions(+), 57 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/composer_controller.js b/app/assets/javascripts/discourse/controllers/composer_controller.js index 6ef3ad662..477675d83 100644 --- a/app/assets/javascripts/discourse/controllers/composer_controller.js +++ b/app/assets/javascripts/discourse/controllers/composer_controller.js @@ -19,12 +19,13 @@ Discourse.ComposerController = Discourse.Controller.extend({ return this.get('content').importQuote(); }, + resetDraftStatus: function() { + this.get('content').resetDraftStatus(); + }, + appendText: function(text) { - var c; - c = this.get('content'); - if (c) { - return c.appendText(text); - } + var c = this.get('content'); + if (c) return c.appendText(text); }, save: function(force) { @@ -95,16 +96,11 @@ Discourse.ComposerController = Discourse.Controller.extend({ }, checkReplyLength: function() { - if (this.present('content.reply')) { - this.set('hasReply', true); - } else { - this.set('hasReply', false); - } + this.set('hasReply', this.present('content.reply')); }, saveDraft: function() { - var model; - model = this.get('content'); + var model = this.get('content'); if (model) model.saveDraft(); }, diff --git a/app/assets/javascripts/discourse/models/composer.js b/app/assets/javascripts/discourse/models/composer.js index e265bd92e..da44a86d4 100644 --- a/app/assets/javascripts/discourse/models/composer.js +++ b/app/assets/javascripts/discourse/models/composer.js @@ -28,7 +28,7 @@ Discourse.Composer = Discourse.Model.extend({ this._super(); var val = Discourse.KeyValueStore.get('composer.showPreview') || '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() { @@ -142,10 +142,7 @@ Discourse.Composer = Discourse.Model.extend({ }).property('action', 'post', 'topic', 'topic.title'), toggleText: (function() { - if (this.get('showPreview')) { - return Em.String.i18n('composer.hide_preview'); - } - return Em.String.i18n('composer.show_preview'); + return this.get('showPreview') ? Em.String.i18n('composer.hide_preview') : Em.String.i18n('composer.show_preview'); }).property('showPreview'), hidePreview: (function() { @@ -159,14 +156,11 @@ Discourse.Composer = Discourse.Model.extend({ if (this.get('loading')) return true; // Title is required on new posts - if (this.get('creatingTopic')) { - if (this.blank('title')) return true; - if (this.get('title').trim().length < Discourse.SiteSettings.min_topic_title_length) return true; - } + if (this.get('creatingTopic') && this.get('titleLength') < Discourse.SiteSettings.min_topic_title_length) return true; // Otherwise just reply is required - if (this.blank('reply')) return true; - if (this.get('reply').trim().length < Discourse.SiteSettings.min_post_length) return true; + if (this.get('replyLength') < Discourse.SiteSettings.min_post_length) return true; + return false; }).property('reply', 'title', 'creatingTopic', 'loading'), @@ -438,6 +432,7 @@ Discourse.Composer = Discourse.Model.extend({ saveDraft: function() { if (this.get('disableDrafts')) 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; var data = { @@ -457,28 +452,38 @@ Discourse.Composer = Discourse.Model.extend({ return Discourse.Draft.save(this.get('draftKey'), this.get('draftSequence'), data).then((function() { composer.set('draftStatus', Em.String.i18n('composer.saved_draft_tip')); }), (function() { - composer.set('draftStatus', 'drafts offline'); + composer.set('draftStatus', Em.String.i18n('composer.drafts_offline')); })); }, resetDraftStatus: (function() { - var len = Discourse.SiteSettings.min_post_length, - replyLength = this.get('replyLength'); - - if (replyLength === 0) { - this.set('draftStatus', Em.String.i18n('composer.min_length.at_least', { n: len })); - } else if (replyLength < len) { - this.set('draftStatus', Em.String.i18n('composer.min_length.more', { n: len - replyLength })); - } else { - this.set('draftStatus', null); + // 'title' is focused + if ($('#reply-title').is(':focus')) { + var titleDiff = Discourse.SiteSettings.min_topic_title_length - this.get('titleLength'); + if (titleDiff > 0) { + return this.set('draftStatus', Em.String.i18n('composer.min_length.need_more_for_title', { n: titleDiff })); + } + // 'reply' is focused + } else if ($('#wmd-input').is(':focus')) { + 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'), - blank: function(prop) { - var p = this.get(prop); - return !(p && p.length > 0); - }, + /** + Computes the length of the title minus non-significant whitespaces + + @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 @@ -486,8 +491,7 @@ Discourse.Composer = Discourse.Model.extend({ @property replyLength **/ replyLength: function() { - var reply = this.get('reply'); - if(!reply) reply = ""; + var reply = this.get('reply') || ""; while (Discourse.BBCode.QUOTE_REGEXP.test(reply)) { reply = reply.replace(Discourse.BBCode.QUOTE_REGEXP, ""); } return reply.replace(/\s+/img, " ").trim().length; }.property('reply') @@ -547,5 +551,3 @@ Discourse.Composer.reopenClass({ // Draft key REPLY_AS_NEW_TOPIC_KEY: REPLY_AS_NEW_TOPIC_KEY }); - - diff --git a/app/assets/javascripts/discourse/templates/composer.js.handlebars b/app/assets/javascripts/discourse/templates/composer.js.handlebars index 55180cf3d..547edf0a6 100644 --- a/app/assets/javascripts/discourse/templates/composer.js.handlebars +++ b/app/assets/javascripts/discourse/templates/composer.js.handlebars @@ -17,7 +17,6 @@ {{#if content.editTitle}} <div class='form-element clearfix'> - {{#if content.creatingPrivateMessage}} {{view Discourse.TextField id="private-message-users" class="span8" placeholderKey="composer.users_placeholder"}} {{/if}} @@ -31,7 +30,6 @@ </div> {{/if}} - <div class='wmd-controls'> <div class='textarea-wrapper'> <div class='wmd-button-bar' id='wmd-button-bar'></div> @@ -43,7 +41,6 @@ {{#if Discourse.currentUser}} <a href="#" {{action togglePreview target="controller"}} class='toggle-preview'>{{{content.toggleText}}}</a> <div class='saving-draft'></div> - {{/if}} </div> @@ -51,7 +48,6 @@ <div class='submit-panel'> <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> - {{#if view.loadingImage}} <div id="image-uploading"> {{i18n image_selector.uploading_image}} {{view.uploadProgress}}% <a {{action cancelUpload target="view"}}>{{i18n cancel}}</a> diff --git a/app/assets/javascripts/discourse/views/composer_view.js b/app/assets/javascripts/discourse/views/composer_view.js index 8860b0911..6c503199b 100644 --- a/app/assets/javascripts/discourse/views/composer_view.js +++ b/app/assets/javascripts/discourse/views/composer_view.js @@ -22,8 +22,7 @@ Discourse.ComposerView = Discourse.View.extend({ educationClosed: null, composeState: (function() { - var state; - state = this.get('content.composeState'); + var state = this.get('content.composeState'); if (!state) { state = Discourse.Composer.CLOSED; } @@ -103,8 +102,7 @@ Discourse.ComposerView = Discourse.View.extend({ }).property('content.composeState', 'content.reply', 'educationClosed', 'educationContents'), newUserEducationVisibilityChanged: (function() { - var $panel; - $panel = $('#new-user-education'); + var $panel = $('#new-user-education'); if (this.get('newUserEducationVisible')) { return $panel.slideDown('fast'); } else { @@ -116,6 +114,11 @@ Discourse.ComposerView = Discourse.View.extend({ $('#new-user-education').css('bottom', sizePx); }, + focusIn: (function() { + var controller = this.get('controller'); + if(controller) controller.resetDraftStatus(); + }), + resize: (function() { // this still needs to wait on animations, need a clean way to do that var _this = this; @@ -130,15 +133,13 @@ Discourse.ComposerView = Discourse.View.extend({ }).observes('content.composeState'), keyUp: function(e) { - var controller; - controller = this.get('controller'); + var controller = this.get('controller'); controller.checkReplyLength(); if (e.which === 27) controller.hitEsc(); }, didInsertElement: function() { - var replyControl; - replyControl = $('#reply-control'); + var replyControl = $('#reply-control'); replyControl.DivResizer({ resize: this.resize, onDrag: this.moveNewUserEducation diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index d599f2f29..e1ae3ff49 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -267,10 +267,11 @@ en: saving_draft_tip: "saving" saved_draft_tip: "saved" saved_local_draft_tip: "saved locally" + drafts_offline: "drafts offline" min_length: - at_least: "enter at least {{n}} characters" - more: "{{n}} to go..." + need_more_for_title: "{{n}} to go for the title" + need_more_for_reply: "{{n}} to go for the reply" save_edit: "Save Edit" reply_original: "Reply on Original Topic" diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index 8b8a29874..1fa144a2e 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -271,10 +271,11 @@ fr: saving_draft_tip: "sauvegarde..." saved_draft_tip: "sauvegardé" saved_local_draft_tip: "sauvegardé en local" + drafts_offline: "sauvegardé hors ligne" min_length: - at_least: "Saisir au moins {{n}} caractères" - more: "{{n}} restants..." + need_more_for_title: "{{n}} caractères restant pour le titre" + need_more_for_reply: "{{n}} caractères restant pour le message" save_edit: "Sauvegarder la modification" reply_original: Répondre à la discussion initiale