Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replaced FineUploader by Uppy library. Refactored things. #8071

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
103 changes: 103 additions & 0 deletions app/assets/javascripts/helpers/ResizerPlugin.es6
@@ -0,0 +1,103 @@
// @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.
*/
window.Resizer = class extends window.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) => {
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 Promise.resolve();
}
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 = Object.assign({}, file, {
data: blob,
size: blob.size,
progress: Object.assign({}, file.progress, {bytesTotal: blob.size}),
type: blob.type,
name: filename,
extension: file.extension === "png" ? "jpg" : file.extension,
meta: Object.assign({}, 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);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon.

// @license-end
129 changes: 72 additions & 57 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,89 @@ 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)
});

const fileInput = $(".uppy-file-picker");
fileInput.attr("accept", this.allowedExtensions.join(","));

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

for (let i = 0; i < files.length; i++) {
let file = files[i];
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}.", ""));
}
}
});
}

/**
* 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
});
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 */
});

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))
);

// since we are adding the smaller scaled image afterwards, we return false
return false;
}
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