diff --git a/retoors/routes.py b/retoors/routes.py index 88afafd..d939a02 100644 --- a/retoors/routes.py +++ b/retoors/routes.py @@ -35,6 +35,8 @@ def setup_routes(app): app.router.add_get("/files/download/{file_path:.*}", FileBrowserView.get_download_file, name="download_file") app.router.add_post("/files/share/{file_path:.*}", FileBrowserView, name="share_file") app.router.add_post("/files/delete/{file_path:.*}", FileBrowserView, name="delete_item") + app.router.add_post("/files/delete_multiple", FileBrowserView, name="delete_multiple_items") + app.router.add_post("/files/share_multiple", FileBrowserView, name="share_multiple_items") app.router.add_get("/shared_file/{share_id}", FileBrowserView.shared_file_handler, name="shared_file") app.router.add_get("/shared_file/{share_id}/download", FileBrowserView.download_shared_file_handler, name="download_shared_file") diff --git a/retoors/static/js/main.js b/retoors/static/js/main.js index f5d7693..e03b3ce 100644 --- a/retoors/static/js/main.js +++ b/retoors/static/js/main.js @@ -107,29 +107,64 @@ document.addEventListener('DOMContentLoaded', () => { window.location.href = `/files/download/${path}`; } - async function shareFile(path, name) { + async function shareFile(paths, names) { const modal = document.getElementById('share-modal'); const linkContainer = document.getElementById('share-link-container'); const loading = document.getElementById('share-loading'); + const shareLinkInput = document.getElementById('share-link-input'); + const shareFileName = document.getElementById('share-file-name'); + const shareLinksList = document.getElementById('share-links-list'); // New element for multiple links - document.getElementById('share-file-name').textContent = `Sharing: ${name}`; + // Clear previous content + shareLinkInput.value = ''; + if (shareLinksList) shareLinksList.innerHTML = ''; linkContainer.style.display = 'none'; loading.style.display = 'block'; modal.style.display = 'block'; + if (paths.length === 1) { + shareFileName.textContent = `Sharing: ${names[0]}`; + } else { + shareFileName.textContent = `Sharing ${paths.length} items`; + } + try { - const response = await fetch(`/files/share/${path}`, { - method: 'POST' + const response = await fetch(`/files/share_multiple`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ paths: paths }) }); const data = await response.json(); - if (data.share_link) { - document.getElementById('share-link-input').value = data.share_link; - linkContainer.style.display = 'block'; + if (data.share_links && data.share_links.length > 0) { + if (data.share_links.length === 1) { + shareLinkInput.value = data.share_links[0]; + linkContainer.style.display = 'block'; + } else { + // Display multiple links + if (!shareLinksList) { + // Create the list if it doesn't exist + const ul = document.createElement('ul'); + ul.id = 'share-links-list'; + linkContainer.appendChild(ul); + shareLinksList = ul; + } + data.share_links.forEach(item => { + const li = document.createElement('li'); + li.innerHTML = `${item.name}: `; + shareLinksList.appendChild(li); + }); + linkContainer.style.display = 'block'; + } loading.style.display = 'none'; + } else { + loading.textContent = 'Error generating share link(s)'; } } catch (error) { - loading.textContent = 'Error generating share link'; + console.error('Error sharing files:', error); + loading.textContent = 'Error generating share link(s)'; } } @@ -140,10 +175,31 @@ document.addEventListener('DOMContentLoaded', () => { alert('Share link copied to clipboard'); } - function deleteFile(path, name) { - document.getElementById('delete-message').textContent = `Are you sure you want to delete "${name}"? This action cannot be undone.`; - document.getElementById('delete-form').action = `/files/delete/${path}`; - document.getElementById('delete-modal').style.display = 'block'; + function deleteFile(paths, names) { + const deleteForm = document.getElementById('delete-form'); + const deleteMessage = document.getElementById('delete-message'); + const deleteModal = document.getElementById('delete-modal'); + + // Clear previous hidden inputs + deleteForm.querySelectorAll('input[name="paths[]"]').forEach(input => input.remove()); + + if (Array.isArray(paths) && paths.length > 1) { + deleteMessage.textContent = `Are you sure you want to delete ${paths.length} items? This action cannot be undone.`; + deleteForm.action = `/files/delete_multiple`; + paths.forEach(path => { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = 'paths[]'; + input.value = path; + deleteForm.appendChild(input); + }); + } else { + const path = Array.isArray(paths) ? paths[0] : paths; + const name = Array.isArray(names) ? names[0] : names; + deleteMessage.textContent = `Are you sure you want to delete "${name}"? This action cannot be undone.`; + deleteForm.action = `/files/delete/${path}`; + } + deleteModal.style.display = 'block'; } // Selection and action buttons @@ -170,10 +226,12 @@ document.addEventListener('DOMContentLoaded', () => { function shareSelected() { const checked = document.querySelectorAll('.file-checkbox:checked'); - if (checked.length === 1) { - const path = checked[0].dataset.path; - const name = checked[0].closest('tr').querySelector('td:nth-child(2)').textContent.trim(); - shareFile(path, name); + if (checked.length > 0) { + const paths = Array.from(checked).map(cb => cb.dataset.path); + const names = Array.from(checked).map(cb => + cb.closest('tr').querySelector('td:nth-child(2)').textContent.trim() + ); + shareFile(paths, names); } } @@ -184,14 +242,7 @@ document.addEventListener('DOMContentLoaded', () => { const names = Array.from(checked).map(cb => cb.closest('tr').querySelector('td:nth-child(2)').textContent.trim() ); - - if (checked.length === 1) { - deleteFile(paths[0], names[0]); - } else { - document.getElementById('delete-message').textContent = - `Are you sure you want to delete ${checked.length} items? This action cannot be undone.`; - document.getElementById('delete-modal').style.display = 'block'; - } + deleteFile(paths, names); } } @@ -255,9 +306,21 @@ document.addEventListener('DOMContentLoaded', () => { if (event.target.classList.contains('download-file-btn')) { downloadFile(event.target.dataset.path); } else if (event.target.classList.contains('share-file-btn')) { - shareFile(event.target.dataset.path, event.target.dataset.name); + const path = event.target.dataset.path; + const name = event.target.dataset.name; + shareFile([path], [name]); } else if (event.target.classList.contains('delete-file-btn')) { - deleteFile(event.target.dataset.path, event.target.dataset.name); + const path = event.target.dataset.path; + const name = event.target.dataset.name; + deleteFile([path], [name]); + } else if (event.target.classList.contains('copy-share-link-item-btn')) { + const linkToCopy = event.target.dataset.link; + navigator.clipboard.writeText(linkToCopy).then(() => { + alert('Share link copied to clipboard'); + }).catch(err => { + console.error('Failed to copy link: ', err); + alert('Failed to copy link'); + }); } }); diff --git a/retoors/templates/pages/file_browser.html b/retoors/templates/pages/file_browser.html index 7ffb034..923c563 100644 --- a/retoors/templates/pages/file_browser.html +++ b/retoors/templates/pages/file_browser.html @@ -130,6 +130,7 @@
Generating share link...