diff --git a/app/javascript/application.js b/app/javascript/application.js index b126c63..ef2a0e4 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,21 +1,23 @@ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails -import "@hotwired/turbo-rails" -import "controllers" +// import "@hotwired/turbo-rails" +// import "controllers" // Initialize the upload form. function upload_form_init() { // Disable the submit button if Javascript is supported. // Upload will start right after selecting images. - // $("#file-submit").hide(); - document.getElementById("file-submit").style.display = "none"; + const fileSubmit = document.getElementById("file-submit"); + if (fileSubmit) + { + fileSubmit.style.display = "none"; + } // Install event handler to notice when files were selected. - // $("#file").on("change", upload_form_files_selected); - document.getElementById("file").addEventListener("change", upload_form_files_selected); + document.getElementById("file")?.addEventListener("change", upload_form_files_selected); } function upload_form_files_selected() { - no_files = this.files.length; + var no_files = this.files.length; if (no_files > 0) { document.getElementById("file-label").innerHTML = `${no_files} files selected - uploading…`; document.getElementById("file-select-button").disabled = true; @@ -28,7 +30,7 @@ function upload_form_files_selected() { } // Handle Ctrl-V to paste an image directly -export function upload_paste_handler(event) { +function upload_paste_handler(event, uploadUrl) { console.log("paste!"); var items = (event.clipboardData || event.originalEvent.clipboardData).items; @@ -48,45 +50,48 @@ export function upload_paste_handler(event) { console.log(item.type); var file = item.getAsFile(); console.log(file); - upload_file_with_ajax(file); + upload_file_with_ajax(file, uploadUrl); } } -function upload_file_with_ajax(file) { +function upload_file_with_ajax(file, uploadUrl) { if (file == null) { console.log("File was null. Strange."); display_error("Maybe the image was too large."); return; } + var formData = new FormData(); formData.append("file", file); + console.log(formData); - fetch(ajax_upload_url, { + fetch(uploadUrl, { method: "POST", body: formData, headers: { + "X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content, // 'Content-Type' is omitted to let the browser set it automatically // with the proper boundary for FormData }, }) - .then(async (response) => { - if (!response.ok) { - const errorData = await response.json(); - console.log(`AJAX file upload returned error: ${response.statusText}`); - display_error(errorData); - throw new Error("Upload failed"); - } - return response.json(); - }) - .then((data) => { - console.log("AJAX file upload was ok"); - window.location.replace(after_upload_url); - }) - .catch((error) => { - if (error.message !== "Upload failed") { - console.log(`AJAX file upload error: ${error}`); - } - }); + .then(async (response) => { + if (!response.ok) { + const errorData = await response.json(); + console.log(`AJAX file upload returned error: ${response.statusText}`); + display_error(errorData); + throw new Error("Upload failed"); + } + return response.json(); + }) + .then((data) => { + console.log("AJAX file upload was ok"); + window.location.replace(after_upload_url); + }) + .catch((error) => { + if (error.message !== "Upload failed") { + console.log(`AJAX file upload error: ${error}`); + } + }); // $.ajax(ajax_upload_url, { // type: "POST", @@ -107,7 +112,8 @@ function upload_file_with_ajax(file) { // }); } -function display_error(msg) { +function display_error(msg) { console.log("foo"); + // $("#messages").html( // '
Sorry, pasting did not work. ' + msg + "
" // ); @@ -117,3 +123,21 @@ function display_error(msg) { `; } + + +document.addEventListener("DOMContentLoaded", () => { + console.log("DOM loaded"); + // Handle uploads on specific views/pages. The DIV with ID + // #data-js-upload-form carries data attributes that tell us where + // uploads will be sent from Javascript. + let element = document.getElementById("data-js-upload-form"); + + if (element) { + console.log("upload_form_init"); + // console.log(element.dataset); + // console.log(element.dataset.uploadUrl); + upload_form_init(element.dataset.uploadUrl); + // document.onpaste = upload_paste_handler(element.dataset.uploadUrl); + document.onpaste = function(event) { upload_paste_handler(event, element.dataset.uploadUrl); } + } +}); diff --git a/app/views/packages/_receive_upload.slim b/app/views/packages/_receive_upload.slim index 6b66f2c..eb564ee 100644 --- a/app/views/packages/_receive_upload.slim +++ b/app/views/packages/_receive_upload.slim @@ -1,5 +1,9 @@ / Render this callout on top of the package's details page / if images have just been uploaded. + +// Make variables accessible by the JS that handles uploads +#data-js-upload-form data-upload-url=upload_receive_json_path + - if (@valid_images and @valid_images.any?) or (@invalid_images and @invalid_images.any?) .callout.warning diff --git a/app/views/packages/details.slim b/app/views/packages/details.slim index d0e9160..3651608 100644 --- a/app/views/packages/details.slim +++ b/app/views/packages/details.slim @@ -66,13 +66,8 @@ = render(partial: 'details_rightbox', locals: {pkg: @package}) javascript: - import {upload_paste_handler} from 'aplication'; - - // Handle Ctrl-V to paste an image directly - document.onpaste = upload_paste_handler; - // Where to send POST requests for uploads - var ajax_upload_url = '#{{upload_receive_json_path}}'; + let ajax_upload_url = '#{{upload_receive_json_path}}'; // Where to go to after a successful image upload - var after_upload_url = '#{{package_path(@package)}}'; + let after_upload_url = '#{{package_path(@package)}}'; diff --git a/app/views/packages/upload.slim b/app/views/packages/upload.slim index fb439d8..22ad9a8 100644 --- a/app/views/packages/upload.slim +++ b/app/views/packages/upload.slim @@ -64,18 +64,11 @@ h1 = "Upload screenshots for #{@package.name}" ' from a shell using after setting "export LANG=C". li can be pasted here from your clipboard (Ctrl-V) -/ javascript: -/ // Hide the submit button unless images are selected for upload -/ // $(document).ready(upload_form_init); -/ import upload_form_init from '../../assets/javascripts/upload.js'; -/ document.addEventListener('DOMContentLoaded', upload_form_init); +javascript: + // todo: use data attributes to pass this information to javascript + // https://chat.deepseek.com/a/chat/s/41a4350b-7799-42bf-aec9-3c205b22ab24 + // Where to send POST requests for uploads + let ajax_upload_url = '#{{upload_receive_json_path}}'; -/ // import upload_paste_handler from '../../assets/javascripts/debshots.js'; -/ // Handle Ctrl-V to paste an image directly -/ document.onpaste = upload_paste_handler; - -/ // Where to send POST requests for uploads -/ var ajax_upload_url = '#{{upload_receive_json_path}}'; - -/ // Where to go to after a successful image upload -/ var after_upload_url = '#{{package_path(@package)}}'; + // Where to go to after a successful image upload + let after_upload_url = '#{{package_path(@package)}}'; diff --git a/config/importmap.rb b/config/importmap.rb new file mode 100644 index 0000000..e7724df --- /dev/null +++ b/config/importmap.rb @@ -0,0 +1,7 @@ +# Pin npm packages by running ./bin/importmap + +pin 'application' +pin '@hotwired/turbo-rails', to: 'turbo.min.js' +pin '@hotwired/stimulus', to: 'stimulus.min.js' +pin '@hotwired/stimulus-loading', to: 'stimulus-loading.js' +pin_all_from 'app/javascript/controllers', under: 'controllers' diff --git a/config/webpack/webpack.config.js b/config/webpack/webpack.config.js new file mode 100644 index 0000000..1e6710b --- /dev/null +++ b/config/webpack/webpack.config.js @@ -0,0 +1,21 @@ +const path = require("path") +const webpack = require("webpack") + +module.exports = { + mode: "production", + devtool: "source-map", + entry: { + application: "./app/javascript/application.js" + }, + output: { + filename: "[name].js", + sourceMapFilename: "[file].map", + chunkFormat: "module", + path: path.resolve(__dirname, '..', '..', 'app/assets/builds') + }, + plugins: [ + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1 + }) + ] +}