compose: Migrate upload banner to new banner styling.

Fixes #22524.

This affects both the banner in the main compose box and the banner
in the message edit compose box. The use of ProgressBar has been
replaced with a more simple CSS (with light Javascript) solution.

The classnames are changing because the upload banner is now a
template rendered and remove()-ed from a banner container
(#compose_banners in the composebox, and a new div for banners in the
message edit view). It used to be in the send_status container so
there are a lot of class renames across the codebase.
This commit is contained in:
evykassirer
2023-01-08 22:43:39 -08:00
committed by Tim Abbott
parent b01ac3623f
commit 97d355fa72
8 changed files with 232 additions and 84 deletions

View File

@@ -18,7 +18,6 @@ mock_esm("@uppy/core", {
}, },
}); });
mock_esm("@uppy/xhr-upload", {default: class XHRUpload {}}); mock_esm("@uppy/xhr-upload", {default: class XHRUpload {}});
mock_esm("@uppy/progress-bar", {default: class ProgressBar {}});
const compose_actions = mock_esm("../../static/js/compose_actions"); const compose_actions = mock_esm("../../static/js/compose_actions");
mock_esm("../../static/js/csrf", {csrf_token: "csrf_token"}); mock_esm("../../static/js/csrf", {csrf_token: "csrf_token"});
@@ -29,7 +28,7 @@ const upload = zrequire("upload");
function test(label, f) { function test(label, f) {
run_test(label, (helpers) => { run_test(label, (helpers) => {
page_params.max_file_upload_size_mib = 25; page_params.max_file_upload_size_mib = 25;
f(helpers); return f(helpers);
}); });
} }
@@ -48,7 +47,11 @@ test("get_item", () => {
assert.equal(upload.get_item("textarea", {mode: "compose"}), $("#compose-textarea")); assert.equal(upload.get_item("textarea", {mode: "compose"}), $("#compose-textarea"));
assert.equal( assert.equal(
upload.get_item("send_status_message", {mode: "compose"}), upload.get_item("send_status_message", {mode: "compose"}),
$("#compose-error-msg"), $("#compose_banners .upload_banner .upload_msg"),
);
assert.equal(
upload.get_item("send_status_close_button", {mode: "compose"}),
$("#compose_banners .upload_banner .compose_banner_close_button"),
); );
assert.equal( assert.equal(
upload.get_item("file_input_identifier", {mode: "compose"}), upload.get_item("file_input_identifier", {mode: "compose"}),
@@ -74,27 +77,30 @@ test("get_item", () => {
assert.equal( assert.equal(
upload.get_item("send_status_identifier", {mode: "edit", row: 11}), upload.get_item("send_status_identifier", {mode: "edit", row: 11}),
`#message-edit-send-status-${CSS.escape(11)}`, `#edit_form_${CSS.escape(11)} .upload_banner`,
); );
assert.equal( assert.equal(
upload.get_item("send_status", {mode: "edit", row: 75}), upload.get_item("send_status", {mode: "edit", row: 75}),
$(`#message-edit-send-status-${CSS.escape(75)}`), $(`#edit_form_${CSS.escape(75)} .upload_banner`),
); );
$(`#message-edit-send-status-${CSS.escape(2)}`).set_find_results( $(`#edit_form_${CSS.escape(2)} .upload_banner`).set_find_results(
".send-status-close", ".compose_banner_close_button",
$(".send-status-close"), $(".compose_banner_close_button"),
); );
assert.equal( assert.equal(
upload.get_item("send_status_close_button", {mode: "edit", row: 2}), upload.get_item("send_status_close_button", {mode: "edit", row: 2}),
$(".send-status-close"), $(`#edit_form_${CSS.escape(2)} .upload_banner .compose_banner_close_button`),
); );
$(`#message-edit-send-status-${CSS.escape(22)}`).set_find_results( $(`#edit_form_${CSS.escape(22)} .upload_banner`).set_find_results(
".error-msg", ".upload_msg",
$(".error-msg"), $(".upload_msg"),
);
assert.equal(
upload.get_item("send_status_message", {mode: "edit", row: 22}),
$(`#edit_form_${CSS.escape(22)} .upload_banner .upload_msg`),
); );
assert.equal(upload.get_item("send_status_message", {mode: "edit", row: 22}), $(".error-msg"));
assert.equal( assert.equal(
upload.get_item("file_input_identifier", {mode: "edit", row: 123}), upload.get_item("file_input_identifier", {mode: "edit", row: 123}),
@@ -158,34 +164,49 @@ test("get_item", () => {
}); });
test("hide_upload_status", () => { test("hide_upload_status", () => {
let banner_removed = false;
$("#compose_banners .upload_banner").remove = () => {
banner_removed = true;
};
$("#compose-send-button").prop("disabled", true); $("#compose-send-button").prop("disabled", true);
$("#compose-send-status").addClass("alert-info").show();
upload.hide_upload_status({mode: "compose"}); upload.hide_upload_status({mode: "compose"});
assert.ok(banner_removed);
assert.equal($("#compose-send-button").prop("disabled"), false); assert.equal($("#compose-send-button").prop("disabled"), false);
assert.equal($("#compose-send-button").hasClass("alert-info"), false);
assert.equal($("#compose-send-button").visible(), false); assert.equal($("#compose-send-button").visible(), false);
}); });
test("show_error_message", () => { test("show_error_message", ({mock_template}) => {
$("#compose_banners .upload_banner .moving_bar").css = () => {};
$("#compose_banners .upload_banner").length = 0;
let banner_shown = false;
mock_template("compose_banner/upload_banner.hbs", false, (data) => {
assert.equal(data.banner_type, "error");
assert.equal(data.banner_text, "Error message");
banner_shown = true;
});
$("#compose-send-button").prop("disabled", true); $("#compose-send-button").prop("disabled", true);
$("#compose-send-status").addClass("alert-info").removeClass("alert-error").hide();
$("#compose-error-msg").text("");
$("#compose-error-msg").hide();
upload.show_error_message({mode: "compose"}, "Error message"); upload.show_error_message({mode: "compose"}, "Error message");
assert.equal($("#compose-send-button").prop("disabled"), false); assert.equal($("#compose-send-button").prop("disabled"), false);
assert.ok($("#compose-send-status").hasClass("alert-error")); assert.ok(banner_shown);
assert.equal($("#compose-send-status").hasClass("alert-info"), false);
assert.ok($("#compose-send-status").visible());
assert.equal($("#compose-error-msg").text(), "Error message");
mock_template("compose_banner/upload_banner.hbs", false, (data) => {
assert.equal(data.banner_type, "error");
assert.equal(data.banner_text, "translated: An unknown error occurred.");
banner_shown = true;
});
upload.show_error_message({mode: "compose"}); upload.show_error_message({mode: "compose"});
assert.equal($("#compose-error-msg").text(), "translated: An unknown error occurred.");
}); });
test("upload_files", ({override_rewire}) => { test("upload_files", async ({mock_template, override_rewire}) => {
$("#compose_banners .upload_banner").remove = () => {};
$("#compose_banners .upload_banner .moving_bar").css = () => {};
$("#compose_banners .upload_banner").length = 0;
let uppy_cancel_all_called = false; let uppy_cancel_all_called = false;
let files = [ let files = [
{ {
@@ -214,20 +235,26 @@ test("upload_files", ({override_rewire}) => {
}); });
const config = {mode: "compose"}; const config = {mode: "compose"};
$("#compose-send-button").prop("disabled", false); $("#compose-send-button").prop("disabled", false);
upload.upload_files(uppy, config, []); await upload.upload_files(uppy, config, []);
assert.ok(!$("#compose-send-button").prop("disabled")); assert.ok(!$("#compose-send-button").prop("disabled"));
let banner_shown = false;
mock_template("compose_banner/upload_banner.hbs", false, (data) => {
assert.equal(data.banner_type, "error");
assert.equal(
data.banner_text,
"translated: File and image uploads have been disabled for this organization.",
);
banner_shown = true;
});
page_params.max_file_upload_size_mib = 0; page_params.max_file_upload_size_mib = 0;
$("#compose-error-msg").text(""); $("#compose_banners .upload_banner .upload_msg").text("");
upload.upload_files(uppy, config, files); await upload.upload_files(uppy, config, files);
assert.equal( assert.ok(banner_shown);
$("#compose-error-msg").text(),
"translated: File and image uploads have been disabled for this organization.",
);
page_params.max_file_upload_size_mib = 25; page_params.max_file_upload_size_mib = 25;
let on_click_close_button_callback; let on_click_close_button_callback;
$(".compose-send-status-close").one = (event, callback) => { $("#compose_banners .upload_banner .compose_banner_close_button").one = (event, callback) => {
assert.equal(event, "click"); assert.equal(event, "click");
on_click_close_button_callback = callback; on_click_close_button_callback = callback;
}; };
@@ -246,18 +273,24 @@ test("upload_files", ({override_rewire}) => {
markdown_preview_hide_button_clicked = true; markdown_preview_hide_button_clicked = true;
}); });
$("#compose-send-button").prop("disabled", false); $("#compose-send-button").prop("disabled", false);
$("#compose-send-status").removeClass("alert-info").hide(); $("#compose_banners .upload_banner").remove();
$("#compose .undo_markdown_preview").show(); $("#compose .undo_markdown_preview").show();
upload.upload_files(uppy, config, files);
banner_shown = false;
mock_template("compose_banner/upload_banner.hbs", false, (data) => {
assert.equal(data.banner_type, "info");
assert.equal(data.banner_text, "translated: Uploading…");
banner_shown = true;
});
await upload.upload_files(uppy, config, files);
assert.ok($("#compose-send-button").prop("disabled")); assert.ok($("#compose-send-button").prop("disabled"));
assert.ok($("#compose-send-status").hasClass("alert-info")); assert.ok(banner_shown);
assert.ok($("#compose-send-status").visible());
assert.equal($("<p>").text(), "translated: Uploading…");
assert.ok(compose_ui_insert_syntax_and_focus_called); assert.ok(compose_ui_insert_syntax_and_focus_called);
assert.ok(compose_ui_autosize_textarea_called); assert.ok(compose_ui_autosize_textarea_called);
assert.ok(markdown_preview_hide_button_clicked); assert.ok(markdown_preview_hide_button_clicked);
assert.ok(uppy_add_file_called); assert.ok(uppy_add_file_called);
banner_shown = false;
files = [ files = [
{ {
name: "budapest.png", name: "budapest.png",
@@ -274,7 +307,8 @@ test("upload_files", ({override_rewire}) => {
add_file_counter += 1; add_file_counter += 1;
throw new Error("some error"); throw new Error("some error");
}; };
upload.upload_files(uppy, config, files); await upload.upload_files(uppy, config, files);
assert.ok(banner_shown);
assert.equal(add_file_counter, 1); assert.equal(add_file_counter, 1);
hide_upload_status_called = false; hide_upload_status_called = false;
@@ -311,7 +345,6 @@ test("uppy_config", () => {
let uppy_stub_called = false; let uppy_stub_called = false;
let uppy_set_meta_called = false; let uppy_set_meta_called = false;
let uppy_used_xhrupload = false; let uppy_used_xhrupload = false;
let uppy_used_progressbar = false;
uppy_stub = function (config) { uppy_stub = function (config) {
uppy_stub_called = true; uppy_stub_called = true;
@@ -336,10 +369,6 @@ test("uppy_config", () => {
assert.equal(params.limit, 5); assert.equal(params.limit, 5);
assert.equal(Object.keys(params.locale.strings).length, 1); assert.equal(Object.keys(params.locale.strings).length, 1);
assert.ok("timedOut" in params.locale.strings); assert.ok("timedOut" in params.locale.strings);
} else if (func_name === "ProgressBar") {
uppy_used_progressbar = true;
assert.equal(params.target, "#compose-send-status");
assert.equal(params.hideAfterFinish, false);
} else { } else {
/* istanbul ignore next */ /* istanbul ignore next */
assert.fail(`Missing tests for ${func_name}`); assert.fail(`Missing tests for ${func_name}`);
@@ -353,7 +382,6 @@ test("uppy_config", () => {
assert.equal(uppy_stub_called, true); assert.equal(uppy_stub_called, true);
assert.equal(uppy_set_meta_called, true); assert.equal(uppy_set_meta_called, true);
assert.equal(uppy_used_xhrupload, true); assert.equal(uppy_used_xhrupload, true);
assert.equal(uppy_used_progressbar, true);
}); });
test("file_input", ({override_rewire}) => { test("file_input", ({override_rewire}) => {
@@ -454,7 +482,10 @@ test("copy_paste", ({override_rewire}) => {
assert.equal(upload_files_called, false); assert.equal(upload_files_called, false);
}); });
test("uppy_events", ({override, override_rewire}) => { test("uppy_events", ({override, override_rewire, mock_template}) => {
$("#compose_banners .upload_banner .moving_bar").css = () => {};
$("#compose_banners .upload_banner").length = 0;
const callbacks = {}; const callbacks = {};
let uppy_cancel_all_called = false; let uppy_cancel_all_called = false;
let state = {}; let state = {};
@@ -486,7 +517,7 @@ test("uppy_events", ({override, override_rewire}) => {
}; };
}; };
upload.setup_upload({mode: "compose"}); upload.setup_upload({mode: "compose"});
assert.equal(Object.keys(callbacks).length, 5); assert.equal(Object.keys(callbacks).length, 6);
const on_upload_success_callback = callbacks["upload-success"]; const on_upload_success_callback = callbacks["upload-success"];
const file = { const file = {
@@ -541,7 +572,7 @@ test("uppy_events", ({override, override_rewire}) => {
override_rewire(upload, "hide_upload_status", () => { override_rewire(upload, "hide_upload_status", () => {
hide_upload_status_called = true; hide_upload_status_called = true;
}); });
$("#compose-send-status").removeClass("alert-error"); $("#compose_banner .upload_banner").removeClass("error");
files = [ files = [
{ {
id: "uppy-zulip/jpeg-1e-image/jpeg-163515-1578367331279", id: "uppy-zulip/jpeg-1e-image/jpeg-163515-1578367331279",
@@ -561,11 +592,11 @@ test("uppy_events", ({override, override_rewire}) => {
assert.equal(files.length, 0); assert.equal(files.length, 0);
hide_upload_status_called = false; hide_upload_status_called = false;
$("#compose-send-status").addClass("alert-error"); $("#compose_banners .upload_banner").addClass("error");
on_complete_callback(); on_complete_callback();
assert.ok(!hide_upload_status_called); assert.ok(!hide_upload_status_called);
$("#compose-send-status").removeClass("alert-error"); $("#compose_banners .upload_banner").removeClass("error");
hide_upload_status_called = false; hide_upload_status_called = false;
files = [ files = [
{ {
@@ -585,19 +616,22 @@ test("uppy_events", ({override, override_rewire}) => {
assert.ok(!hide_upload_status_called); assert.ok(!hide_upload_status_called);
assert.equal(files.length, 1); assert.equal(files.length, 1);
mock_template("compose_banner/upload_banner.hbs", false, (data) => {
assert.equal(data.banner_type, "error");
assert.equal(data.banner_text, "Some error message");
});
state = { state = {
type: "error", type: "error",
details: "Some error", details: "Some error",
message: "Some error message", message: "Some error message",
}; };
const on_info_visible_callback = callbacks["info-visible"]; const on_info_visible_callback = callbacks["info-visible"];
$("#compose-error-msg").text(""); $("#compose_banners .upload_banner .upload_msg").text("");
uppy_cancel_all_called = false; uppy_cancel_all_called = false;
compose_ui_replace_syntax_called = false; compose_ui_replace_syntax_called = false;
const on_restriction_failed_callback = callbacks["restriction-failed"]; const on_restriction_failed_callback = callbacks["restriction-failed"];
on_info_visible_callback(); on_info_visible_callback();
assert.ok(uppy_cancel_all_called); assert.ok(uppy_cancel_all_called);
assert.equal($("#compose-error-msg").text(), "Some error message");
override_rewire(compose_ui, "replace_syntax", (old_syntax, new_syntax, textarea) => { override_rewire(compose_ui, "replace_syntax", (old_syntax, new_syntax, textarea) => {
compose_ui_replace_syntax_called = true; compose_ui_replace_syntax_called = true;
assert.equal(old_syntax, "[translated: Uploading copenhagen.png…]()"); assert.equal(old_syntax, "[translated: Uploading copenhagen.png…]()");
@@ -627,7 +661,11 @@ test("uppy_events", ({override, override_rewire}) => {
on_info_visible_callback(); on_info_visible_callback();
const on_upload_error_callback = callbacks["upload-error"]; const on_upload_error_callback = callbacks["upload-error"];
$("#compose-error-msg").text(""); $("#compose_banners .upload_banner .upload_msg").text("");
mock_template("compose_banner/upload_banner.hbs", false, (data) => {
assert.equal(data.banner_type, "error");
assert.equal(data.banner_text, "Response message");
});
compose_ui_replace_syntax_called = false; compose_ui_replace_syntax_called = false;
response = { response = {
body: { body: {
@@ -637,21 +675,22 @@ test("uppy_events", ({override, override_rewire}) => {
uppy_cancel_all_called = false; uppy_cancel_all_called = false;
on_upload_error_callback(file, null, response); on_upload_error_callback(file, null, response);
assert.ok(uppy_cancel_all_called); assert.ok(uppy_cancel_all_called);
assert.equal($("#compose-error-msg").text(), "Response message");
assert.ok(compose_ui_replace_syntax_called); assert.ok(compose_ui_replace_syntax_called);
mock_template("compose_banner/upload_banner.hbs", false, (data) => {
assert.equal(data.banner_type, "error");
assert.equal(data.banner_text, "translated: An unknown error occurred.");
});
compose_ui_replace_syntax_called = false; compose_ui_replace_syntax_called = false;
uppy_cancel_all_called = false; uppy_cancel_all_called = false;
on_upload_error_callback(file, null, null); on_upload_error_callback(file, null, null);
assert.ok(uppy_cancel_all_called); assert.ok(uppy_cancel_all_called);
assert.equal($("#compose-error-msg").text(), "translated: An unknown error occurred.");
assert.ok(compose_ui_replace_syntax_called); assert.ok(compose_ui_replace_syntax_called);
$("#compose-error-msg").text(""); $("#compose_banners .upload_banner .upload_msg").text("");
$("#compose-textarea").val("user modified text"); $("#compose-textarea").val("user modified text");
uppy_cancel_all_called = false; uppy_cancel_all_called = false;
on_upload_error_callback(file, null); on_upload_error_callback(file, null);
assert.ok(uppy_cancel_all_called); assert.ok(uppy_cancel_all_called);
assert.equal($("#compose-error-msg").text(), "translated: An unknown error occurred.");
assert.ok(compose_ui_replace_syntax_called); assert.ok(compose_ui_replace_syntax_called);
assert.equal($("#compose-textarea").val(), "user modified text"); assert.equal($("#compose-textarea").val(), "user modified text");
}); });

View File

@@ -229,6 +229,14 @@ function FakeElement(selector, opts) {
shown = show; shown = show;
return $self; return $self;
}, },
toggleClass(class_name, add) {
if (add) {
classes.set(class_name, true);
} else {
classes.delete(class_name);
}
return $self;
},
trigger(ev) { trigger(ev) {
event_store.trigger($self, ev); event_store.trigger($self, ev);
return $self; return $self;

View File

@@ -337,11 +337,6 @@ export function initialize() {
ui_util.blur_active_element(); ui_util.blur_active_element();
} }
}); });
$(".message_edit_form .send-status-close").on("click", function () {
const row_id = rows.id($(this).closest(".message_row"));
const $send_status = $(`#message-edit-send-status-${CSS.escape(row_id)}`);
$send_status.stop(true).fadeOut(200);
});
$("body").on("click", ".message_edit_form .compose_upload_file", function (e) { $("body").on("click", ".message_edit_form .compose_upload_file", function (e) {
e.preventDefault(); e.preventDefault();

View File

@@ -1,8 +1,9 @@
import {Uppy} from "@uppy/core"; import {Uppy} from "@uppy/core";
import ProgressBar from "@uppy/progress-bar";
import XHRUpload from "@uppy/xhr-upload"; import XHRUpload from "@uppy/xhr-upload";
import $ from "jquery"; import $ from "jquery";
import render_upload_banner from "../templates/compose_banner/upload_banner.hbs";
import * as compose_actions from "./compose_actions"; import * as compose_actions from "./compose_actions";
import * as compose_state from "./compose_state"; import * as compose_state from "./compose_state";
import * as compose_ui from "./compose_ui"; import * as compose_ui from "./compose_ui";
@@ -32,14 +33,16 @@ export function get_item(key, config) {
return $("#compose-textarea"); return $("#compose-textarea");
case "send_button": case "send_button":
return $("#compose-send-button"); return $("#compose-send-button");
case "banner_container":
return $("#compose_banners");
case "send_status_identifier": case "send_status_identifier":
return "#compose-send-status"; return "#compose_banners .upload_banner";
case "send_status": case "send_status":
return $("#compose-send-status"); return $("#compose_banners .upload_banner");
case "send_status_close_button": case "send_status_close_button":
return $(".compose-send-status-close"); return $("#compose_banners .upload_banner .compose_banner_close_button");
case "send_status_message": case "send_status_message":
return $("#compose-error-msg"); return $("#compose_banners .upload_banner .upload_msg");
case "file_input_identifier": case "file_input_identifier":
return "#compose .file_input"; return "#compose .file_input";
case "source": case "source":
@@ -62,16 +65,20 @@ export function get_item(key, config) {
return $(`#edit_form_${CSS.escape(config.row)} .message_edit_content`) return $(`#edit_form_${CSS.escape(config.row)} .message_edit_content`)
.closest(".message_edit_form") .closest(".message_edit_form")
.find(".message_edit_save"); .find(".message_edit_save");
case "banner_container":
return $(`#edit_form_${CSS.escape(config.row)} .banners`);
case "send_status_identifier": case "send_status_identifier":
return `#message-edit-send-status-${CSS.escape(config.row)}`; return `#edit_form_${CSS.escape(config.row)} .upload_banner`;
case "send_status": case "send_status":
return $(`#message-edit-send-status-${CSS.escape(config.row)}`); return $(`#edit_form_${CSS.escape(config.row)} .upload_banner`);
case "send_status_close_button": case "send_status_close_button":
return $(`#message-edit-send-status-${CSS.escape(config.row)}`).find( return $(
".send-status-close", `#edit_form_${CSS.escape(
config.row,
)} .upload_banner .compose_banner_close_button`,
); );
case "send_status_message": case "send_status_message":
return $(`#message-edit-send-status-${CSS.escape(config.row)}`).find(".error-msg"); return $(`#edit_form_${CSS.escape(config.row)} .upload_banner .upload_msg`);
case "file_input_identifier": case "file_input_identifier":
return `#edit_form_${CSS.escape(config.row)} .file_input`; return `#edit_form_${CSS.escape(config.row)} .file_input`;
case "source": case "source":
@@ -90,7 +97,36 @@ export function get_item(key, config) {
export function hide_upload_status(config) { export function hide_upload_status(config) {
get_item("send_button", config).prop("disabled", false); get_item("send_button", config).prop("disabled", false);
get_item("send_status", config).removeClass("alert-info").hide(); get_item("send_status", config).remove();
}
function show_upload_banner(config, banner_type, banner_text) {
// We only show one upload banner at a time per compose box,
// and all uploads are combined into the same progress bar.
// TODO: It would be nice to separate the error banner into
// a different element, so that we can show it at the same
// time as the upload bar and other uploads can still continue
// when an error occurs.
const $upload_banner = get_item("send_status", config);
if ($upload_banner.length) {
if (banner_type === "error") {
// Hide moving bar so that it doesn't do the 1s transition to 0
const $moving_bar = $(`${get_item("send_status_identifier", config)} .moving_bar`);
$moving_bar.hide();
$upload_banner.removeClass("info").addClass("error");
// Show it again once the animation is complete.
setTimeout(() => $moving_bar.show(), 1000);
} else {
$upload_banner.removeClass("error").addClass("info");
}
get_item("send_status_message", config).text(banner_text);
return;
}
const $new_banner = render_upload_banner({
banner_type,
banner_text,
});
get_item("banner_container", config).append($new_banner);
} }
export function show_error_message( export function show_error_message(
@@ -98,11 +134,10 @@ export function show_error_message(
message = $t({defaultMessage: "An unknown error occurred."}), message = $t({defaultMessage: "An unknown error occurred."}),
) { ) {
get_item("send_button", config).prop("disabled", false); get_item("send_button", config).prop("disabled", false);
get_item("send_status", config).addClass("alert-error").removeClass("alert-info").show(); show_upload_banner(config, "error", message);
get_item("send_status_message", config).text(message);
} }
export function upload_files(uppy, config, files) { export async function upload_files(uppy, config, files) {
if (files.length === 0) { if (files.length === 0) {
return; return;
} }
@@ -128,8 +163,7 @@ export function upload_files(uppy, config, files) {
} }
get_item("send_button", config).prop("disabled", true); get_item("send_button", config).prop("disabled", true);
get_item("send_status", config).addClass("alert-info").removeClass("alert-error").show(); show_upload_banner(config, "info", $t({defaultMessage: "Uploading…"}));
get_item("send_status_message", config).html($("<p>").text($t({defaultMessage: "Uploading…"})));
get_item("send_status_close_button", config).one("click", () => { get_item("send_status_close_button", config).one("click", () => {
for (const file of uppy.getFiles()) { for (const file of uppy.getFiles()) {
compose_ui.replace_syntax( compose_ui.replace_syntax(
@@ -280,7 +314,7 @@ export function setup_upload(config) {
} }
} }
const has_errors = get_item("send_status", config).hasClass("alert-error"); const has_errors = get_item("send_status", config).hasClass("error");
if (!uploads_in_progress && !has_errors) { if (!uploads_in_progress && !has_errors) {
// Hide upload status for 100ms after the 1s transition to 100% // Hide upload status for 100ms after the 1s transition to 100%
// so that the user can see the progress bar at 100%. // so that the user can see the progress bar at 100%.
@@ -314,6 +348,8 @@ export function setup_upload(config) {
if (info.type === "error") { if (info.type === "error") {
// The remaining errors are mostly frontend errors like file being too large // The remaining errors are mostly frontend errors like file being too large
// for upload. // for upload.
// TODO: It would be nice to keep the other uploads going if one fails,
// and show both an error message and the upload bar.
uppy.cancelAll(); uppy.cancelAll();
show_error_message(config, info.message); show_error_message(config, info.message);
} }

View File

@@ -399,10 +399,53 @@
} }
} }
} }
&.info {
background-color: hsl(204, 58%, 92%);
border-color: hsla(204, 49%, 29%, 0.4);
position: relative;
color: hsl(204, 49%, 29%);
.compose_banner_close_button {
color: hsla(204, 49%, 29%, 0.5);
&:hover {
color: hsl(204, 49%, 29%);
}
&:active {
color: hsla(204, 49%, 29%, 0.75);
}
}
}
}
.upload_banner {
overflow: hidden;
&.hidden {
display: none;
}
.moving_bar {
position: absolute;
width: 0;
/* The progress updates seem to come every second or so,
so this is the smoothest it can probably get. */
transition: width 1s ease-in-out;
background: hsl(204, 63%, 85%);
top: 0;
bottom: 0;
}
.upload_msg,
.compose_banner_close_button {
z-index: 1;
position: relative;
}
} }
/* Like .nav-tabs > li > a */ /* Like .nav-tabs > li > a */
div[id^="message-edit-send-status"],
#compose-send-status { #compose-send-status {
padding: 8px 14px; padding: 8px 14px;
margin-bottom: 8px; margin-bottom: 8px;

View File

@@ -251,6 +251,31 @@
} }
} }
} }
&.info {
background-color: hsl(204, 100%, 12%);
border-color: hsla(205, 58%, 69%, 0.4);
position: relative;
color: hsl(205, 58%, 69%);
.compose_banner_close_button {
color: hsla(205, 58%, 69%, 0.5);
&:hover {
color: hsl(205, 58%, 69%);
}
&:active {
color: hsla(205, 58%, 69%, 0.75);
}
}
}
}
.upload_banner {
.moving_bar {
background: hsl(204, 63%, 18%);
}
} }
.message_embed .data-container::after { .message_embed .data-container::after {

View File

@@ -0,0 +1,5 @@
<div class="upload_banner compose_banner {{banner_type}}">
<div class="moving_bar"></div>
<p class="upload_msg">{{banner_text}}</p>
<a role="button" class="zulip-icon zulip-icon-close compose_banner_close_button"></a>
</div>

View File

@@ -1,10 +1,7 @@
{{! Client-side Mustache template for rendering the message edit form. }} {{! Client-side Mustache template for rendering the message edit form. }}
<form id="edit_form_{{message_id}}" class="new-style"> <form id="edit_form_{{message_id}}" class="new-style">
<div class="alert" id="message-edit-send-status-{{message_id}}"> <div class="banners"></div>
<span class="send-status-close">&times;</span>
<span class="error-msg"></span>
</div>
<div class="edit-controls"> <div class="edit-controls">
{{> copy_message_button message_id=this.message_id}} {{> copy_message_button message_id=this.message_id}}
<textarea class="message_edit_content" maxlength="{{ max_message_length }}">{{content}}</textarea> <textarea class="message_edit_content" maxlength="{{ max_message_length }}">{{content}}</textarea>