mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	import/order sorts require() calls as well as import statements. Signed-off-by: Anders Kaseorg <anders@zulip.com>
		
			
				
	
	
		
			314 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const Uppy = require("@uppy/core");
 | |
| const ProgressBar = require("@uppy/progress-bar");
 | |
| const XHRUpload = require("@uppy/xhr-upload");
 | |
| 
 | |
| exports.make_upload_absolute = function (uri) {
 | |
|     if (uri.startsWith(compose.uploads_path)) {
 | |
|         // Rewrite the URI to a usable link
 | |
|         return compose.uploads_domain + uri;
 | |
|     }
 | |
|     return uri;
 | |
| };
 | |
| 
 | |
| // Show the upload button only if the browser supports it.
 | |
| exports.feature_check = function (upload_button) {
 | |
|     if (window.XMLHttpRequest && new XMLHttpRequest().upload) {
 | |
|         upload_button.removeClass("notdisplayed");
 | |
|     }
 | |
| };
 | |
| exports.get_translated_status = function (file) {
 | |
|     const status = i18n.t("Uploading __filename__…", {filename: file.name});
 | |
|     return "[" + status + "]()";
 | |
| };
 | |
| 
 | |
| exports.get_item = function (key, config) {
 | |
|     if (!config) {
 | |
|         throw Error("Missing config");
 | |
|     }
 | |
|     if (config.mode === "compose") {
 | |
|         switch (key) {
 | |
|             case "textarea":
 | |
|                 return $("#compose-textarea");
 | |
|             case "send_button":
 | |
|                 return $("#compose-send-button");
 | |
|             case "send_status_identifier":
 | |
|                 return "#compose-send-status";
 | |
|             case "send_status":
 | |
|                 return $("#compose-send-status");
 | |
|             case "send_status_close_button":
 | |
|                 return $(".compose-send-status-close");
 | |
|             case "send_status_message":
 | |
|                 return $("#compose-error-msg");
 | |
|             case "file_input_identifier":
 | |
|                 return "#file_input";
 | |
|             case "source":
 | |
|                 return "compose-file-input";
 | |
|             case "drag_drop_container":
 | |
|                 return $("#compose");
 | |
|             default:
 | |
|                 throw Error(`Invalid key name for mode "${config.mode}"`);
 | |
|         }
 | |
|     } else if (config.mode === "edit") {
 | |
|         if (!config.row) {
 | |
|             throw Error("Missing row in config");
 | |
|         }
 | |
|         switch (key) {
 | |
|             case "textarea":
 | |
|                 return $("#message_edit_content_" + config.row);
 | |
|             case "send_button":
 | |
|                 return $("#message_edit_content_" + config.row)
 | |
|                     .closest("#message_edit_form")
 | |
|                     .find(".message_edit_save");
 | |
|             case "send_status_identifier":
 | |
|                 return "#message-edit-send-status-" + config.row;
 | |
|             case "send_status":
 | |
|                 return $("#message-edit-send-status-" + config.row);
 | |
|             case "send_status_close_button":
 | |
|                 return $("#message-edit-send-status-" + config.row).find(".send-status-close");
 | |
|             case "send_status_message":
 | |
|                 return $("#message-edit-send-status-" + config.row).find(".error-msg");
 | |
|             case "file_input_identifier":
 | |
|                 return "#message_edit_file_input_" + config.row;
 | |
|             case "source":
 | |
|                 return "message-edit-file-input";
 | |
|             case "drag_drop_container":
 | |
|                 return $("#message_edit_form");
 | |
|             default:
 | |
|                 throw Error(`Invalid key name for mode "${config.mode}"`);
 | |
|         }
 | |
|     } else {
 | |
|         throw Error("Invalid upload mode!");
 | |
|     }
 | |
| };
 | |
| 
 | |
| exports.hide_upload_status = function (config) {
 | |
|     exports.get_item("send_button", config).prop("disabled", false);
 | |
|     exports.get_item("send_status", config).removeClass("alert-info").hide();
 | |
| };
 | |
| 
 | |
| exports.show_error_message = function (config, message) {
 | |
|     if (!message) {
 | |
|         message = i18n.t("An unknown error occurred.");
 | |
|     }
 | |
|     exports.get_item("send_button", config).prop("disabled", false);
 | |
|     exports
 | |
|         .get_item("send_status", config)
 | |
|         .addClass("alert-error")
 | |
|         .removeClass("alert-info")
 | |
|         .show();
 | |
|     exports.get_item("send_status_message", config).text(message);
 | |
| };
 | |
| 
 | |
| exports.upload_files = function (uppy, config, files) {
 | |
|     if (files.length === 0) {
 | |
|         return;
 | |
|     }
 | |
|     if (page_params.max_file_upload_size_mib === 0) {
 | |
|         exports.show_error_message(
 | |
|             config,
 | |
|             i18n.t("File and image uploads have been disabled for this organization."),
 | |
|         );
 | |
|         return;
 | |
|     }
 | |
|     exports.get_item("send_button", config).prop("disabled", true);
 | |
|     exports
 | |
|         .get_item("send_status", config)
 | |
|         .addClass("alert-info")
 | |
|         .removeClass("alert-error")
 | |
|         .show();
 | |
|     exports.get_item("send_status_message", config).html($("<p>").text(i18n.t("Uploading…")));
 | |
|     exports.get_item("send_status_close_button", config).one("click", () => {
 | |
|         uppy.getFiles().forEach((file) => {
 | |
|             compose_ui.replace_syntax(
 | |
|                 exports.get_translated_status(file),
 | |
|                 "",
 | |
|                 exports.get_item("textarea", config),
 | |
|             );
 | |
|         });
 | |
|         compose_ui.autosize_textarea();
 | |
|         uppy.cancelAll();
 | |
|         exports.get_item("textarea", config).trigger("focus");
 | |
|         setTimeout(() => {
 | |
|             exports.hide_upload_status(config);
 | |
|         }, 500);
 | |
|     });
 | |
| 
 | |
|     for (const file of files) {
 | |
|         try {
 | |
|             compose_ui.insert_syntax_and_focus(
 | |
|                 exports.get_translated_status(file),
 | |
|                 exports.get_item("textarea", config),
 | |
|             );
 | |
|             compose_ui.autosize_textarea();
 | |
|             uppy.addFile({
 | |
|                 source: exports.get_item("source", config),
 | |
|                 name: file.name,
 | |
|                 type: file.type,
 | |
|                 data: file,
 | |
|             });
 | |
|         } catch (error) {
 | |
|             // Errors are handled by info-visible and upload-error event callbacks.
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| exports.setup_upload = function (config) {
 | |
|     const uppy = Uppy({
 | |
|         debug: false,
 | |
|         autoProceed: true,
 | |
|         restrictions: {
 | |
|             maxFileSize: page_params.max_file_upload_size_mib * 1024 * 1024,
 | |
|         },
 | |
|         locale: {
 | |
|             strings: {
 | |
|                 exceedsSize: i18n.t("This file exceeds maximum allowed size of"),
 | |
|                 failedToUpload: i18n.t("Failed to upload %{file}"),
 | |
|             },
 | |
|         },
 | |
|     });
 | |
|     uppy.setMeta({
 | |
|         csrfmiddlewaretoken: csrf_token,
 | |
|     });
 | |
|     uppy.use(XHRUpload, {
 | |
|         endpoint: "/json/user_uploads",
 | |
|         formData: true,
 | |
|         fieldName: "file",
 | |
|         // Number of concurrent uploads
 | |
|         limit: 5,
 | |
|         locale: {
 | |
|             strings: {
 | |
|                 timedOut: i18n.t("Upload stalled for %{seconds} seconds, aborting."),
 | |
|             },
 | |
|         },
 | |
|     });
 | |
| 
 | |
|     uppy.use(ProgressBar, {
 | |
|         target: exports.get_item("send_status_identifier", config),
 | |
|         hideAfterFinish: false,
 | |
|     });
 | |
| 
 | |
|     $("body").on("change", exports.get_item("file_input_identifier", config), (event) => {
 | |
|         const files = event.target.files;
 | |
|         exports.upload_files(uppy, config, files);
 | |
|         event.target.value = "";
 | |
|     });
 | |
| 
 | |
|     const drag_drop_container = exports.get_item("drag_drop_container", config);
 | |
|     drag_drop_container.on("dragover", (event) => event.preventDefault());
 | |
|     drag_drop_container.on("dragenter", (event) => event.preventDefault());
 | |
| 
 | |
|     drag_drop_container.on("drop", (event) => {
 | |
|         event.preventDefault();
 | |
|         const files = event.originalEvent.dataTransfer.files;
 | |
|         exports.upload_files(uppy, config, files);
 | |
|     });
 | |
| 
 | |
|     drag_drop_container.on("paste", (event) => {
 | |
|         const clipboard_data = event.clipboardData || event.originalEvent.clipboardData;
 | |
|         if (!clipboard_data) {
 | |
|             return;
 | |
|         }
 | |
|         const items = clipboard_data.items;
 | |
|         const files = [];
 | |
|         for (const item of items) {
 | |
|             if (item.kind !== "file") {
 | |
|                 continue;
 | |
|             }
 | |
|             const file = item.getAsFile();
 | |
|             files.push(file);
 | |
|         }
 | |
|         exports.upload_files(uppy, config, files);
 | |
|     });
 | |
| 
 | |
|     uppy.on("upload-success", (file, response) => {
 | |
|         const uri = response.body.uri;
 | |
|         if (uri === undefined) {
 | |
|             return;
 | |
|         }
 | |
|         const split_uri = uri.split("/");
 | |
|         const filename = split_uri[split_uri.length - 1];
 | |
|         if (config.mode === "compose" && !compose_state.composing()) {
 | |
|             compose_actions.start("stream");
 | |
|         }
 | |
|         const absolute_uri = exports.make_upload_absolute(uri);
 | |
|         const filename_uri = "[" + filename + "](" + absolute_uri + ")";
 | |
|         compose_ui.replace_syntax(
 | |
|             exports.get_translated_status(file),
 | |
|             filename_uri,
 | |
|             exports.get_item("textarea", config),
 | |
|         );
 | |
|         compose_ui.autosize_textarea();
 | |
|     });
 | |
| 
 | |
|     uppy.on("complete", () => {
 | |
|         let uploads_in_progress = false;
 | |
|         uppy.getFiles().forEach((file) => {
 | |
|             if (file.progress.uploadComplete) {
 | |
|                 // The uploaded files should be removed since uppy don't allow files in the store
 | |
|                 // to be re-uploaded again.
 | |
|                 uppy.removeFile(file.id);
 | |
|             } else {
 | |
|                 // Happens when user tries to upload files when there is already an existing batch
 | |
|                 // being uploaded. So when the first batch of files complete, the second batch would
 | |
|                 // still be in progress.
 | |
|                 uploads_in_progress = true;
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         const has_errors = exports.get_item("send_status", config).hasClass("alert-error");
 | |
|         if (!uploads_in_progress && !has_errors) {
 | |
|             setTimeout(() => {
 | |
|                 exports.hide_upload_status(config);
 | |
|             }, 500);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     uppy.on("info-visible", () => {
 | |
|         const info = uppy.getState().info;
 | |
|         if (info.type === "error" && info.message === "No Internet connection") {
 | |
|             // server_events already handles the case of no internet.
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (info.type === "error" && info.details === "Upload Error") {
 | |
|             // The server errors come under 'Upload Error'. But we can't handle them
 | |
|             // here because info object don't contain response.body.msg received from
 | |
|             // the server. Server errors are hence handled by on('upload-error').
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (info.type === "error") {
 | |
|             // The remaining errors are mostly frontend errors like file being too large
 | |
|             // for upload.
 | |
|             uppy.cancelAll();
 | |
|             exports.show_error_message(config, info.message);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     uppy.on("upload-error", (file, error, response) => {
 | |
|         const message = response ? response.body.msg : null;
 | |
|         uppy.cancelAll();
 | |
|         exports.show_error_message(config, message);
 | |
|         compose_ui.replace_syntax(
 | |
|             exports.get_translated_status(file),
 | |
|             "",
 | |
|             exports.get_item("textarea", config),
 | |
|         );
 | |
|         compose_ui.autosize_textarea();
 | |
|     });
 | |
| 
 | |
|     uppy.on("restriction-failed", (file) => {
 | |
|         compose_ui.replace_syntax(
 | |
|             exports.get_translated_status(file),
 | |
|             "",
 | |
|             exports.get_item("textarea", config),
 | |
|         );
 | |
|         compose_ui.autosize_textarea();
 | |
|     });
 | |
| 
 | |
|     return uppy;
 | |
| };
 | |
| 
 | |
| window.upload = exports;
 |