From 72989dca7c4355b661c54ae56b6b0313c87e15b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9gis=20Hanol?= <regis@hanol.fr>
Date: Mon, 1 Apr 2013 03:19:21 +0200
Subject: [PATCH] improved images upload on the client side

---
 .../templates/composer.js.handlebars          | 10 +-
 .../discourse/views/composer_view.js          | 94 +++++++++++--------
 .../stylesheets/application/compose.css.scss  | 17 ++--
 .../application/topic-post.css.scss           |  4 +
 config/locales/client.en.yml                  |  1 +
 config/locales/client.fr.yml                  |  1 +
 6 files changed, 76 insertions(+), 51 deletions(-)

diff --git a/app/assets/javascripts/discourse/templates/composer.js.handlebars b/app/assets/javascripts/discourse/templates/composer.js.handlebars
index 7ad7b464e..cfae2f1f1 100644
--- a/app/assets/javascripts/discourse/templates/composer.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/composer.js.handlebars
@@ -51,6 +51,11 @@
             {{#if Discourse.currentUser}}
                <a href="#" {{action togglePreview target="controller"}} class='toggle-preview'>{{{content.toggleText}}}</a>
                <div class='saving-draft'></div>
+               {{#if view.loadingImage}}
+                <div id="image-uploading">
+                  {{i18n image_selector.uploading_image}} {{view.uploadProgress}}% <a {{action cancelUpload target="view"}}>{{i18n cancel}}</a>
+                </div>
+              {{/if}}
             {{/if}}
           </div>
 
@@ -58,11 +63,6 @@
             <div class='submit-panel'>
               <button {{action save target="controller"}} tabindex="4" {{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>
-                </div>
-              {{/if}}
             </div>
           {{/if}}
 
diff --git a/app/assets/javascripts/discourse/views/composer_view.js b/app/assets/javascripts/discourse/views/composer_view.js
index 179dcabc1..0eb07b7d0 100644
--- a/app/assets/javascripts/discourse/views/composer_view.js
+++ b/app/assets/javascripts/discourse/views/composer_view.js
@@ -271,47 +271,65 @@ Discourse.ComposerView = Discourse.View.extend({
 
     // In case it's still bound somehow
     $uploadTarget.fileupload('destroy');
+    $uploadTarget.off();
 
     $uploadTarget.fileupload({
-      url: '/uploads',
-      dataType: 'json',
-      timeout: 20000,
-      formData: {
-        topic_id: 1234
-      },
-      paste: function(e, data) {
-        if (data.files.length > 0) {
-          _this.set('loadingImage', true);
-          _this.set('uploadProgress', 0);
-        }
-        return true;
-      },
-      drop: function(e, data) {
-        if (e.originalEvent.dataTransfer.files.length === 1) {
-          _this.set('loadingImage', true);
-          return _this.set('uploadProgress', 0);
-        }
-      },
-      progressall: function(e, data) {
-        var progress;
-        progress = parseInt(data.loaded / data.total * 100, 10);
-        return _this.set('uploadProgress', progress);
-      },
-      done: function(e, data) {
-        var html, upload;
-        _this.set('loadingImage', false);
-        upload = data.result;
-        html = "<img src=\"" + upload.url + "\" width=\"" + upload.width + "\" height=\"" + upload.height + "\">";
-        return _this.addMarkdown(html);
-      },
-      fail: function(e, data) {
-        // 413 == entity too large, returned usually from nginx
-        if(data.jqXHR && data.jqXHR.status === 413) {
-          bootbox.alert(Em.String.i18n('post.errors.upload_too_large', {max_size_kb: Discourse.SiteSettings.max_upload_size_kb}));
+        url: '/uploads',
+        dataType: 'json',
+        timeout: 20000,
+        formData: { topic_id: 1234 }
+    });
+
+    var addImages = function (e, data) {
+      console.log('addImages');
+      // can only upload one image at a time
+      if (data.files.length > 1) {
+        bootbox.alert(Em.String.i18n('post.errors.upload_too_many_images'));
+      } else if (data.files.length > 0) {
+        // check image size
+        var fileSizeInKB = data.files[0].size / 1024;
+        if (fileSizeInKB > Discourse.SiteSettings.max_upload_size_kb) {
+          bootbox.alert(Em.String.i18n('post.errors.upload_too_large', { max_size_kb: Discourse.SiteSettings.max_upload_size_kb }));
         } else {
-          bootbox.alert(Em.String.i18n('post.errors.upload'));
+          // reset upload status
+          _this.setProperties({
+            uploadProgress: 0,
+            loadingImage: true
+          });
+          return true;
         }
-        return _this.set('loadingImage', false);
+      }
+      return false;
+    };
+
+    // paste
+    $uploadTarget.on('fileuploadpaste', addImages);
+
+    // drop
+    $uploadTarget.on('fileuploaddrop', addImages);
+
+    // progress all
+    $uploadTarget.on('fileuploadprogressall', function (e, data) {
+      var progress = parseInt(data.loaded / data.total * 100, 10);
+      _this.set('uploadProgress', progress);
+    });
+
+    // done
+    $uploadTarget.on('fileuploaddone', function (e, data) {
+      var upload = data.result;
+      var html = "<img src=\"" + upload.url + "\" width=\"" + upload.width + "\" height=\"" + upload.height + "\">";
+      _this.addMarkdown(html);
+      _this.set('loadingImage', false);
+    });
+
+    // fail
+    $uploadTarget.on('fileuploadfail', function (e, data) {
+      _this.set('loadingImage', false);
+      // 413 == entity too large, returned usually from nginx
+      if(data.jqXHR && data.jqXHR.status === 413) {
+        bootbox.alert(Em.String.i18n('post.errors.upload_too_large', {max_size_kb: Discourse.SiteSettings.max_upload_size_kb}));
+      } else {
+        bootbox.alert(Em.String.i18n('post.errors.upload'));
       }
     });
 
@@ -372,5 +390,3 @@ Discourse.NotifyingTextArea = Ember.TextArea.extend({
 });
 
 RSVP.EventTarget.mixin(Discourse.ComposerView);
-
-
diff --git a/app/assets/stylesheets/application/compose.css.scss b/app/assets/stylesheets/application/compose.css.scss
index 46a9dbed4..36d15f407 100644
--- a/app/assets/stylesheets/application/compose.css.scss
+++ b/app/assets/stylesheets/application/compose.css.scss
@@ -55,7 +55,7 @@
 }
 
 #reply-control {
-  .toggle-preview, .saving-draft {
+  .toggle-preview, .saving-draft, #image-uploading {
     position: absolute;
     bottom: -31px;
     margin-top: 0px;
@@ -64,6 +64,11 @@
     right: 5px;
     text-decoration: underline;
   }
+  #image-uploading {
+    left: 51%;
+    font-size: 12px;
+    color: darken($gray, 40);
+  }
   .saving-draft {
     right: 51%;
     color: lighten($black, 60);
@@ -250,6 +255,10 @@
       img {
         // Otherwise we get the wrong size in JS
         max-width: none;
+        border-radius: 4px;
+        moz-border-radius: 4px;
+        webkit-border-radius: 4px;
+        ms-border-radius: 4px;
       }
     }
     #wmd-preview {
@@ -271,12 +280,6 @@
       position: absolute;
       display: block;
       bottom: 8px;
-      #image-uploading {
-        display: inline-block;
-        margin-left: 330px;
-        font-size: 12px;
-        color: darken($gray, 40);
-      }
     }
   }
 }
diff --git a/app/assets/stylesheets/application/topic-post.css.scss b/app/assets/stylesheets/application/topic-post.css.scss
index e2442b6f7..8192a7e2f 100644
--- a/app/assets/stylesheets/application/topic-post.css.scss
+++ b/app/assets/stylesheets/application/topic-post.css.scss
@@ -526,6 +526,10 @@
     }
     img {
       max-width: 100%;
+      border-radius: 4px;
+      webkit-border-radius: 4px;
+      ms-border-radius: 4px;
+      moz-border-radius: 4px;
     }
     .topic-body {
       position: relative;
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index d5554a37f..a32ac1d6a 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -550,6 +550,7 @@ en:
         edit: "Sorry, there was an error editing your post. Please try again."
         upload: "Sorry, there was an error uploading that file. Please try again."
         upload_too_large: "Sorry, the file you are trying to upload is too big (maximum size is {{max_size_kb}}kb), please resize it and try again."
+        upload_too_many_images: "Sorry, but you can only upload one image at a time."
 
       abandon: "Are you sure you want to abandon your post?"
 
diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml
index 5f406212b..3f78f0a78 100644
--- a/config/locales/client.fr.yml
+++ b/config/locales/client.fr.yml
@@ -552,6 +552,7 @@ fr:
         edit: "Désolé, il y a eu une erreur lors de l'édition de votre message. Merci de réessayer."
         upload: "Désolé, il y a eu une erreur lors de l'envoi du fichier. Merci de réessayer."
         upload_too_large: "Désolé, le fichier que vous êtes en train d'envoyer est trop grand (maximum {{max_size_kb}}Kb). Merci de le redimensionner et de réessayer."
+        upload_too_many_images: "Désolé, mais vous ne pouvez envoyer qu'une seule image à la fois."
 
       abandon: "Voulez-vous vraiment abandonner ce message ?"