FIX: keep files in order when adding multiple uploads (#6306)

* FIX: keep files in order when adding multiple uploads

* use filename in the placeholder when uploading files

* add tests

* add consecutive nr to the placeholder when multiple uploads with the same filename
This commit is contained in:
Maja Komel 2018-10-03 03:12:36 +02:00 committed by Sam
parent a6f0436a29
commit 73443d889c
3 changed files with 161 additions and 25 deletions

View File

@ -53,10 +53,15 @@ export default Ember.Component.extend({
_xhr: null,
shouldBuildScrollMap: true,
scrollMap: null,
uploadFilenamePlaceholder: null,
@computed
uploadPlaceholder() {
return `[${I18n.t("uploading")}]() `;
@computed("uploadFilenamePlaceholder")
uploadPlaceholder(uploadFilenamePlaceholder) {
const clipboard = I18n.t("clipboard");
const filename = uploadFilenamePlaceholder
? uploadFilenamePlaceholder
: clipboard;
return `[${I18n.t("uploading_filename", { filename })}]() `;
},
@computed("composer.requiredCategoryMissing")
@ -218,6 +223,53 @@ export default Ember.Component.extend({
}
},
_setUploadPlaceholderSend(data) {
const filename = this._filenamePlaceholder(data);
this.set("uploadFilenamePlaceholder", filename);
// when adding two separate files with the same filename search for matching
// placeholder already existing in the editor ie [Uploading: test.png...]
// and add order nr to the next one: [Uplodading: test.png(1)...]
const regexString = `\\[${I18n.t("uploading_filename", {
filename: filename + "(?:\\()?([0-9])?(?:\\))?"
})}\\]\\(\\)`;
const globalRegex = new RegExp(regexString, "g");
const matchingPlaceholder = this.get("composer.reply").match(globalRegex);
if (matchingPlaceholder) {
// get last matching placeholder and its consecutive nr in regex
// capturing group and apply +1 to the placeholder
const lastMatch = matchingPlaceholder[matchingPlaceholder.length - 1];
const regex = new RegExp(regexString);
const orderNr = regex.exec(lastMatch)[1]
? parseInt(regex.exec(lastMatch)[1]) + 1
: 1;
data.orderNr = orderNr;
const filenameWithOrderNr = `${filename}(${orderNr})`;
this.set("uploadFilenamePlaceholder", filenameWithOrderNr);
}
},
_setUploadPlaceholderDone(data) {
const filename = this._filenamePlaceholder(data);
const filenameWithSize = `${filename} (${data.total})`;
this.set("uploadFilenamePlaceholder", filenameWithSize);
if (data.orderNr) {
const filenameWithOrderNr = `${filename}(${data.orderNr})`;
this.set("uploadFilenamePlaceholder", filenameWithOrderNr);
} else {
this.set("uploadFilenamePlaceholder", filename);
}
},
_filenamePlaceholder(data) {
return data.files[0].name.replace(/\u200B-\u200D\uFEFF]/g, "");
},
_resetUploadFilenamePlaceholder() {
this.set("uploadFilenamePlaceholder", null);
},
_enableAdvancedEditorPreviewSync() {
return this.siteSettings.enable_advanced_editor_preview_sync;
},
@ -542,23 +594,26 @@ export default Ember.Component.extend({
},
_resetUpload(removePlaceholder) {
if (this._validUploads > 0) {
this._validUploads--;
}
if (this._validUploads === 0) {
this.setProperties({
uploadProgress: 0,
isUploading: false,
isCancellable: false
});
}
if (removePlaceholder) {
this.appEvents.trigger(
"composer:replace-text",
this.get("uploadPlaceholder"),
""
);
}
Ember.run.next(() => {
if (this._validUploads > 0) {
this._validUploads--;
}
if (this._validUploads === 0) {
this.setProperties({
uploadProgress: 0,
isUploading: false,
isCancellable: false
});
}
if (removePlaceholder) {
this.appEvents.trigger(
"composer:replace-text",
this.get("uploadPlaceholder"),
""
);
}
this._resetUploadFilenamePlaceholder();
});
},
_bindUploadTarget() {
@ -568,7 +623,6 @@ export default Ember.Component.extend({
const $element = this.$();
const csrf = this.session.get("csrfToken");
const uploadPlaceholder = this.get("uploadPlaceholder");
$element.fileupload({
url: Discourse.getURL(
@ -637,7 +691,13 @@ export default Ember.Component.extend({
$element.on("fileuploadsend", (e, data) => {
this._pasted = false;
this._validUploads++;
this.appEvents.trigger("composer:insert-text", uploadPlaceholder);
this._setUploadPlaceholderSend(data);
this.appEvents.trigger(
"composer:insert-text",
this.get("uploadPlaceholder")
);
if (data.xhr && data.originalFiles.length === 1) {
this.set("isCancellable", true);
@ -647,13 +707,13 @@ export default Ember.Component.extend({
$element.on("fileuploaddone", (e, data) => {
let upload = data.result;
this._setUploadPlaceholderDone(data);
if (!this._xhr || !this._xhr._userCancelled) {
const markdown = getUploadMarkdown(upload);
cacheShortUploadUrl(upload.short_url, upload.url);
this.appEvents.trigger(
"composer:replace-text",
uploadPlaceholder.trim(),
this.get("uploadPlaceholder").trim(),
markdown
);
this._resetUpload(false);
@ -663,6 +723,7 @@ export default Ember.Component.extend({
});
$element.on("fileuploadfail", (e, data) => {
this._setUploadPlaceholderDone(data);
this._resetUpload(true);
const userCancelled = this._xhr && this._xhr._userCancelled;

View File

@ -317,7 +317,8 @@ en:
upload: "Upload"
uploading: "Uploading..."
uploading_filename: "Uploading {{filename}}..."
uploading_filename: "Uploading: {{filename}}..."
clipboard: "clipboard"
uploaded: "Uploaded!"
pasting: "Pasting..."

View File

@ -89,6 +89,80 @@ QUnit.test("Tests the Composer controls", async assert => {
assert.ok(!exists(".bootbox.modal"), "the confirmation can be cancelled");
});
QUnit.test("Composer upload placeholder", async assert => {
await visit("/");
await click("#create-topic");
const file1 = new Blob([""], { type: "image/png" });
file1.name = "test.png";
const data1 = {
files: [file1],
result: {
original_filename: "test.png",
thumbnail_width: 200,
thumbnail_height: 300,
url: "/uploads/test1.ext"
}
};
const file2 = new Blob([""], { type: "image/png" });
file2.name = "test.png";
const data2 = {
files: [file2],
result: {
original_filename: "test.png",
thumbnail_width: 100,
thumbnail_height: 200,
url: "/uploads/test2.ext"
}
};
const file3 = new Blob([""], { type: "image/png" });
file3.name = "image.png";
const data3 = {
files: [file3],
result: {
original_filename: "image.png",
thumbnail_width: 300,
thumbnail_height: 400,
url: "/uploads/test3.ext"
}
};
await find(".wmd-controls").trigger("fileuploadsend", data1);
assert.equal(find(".d-editor-input").val(), "[Uploading: test.png...]() ");
await find(".wmd-controls").trigger("fileuploadsend", data2);
assert.equal(
find(".d-editor-input").val(),
"[Uploading: test.png...]() [Uploading: test.png(1)...]() "
);
await find(".wmd-controls").trigger("fileuploadsend", data3);
assert.equal(
find(".d-editor-input").val(),
"[Uploading: test.png...]() [Uploading: test.png(1)...]() [Uploading: image.png...]() "
);
await find(".wmd-controls").trigger("fileuploaddone", data2);
assert.equal(
find(".d-editor-input").val(),
"[Uploading: test.png...]() ![test|100x200](/uploads/test2.ext) [Uploading: image.png...]() "
);
await find(".wmd-controls").trigger("fileuploaddone", data3);
assert.equal(
find(".d-editor-input").val(),
"[Uploading: test.png...]() ![test|100x200](/uploads/test2.ext) ![image|300x400](/uploads/test3.ext) "
);
await find(".wmd-controls").trigger("fileuploaddone", data1);
assert.equal(
find(".d-editor-input").val(),
"![test|200x300](/uploads/test1.ext) ![test|100x200](/uploads/test2.ext) ![image|300x400](/uploads/test3.ext) "
);
});
QUnit.test("Create a topic with server side errors", async assert => {
await visit("/");
await click("#create-topic");