Skip to content

Commit

Permalink
Replaced fine uploader by uppy. Refactored things.
Browse files Browse the repository at this point in the history
  • Loading branch information
Fensterbank committed Oct 31, 2019
1 parent 1cbb3f9 commit dabd4cb
Show file tree
Hide file tree
Showing 26 changed files with 479 additions and 251 deletions.
1 change: 0 additions & 1 deletion Gemfile
Expand Up @@ -116,7 +116,6 @@ source "https://rails-assets.org" do
gem "rails-assets-bootstrap-markdown", "2.10.0"
gem "rails-assets-corejs-typeahead", "1.2.1"
gem "rails-assets-cropperjs", "1.4.3"
gem "rails-assets-fine-uploader", "5.13.0"
gem "rails-assets-pica", "5.0.0"

# jQuery plugins
Expand Down
2 changes: 0 additions & 2 deletions Gemfile.lock
Expand Up @@ -537,7 +537,6 @@ GEM
rails-assets-corejs-typeahead (1.2.1)
rails-assets-jquery (>= 1.11)
rails-assets-cropperjs (1.4.3)
rails-assets-fine-uploader (5.13.0)
rails-assets-highlightjs (9.12.0)
rails-assets-jasmine (3.4.0)
rails-assets-jasmine-ajax (4.0.0)
Expand Down Expand Up @@ -863,7 +862,6 @@ DEPENDENCIES
rails-assets-bootstrap-markdown (= 2.10.0)!
rails-assets-corejs-typeahead (= 1.2.1)!
rails-assets-cropperjs (= 1.4.3)!
rails-assets-fine-uploader (= 5.13.0)!
rails-assets-highlightjs (= 9.12.0)!
rails-assets-jasmine-ajax (= 4.0.0)!
rails-assets-jquery (= 3.4.1)!
Expand Down
8 changes: 4 additions & 4 deletions app/assets/javascripts/app/views/publisher/uploader_view.js
Expand Up @@ -38,8 +38,8 @@ app.views.PublisherUploader = Backbone.View.extend({
},

progressHandler: function(id, fileName, progress) {
this.publisher.photozoneEl
.find("li.loading#upload-" + id + " .progress-bar")
$(document.getElementById("upload-" + id))
.find(".progress-bar")
.width(progress + "%");
},

Expand Down Expand Up @@ -71,7 +71,7 @@ app.views.PublisherUploader = Backbone.View.extend({
"<input type=\"hidden\", value=\"" + photoId + "\" name=\"photos[]\" />"
);
// replace placeholder
var placeholder = publisher.photozoneEl.find("li.loading#upload-" + id);
var placeholder = $(document.getElementById("upload-" + id));

var imgElement = document.createElement("img");
imgElement.src = image.thumb_medium.url;
Expand Down Expand Up @@ -100,7 +100,7 @@ app.views.PublisherUploader = Backbone.View.extend({
},

_cancelPhotoUpload: function(id) {
this.publisher.photozoneEl.find("li.loading#upload-" + id).remove();
$(document.getElementById("upload-" + id)).remove();
},

// remove an already uploaded photo
Expand Down
100 changes: 100 additions & 0 deletions app/assets/javascripts/helpers/ResizerPlugin.es6
@@ -0,0 +1,100 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
/**
* Custom resizer plugin for Uppy. Resizes image files if necessary using Pica image processing library.
*/
class Resizer extends Uppy.Core.Plugin {
constructor(uppy, options) {
super(uppy, options);
this.id = options.id || 'Resizer';
this.type = 'modifier';
this.maxSize = options.maxSize;
this.maxFileSize = options.maxFileSize;

this.prepareUpload = this.prepareUpload.bind(this);
this.resize = this.resize.bind(this);
this.pica = window.pica();
}

/**
* Resizes the file if necessary based on conditions.
* If the file exceeds the file size limit it will either be resized to maximum dimensions or only recompressed as JPEG (e.g. for huge PNG files not exceeding the dimension limit)
* @param file The uppy file
*/
resize(file) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = URL.createObjectURL(file.data);
const canvas = document.createElement('canvas');

img.onload = () => {
if (img.width > this.maxSize || img.height > this.maxSize) {
this.uppy.log(`[Resize] ${file.name}: Image dimensions ${img.width}x${img.height} are greater than max size ${this.maxSize}. Resizing image...`);
let ratio;

if (img.height > img.width) {
ratio = img.width / img.height;
canvas.width = this.maxSize * ratio;
canvas.height = this.maxSize;
} else {
ratio = img.height / img.width;
canvas.width = this.maxSize;
canvas.height = this.maxSize * ratio;
}
this.uppy.log(`[Resize] ${file.name}: New image dimensions: ${canvas.width}x${canvas.height}...`);
} else {
this.uppy.log(`[Resize] ${file.name}: Image dimensions ${img.width}x${img.height} are smaller than max size ${this.maxSize}. Saving to JPEG without resizing...`);
canvas.width = img.width;
canvas.height = img.height;
}

this.pica.resize(img, canvas)
.then(result => this.pica.toBlob(result, 'image/jpeg', 0.85))
.then(blob => resolve(blob));
}
});
}

prepareUpload(fileIDs) {
const promises = fileIDs.map((fileID) => {
const file = this.uppy.getFile(fileID);
if (file.type.split('/')[0] !== 'image' || (file.size <= this.maxFileSize)) {
return;
}
this.uppy.log(`[Resize] ${file.name}: File size ${file.size} exceeds max file size of ${this.maxFileSize}.`);

return this.resize(file).then((blob) => {
// since size and eventually type are changing, we have to overwrite several redundant information in the file object
const filename = file.name.replace(/\.png$/i, '.jpg')
const processedFile = {
...file,
data: blob,
size: blob.size,
progress: { ...file.progress, bytesTotal: blob.size },
size: blob.size,
type: blob.type,
name: filename,
extension: file.extension === 'png' ? 'jpg' : file.extension,
meta: {
...file.meta,
filename: filename,
name: filename,
totalfilesize: blob.size,
type: blob.type,
}
}
this.uppy.log(`[Resize] ${filename}: New file size: ${processedFile.data.size}.`);
this.uppy.setFileState(fileID, processedFile);
});
})
return Promise.all(promises);
}

install() {
this.uppy.addPreProcessor(this.prepareUpload);
}

uninstall() {
this.uppy.removePreProcessor(this.prepareUpload)
}
}
// @license-end
130 changes: 72 additions & 58 deletions app/assets/javascripts/helpers/post_photo_uploader.es6
Expand Up @@ -8,11 +8,13 @@ Diaspora.PostPhotoUploader = class {
constructor(el, aspectIds) {
this.element = el;
this.sizeLimit = 4194304;
this.allowedExtensions = [".jpg", ".jpeg", ".png", ".gif"];
this.aspectIds = aspectIds;

this.onProgress = null;
this.onUploadStarted = null;
this.onUploadCompleted = null;
this.uppy = null;

/**
* Shows a message using flash messages or alert for mobile.
Expand All @@ -28,76 +30,88 @@ Diaspora.PostPhotoUploader = class {
*/
this.func = param => (typeof param === "function");

this.initFineUploader();
this.initUppy();
}

/**
* Initializes the fine uploader component
* Initializes uppy
*/
initFineUploader() {
this.fineUploader = new qq.FineUploaderBasic({
element: this.element,
button: this.element,
text: {
fileInputTitle: Diaspora.I18n.t("photo_uploader.upload_photos")
initUppy() {
this.uppy = new window.Uppy.Core({
id: "post-photo-uppy",
autoProceed: true,
allowMultipleUploads: true,
restrictions: {
maxFileSize: (window.Promise ? null : this.sizeLimit),
allowedFileTypes: this.allowedExtensions
},
request: {
endpoint: Routes.photos(),
params: {
/* eslint-disable camelcase */
authenticity_token: $("meta[name='csrf-token']").attr("content"),
photo: {
pending: true,
aspect_ids: this.aspectIds
}
/* eslint-enable camelcase */
locale: {
strings: {
exceedsSize: Diaspora.I18n.t("photo_uploader.size_error"),
youCanOnlyUploadFileTypes: Diaspora.I18n.t("photo_uploader.invalid_ext").replace("{extensions}", "%{types}")
}
},
validation: {
allowedExtensions: ["jpg", "jpeg", "png", "gif"],
sizeLimit: (window.Promise && qq.supportedFeatures.scaling ? null : this.sizeLimit)
},
messages: {
typeError: Diaspora.I18n.t("photo_uploader.invalid_ext"),
sizeError: Diaspora.I18n.t("photo_uploader.size_error"),
emptyError: Diaspora.I18n.t("photo_uploader.empty")
},
callbacks: {
onSubmit: (id, name) => this.onPictureSelected(id, name),
onUpload: (id, name) => (this.func(this.onUploadStarted) && this.onUploadStarted(id, name)),
onProgress: (id, fileName, loaded, total) =>
(this.func(this.onProgress) && this.onProgress(id, fileName, Math.round(loaded / total * 100))),
onComplete: (id, name, json) => (this.func(this.onUploadCompleted) && this.onUploadCompleted(id, name, json)),
onError: (id, name, errorReason) => this.showMessage("error", errorReason)
}
});
}

/**
* Called when a picture from user's device has been selected.
* Scales the images using Pica if the image exceeds the file size limit
* @param {number} id - The current file's id.
* @param {string} name - The current file's name.
*/
onPictureSelected(id, name) {
// scale image because it's bigger than the size limit and the browser supports it
if (this.fineUploader.getSize(id) > this.sizeLimit && window.Promise && qq.supportedFeatures.scaling) {
this.fineUploader.scaleImage(id, {
maxSize: 3072,
customResizer: !qq.ios() && (i => window.pica().resize(i.sourceCanvas, i.targetCanvas))
}).then(scaledImage => {
this.fineUploader.addFiles({
blob: scaledImage,
name: name
});
const fileInput = $(".uppy-file-picker");
fileInput.attr("accept", this.allowedExtensions.join(","));

fileInput.on("change", (event) => {
const files = Array.from(event.target.files);

files.forEach((file) => {
try {
this.uppy.addFile({
source: "file input",
name: file.name,
type: file.type,
data: file
});
} catch (err) {
this.showMessage("error", err.message.replace("{file}", file.name).replace("{sizeLimit}.", ""));
}
});
});

this.uppy.setMeta({
/* eslint-disable camelcase */
authenticity_token: $("meta[name='csrf-token']").attr("content"),
"photo[pending]": true,
"photo[aspect_ids]": this.aspectIds
/* eslint-enable camelcase */
});

// since we are adding the smaller scaled image afterwards, we return false
return false;
}
this.uppy.use(window.Uppy.XHRUpload, {
endpoint: Routes.photos(),
fieldName: "file",
limit: 10
});

this.uppy.use(window.Resizer, {
maxSize: 3072,
maxFileSize: this.sizeLimit
});

this.uppy.on("file-added", (file) => {
this.uppy.setFileMeta(file.id, {
totalfilesize: file.size,
filename: file.name
});
});

this.uppy.on("upload", (data) =>
this.func(this.onUploadStarted) && data.fileIDs.forEach(fileID => this.onUploadStarted(fileID))
);

this.uppy.on("upload-progress", (file, progress) => this.func(this.onProgress)
&& this.onProgress(file.id, file.name, Math.round(progress.bytesUploaded / progress.bytesTotal * 100))
);

this.uppy.on("upload-success", (file, response) => {
this.func(this.onUploadCompleted) && this.onUploadCompleted(file.id, file.name, response.body);
});

// return true to upload the image without scaling
return true;
this.uppy.on("upload-error", (file, error) => this.showMessage("error", error.message));
}
};
// @license-end

0 comments on commit dabd4cb

Please sign in to comment.