FEATURE: use native file picker in composer (#13552)

We want to remove completely our custom modal for uploading files in composer and directly trigger the system file picker.

This PR makes it happen. The fix is pretty simple since we already weren't using our custom modal on mobile. We just need to start using the same hidden <input type="file"> that we already use on mobile.

It seems to be pretty tricky to test opening a system modal so I haven't added new tests. We already have other tests for file uploading though. We directly trigger jquery-File-Upload plugin hooks in those tests - 3dda926cb2/app/assets/javascripts/discourse/tests/acceptance/composer-attachment-test.js (L89).
This commit is contained in:
Andrei Prigorshnev 2021-06-30 12:45:47 +04:00 committed by GitHub
parent b63c9febe8
commit a2e0da16a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 14 additions and 240 deletions

View File

@ -831,10 +831,12 @@ export default Component.extend({
});
if (this.site.mobileView) {
$("#reply-control .mobile-file-upload").on("click.uploader", function () {
// redirect the click on the hidden file input
$("#mobile-uploader").click();
});
const uploadButton = document.getElementById("mobile-file-upload");
uploadButton.addEventListener(
"click",
() => document.getElementById("file-uploader").click(),
false
);
}
},

View File

@ -1,69 +0,0 @@
import {
allowsAttachments,
authorizedExtensions,
uploadIcon,
} from "discourse/lib/uploads";
import Controller from "@ember/controller";
import I18n from "I18n";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import discourseComputed from "discourse-common/utils/decorators";
import { equal } from "@ember/object/computed";
export default Controller.extend(ModalFunctionality, {
imageUrl: null,
local: equal("selection", "local"),
remote: equal("selection", "remote"),
selection: "local",
@discourseComputed()
allowAdditionalFormats() {
return allowsAttachments(this.currentUser.staff, this.siteSettings);
},
@discourseComputed()
uploadIcon() {
return uploadIcon(this.currentUser.staff, this.siteSettings);
},
@discourseComputed("allowAdditionalFormats")
title(allowAdditionalFormats) {
const suffix = allowAdditionalFormats ? "_with_attachments" : "";
return `upload_selector.title${suffix}`;
},
@discourseComputed("selection", "allowAdditionalFormats")
tip(selection, allowAdditionalFormats) {
const suffix = allowAdditionalFormats ? "_with_attachments" : "";
return I18n.t(`upload_selector.${selection}_tip${suffix}`);
},
@discourseComputed()
supportedFormats() {
const extensions = authorizedExtensions(
this.currentUser.staff,
this.siteSettings
);
return `(${extensions})`;
},
actions: {
upload() {
if (this.local) {
$(".wmd-controls").fileupload("add", {
fileInput: $("#filename-input"),
});
} else {
const imageUrl = this.imageUrl || "";
const toolbarEvent = this.toolbarEvent;
if (imageUrl.match(/\.(jpg|jpeg|png|gif|heic|heif|webp)$/)) {
toolbarEvent.addText(`![](${imageUrl})`);
} else {
toolbarEvent.addText(imageUrl);
}
}
this.send("closeModal");
},
},
});

View File

@ -137,11 +137,8 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
showModal("not-activated", { title: "log_in" }).setProperties(props);
},
showUploadSelector(toolbarEvent) {
showModal("uploadSelector").setProperties({
toolbarEvent,
imageUrl: null,
});
showUploadSelector() {
document.getElementById("file-uploader").click();
},
showKeyboardShortcutsHelp() {

View File

@ -19,6 +19,4 @@
disabled=disableTextarea
outletArgs=(hash composer=composer editorType="composer")}}
{{#if site.mobileView}}
<input type="file" id="mobile-uploader" multiple>
{{/if}}
<input type="file" id="file-uploader" multiple>

View File

@ -200,7 +200,7 @@
{{plugin-outlet name="composer-mobile-buttons-bottom" connectorTagName="" args=(hash model=model)}}
{{#if allowUpload}}
<a class="btn btn-default no-text mobile-file-upload {{if isUploading "hidden"}}" aria-label={{i18n "composer.upload_title"}}>
<a id="mobile-file-upload" class="btn btn-default no-text mobile-file-upload {{if isUploading "hidden"}}" aria-label={{i18n "composer.upload_title"}}>
{{d-icon uploadIcon}}
</a>
{{/if}}

View File

@ -1,45 +0,0 @@
{{#d-modal-body title=title class="upload-selector"}}
<div class="radios">
{{radio-button name="upload" id="local" value="local" selection=selection}}
<label class="radio" for="local">{{i18n "upload_selector.from_my_computer"}}</label>
{{#if local}}
<div class="inputs">
<input type="file" id="filename-input" multiple><br>
<span class="description">{{tip}}</span>
{{#if allowAdditionalFormats}}
{{hidden-details label="upload_selector.supported_formats" details=supportedFormats}}
{{/if}}
</div>
{{/if}}
</div>
<div class="radios">
{{radio-button name="upload" id="remote" value="remote" selection=selection}}
<label class="radio" for="remote">{{i18n "upload_selector.from_the_web"}}</label>
{{#if remote}}
<div class="inputs">
{{input value=imageUrl placeholder="http://example.com/image.png"}}
<span class="description">{{tip}}</span>
{{#if allowAdditionalFormats}}
{{hidden-details label="upload_selector.supported_formats" details=supportedFormats}}
{{/if}}
</div>
{{/if}}
</div>
<div class="radios">
<div class="inputs">
<p class="hint">
{{#if capabilities.canPasteImages}}
{{i18n "upload_selector.hint"}}
{{else}}
{{i18n "upload_selector.hint_for_supported_browsers"}}
{{/if}}
</p>
</div>
</div>
{{/d-modal-body}}
<div class="modal-footer">
{{d-button action=(action "upload") class="btn-primary" icon=uploadIcon label="upload"}}
{{plugin-outlet name="upload-actions"}}
{{d-modal-cancel close=(route-action "closeModal")}}
</div>

View File

@ -362,6 +362,10 @@
#file-uploading {
color: var(--primary-high);
}
#file-uploader {
display: none;
}
}
.autocomplete {

View File

@ -1,49 +1,3 @@
.upload-selector {
label {
display: inline-block;
padding-left: 10px;
}
&.modal-body {
width: 460px;
}
.radios {
min-height: 60px;
display: flex;
align-items: flex-start;
label {
flex: 1 0 auto;
margin-right: 1em;
margin-top: 1px;
}
.inputs {
width: 100%;
input {
width: 90%;
margin: 0 0 5px 0;
}
input[type="file"] {
font-size: $font-0;
line-height: $line-height-medium;
}
.description,
.hint {
color: var(--primary-med-or-secondary-med);
display: block;
}
.hint {
font-style: italic;
margin: 5px 0 0 0;
}
.label {
margin: 0 0 5px 0;
}
}
}
.radios:last-child {
min-height: 20px;
}
}
.uploaded-image-preview {
height: 270px;
width: 400px;

View File

@ -171,10 +171,6 @@
}
}
#mobile-uploader {
display: none;
}
.title-and-category,
.user-selector {
margin: 0;

View File

@ -1,37 +1,3 @@
.upload-selector {
input[type="text"] {
width: calc(100% - 20px);
}
input[type="file"] {
font-size: $font-0;
line-height: $line-height-medium;
}
.description {
color: var(--primary-medium);
}
.radios {
display: flex;
flex-wrap: wrap;
align-items: center;
&:first-of-type {
margin-bottom: 1em;
}
input,
label {
min-height: 20px;
line-height: $line-height-medium;
margin: 0;
}
.radio {
padding-left: 5px;
}
.inputs {
margin-top: 10px;
width: 100%;
}
}
}
.uploaded-image-preview {
height: 150px;
}

View File

@ -2236,21 +2236,10 @@ en:
votes_released: "Vote was released"
upload_selector:
title: "Add an image"
title_with_attachments: "Add an image or a file"
from_my_computer: "From my device"
from_the_web: "From the web"
remote_tip: "link to image"
remote_tip_with_attachments: "link to image or file"
local_tip: "select images from your device"
local_tip_with_attachments: "select images or files from your device"
hint: "(you can also drag & drop into the editor to upload)"
hint_for_supported_browsers: "you can also drag and drop or paste images into the editor"
uploading: "Uploading"
processing: "Processing Upload"
select_file: "Select File"
default_image_alt_text: image
supported_formats: "supported formats"
search:
sort_by: "Sort by"

View File

@ -222,24 +222,6 @@ const path = require("path");
return page.waitForSelector(".d-editor-preview p", { visible: true });
});
await exec("open upload modal", () => {
return page.click(".d-editor-button-bar .upload");
});
await exec("upload modal is open", () => {
return page.waitForSelector("#filename-input", { visible: true });
});
await exec("upload modal closes", () => {
let promise = page.click(".d-modal-cancel");
promise = promise.then(() => {
return page.waitForSelector("#filename-input", { hidden: true });
});
return promise;
});
await exec("submit the topic", () => {
return page.click(".submit-panel .create");
});