diff --git a/warehouse/static/js/site.js b/warehouse/static/js/site.js index a2de36a4dc1618e4724a40ccf80ea10184dba1e7..c0f4560c868b7f3898544d03920a2fff44e9644b 100644 --- a/warehouse/static/js/site.js +++ b/warehouse/static/js/site.js @@ -1,11 +1,59 @@ +let active_uploads = 0; + document.addEventListener('DOMContentLoaded', function() { function submit_on_change(event) { - event.target.closest('form').submit(); + const form = event.target.closest('form'); + const form_data = new FormData(); + form_data.append("csrf_token", form.querySelector('input[name="csrf_token"]').value); + form_data.append("file", event.target.files[0]) + const alertContainer = document.getElementById('alertContainer'); + const toastContainer = document.querySelector('.toast-container'); + const tmp = document.createElement("div"); + tmp.innerHTML = ` + <div class="toast align-items-center text-bg-primary border-0 mb-2" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false"> + <div class="toast-body"> + <div class="spinner-border spinner-border-sm" role="status"></div> + Uploading photo ... + </div> + </div> + `; + const toastElement = tmp.querySelector('div'); + toastContainer.append(toastElement); + const toast = new bootstrap.Toast(toastElement); + toast.show(); + active_uploads += 1; + fetch(form.action, { + method: "POST", + credentials: "include", + body: form_data, + }).then((response) => { + if (!response.ok) { + throw new Error("Upload error"); + } + toast.hide(); + const tmp = document.createElement("div"); + tmp.innerHTML = '<div class="alert alert-success" role="alert">Photo added</div>'; + alertContainer.append(tmp.querySelector('div')); + active_uploads -= 1; + }).catch((error) => { + toast.hide(); + const tmp = document.createElement("div"); + tmp.innerHTML = '<div class="alert alert-danger" role="alert">Photo upload failed!'; + alertContainer.prepend(tmp.querySelector('div')); + active_uploads -= 1; + }); } const buttons = document.querySelectorAll('.submit-on-change'); buttons.forEach((button) => { - console.log(button); button.addEventListener('change', submit_on_change); }); + + window.addEventListener('beforeunload', (event) => { + if (!active_uploads) { + return; + } + event.preventDefault(); + event.returnValue = ''; + }); }) diff --git a/warehouse/templates/layout.html b/warehouse/templates/layout.html index a9f7e4c6cec644f5ef5f53cfe0c1579e6d58a91c..15cd603e3a3a00ce4953273c21290e5057f5e360 100644 --- a/warehouse/templates/layout.html +++ b/warehouse/templates/layout.html @@ -5,7 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="{{ url_for('static', filename="css/bootstrap.min.css") }}" rel="stylesheet"> <script src="{{ url_for('static', filename="js/bootstrap.bundle.min.js") }}"></script> - <script src="{{ url_for('static', filename="js/site.js") }}"></script> + <script src="{{ url_for('static', filename="js/site.js", v=1) }}"></script> <title>Warehouse</title> </head> <body> @@ -28,13 +28,15 @@ </div> </nav> <div class="col-md-8 py-2 px-3 mx-auto"> + <div id="alertContainer"> {% for category, message in get_flashed_messages(with_categories=true) %} - <div class="col-12"> <div class="alert alert-{{ 'danger' if category == 'error' else 'warning' if category == 'warning' else 'primary' }}" role="alert">{{ message }}</div> - </div> {% endfor %} + </div> {% block body %} {% endblock %} </div> + <div class="toast-container position-fixed bottom-0 end-0 p-3"> + </div> </body> </html>