diff --git a/resources/views/filament/server/pages/file-upload.blade.php b/resources/views/filament/server/pages/file-upload.blade.php index 920a0ea5d..6d18300e2 100644 --- a/resources/views/filament/server/pages/file-upload.blade.php +++ b/resources/views/filament/server/pages/file-upload.blade.php @@ -1,279 +1,308 @@
+ xhr.open('POST', url.toString()); + xhr.send(formData); + }); + } catch (err) { + fileData.status = 'error'; + fileData.error = 'Failed to get upload token'; + throw err; + } + }, + + formatBytes(bytes) { + if (bytes === 0) return '0.00 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]; + }, + formatSpeed(bytesPerSecond) { + return this.formatBytes(bytesPerSecond) + '/s'; + }, + handleEscapeKey(e) { + if (e.key === 'Escape' && this.isUploading) { + this.isUploading = false; + this.uploadQueue = []; + } + }, + async handleFileSelect(e) { + const files = Array.from(e.target.files); + if (files.length > 0) { + const filesWithPaths = files.map(f => ({ + file: f, + path: '' + })); + await this.uploadFilesWithFolders(filesWithPaths); + } + }, + triggerBrowse() { + this.$refs.fileInput.click(); + }, +}" + class="relative">
diff --git a/resources/views/filament/server/pages/list-files.blade.php b/resources/views/filament/server/pages/list-files.blade.php index e864d0ee9..0ff5dfcb7 100644 --- a/resources/views/filament/server/pages/list-files.blade.php +++ b/resources/views/filament/server/pages/list-files.blade.php @@ -44,7 +44,10 @@ } if (files && files.length > 0 && filesWithPaths.length === 0) { - filesWithPaths = Array.from(files).map(f => ({ file: f, path: '' })); + filesWithPaths = Array.from(files).map(f => ({ + file: f, + path: '' + })); } if (filesWithPaths.length > 0) { @@ -53,67 +56,67 @@ }, async extractFilesFromItems(items) { - const filesWithPaths = []; - const traversePromises = []; + const filesWithPaths = []; + const traversePromises = []; - for (let i = 0; i < items.length; i++) { - const entry = items[i].webkitGetAsEntry?.(); + for (let i = 0; i < items.length; i++) { + const entry = items[i].webkitGetAsEntry?.(); - if (entry) { - traversePromises.push(this.traverseFileTree(entry, '', filesWithPaths)); - } else if (items[i].kind === 'file') { - const file = items[i].getAsFile(); - if (file) { - filesWithPaths.push({ - file: file, - path: '', - }); + if (entry) { + traversePromises.push(this.traverseFileTree(entry, '', filesWithPaths)); + } else if (items[i].kind === 'file') { + const file = items[i].getAsFile(); + if (file) { + filesWithPaths.push({ + file: file, + path: '', + }); + } } } - } - await Promise.all(traversePromises); + await Promise.all(traversePromises); - return filesWithPaths; + return filesWithPaths; }, - async traverseFileTree(entry, path, filesWithPaths) { - return new Promise((resolve) => { - if (entry.isFile) { - entry.file((file) => { - filesWithPaths.push({ - file: file, - path: path, + async traverseFileTree(entry, path, filesWithPaths) { + return new Promise((resolve) => { + if (entry.isFile) { + entry.file((file) => { + filesWithPaths.push({ + file: file, + path: path, + }); + resolve(); }); + } else if (entry.isDirectory) { + const reader = entry.createReader(); + const readEntries = () => { + reader.readEntries(async (entries) => { + if (entries.length === 0) { + resolve(); + return; + } + + const subPromises = entries.map((e) => + this.traverseFileTree( + e, + path ? `${path}/${entry.name}` : entry.name, + filesWithPaths + ) + ); + + await Promise.all(subPromises); + readEntries(); + }); + }; + readEntries(); + } else { resolve(); - }); - } else if (entry.isDirectory) { - const reader = entry.createReader(); - const readEntries = () => { - reader.readEntries(async (entries) => { - if (entries.length === 0) { - resolve(); - return; - } - - const subPromises = entries.map((e) => - this.traverseFileTree( - e, - path ? `${path}/${entry.name}` : entry.name, - filesWithPaths - ) - ); - - await Promise.all(subPromises); - readEntries(); - }); - }; - readEntries(); - } else { - resolve(); - } - }); - }, + } + }); + }, async uploadFilesWithFolders(filesWithPaths) { this.isUploading = true; this.uploadQueue = []; @@ -124,7 +127,10 @@ try { const uploadSizeLimit = await $wire.getUploadSizeLimit(); - for (const { file } of filesWithPaths) { + for (const { + file + } + of filesWithPaths) { if (file.size > uploadSizeLimit) { new window.FilamentNotification() .title(`File ${file.name} exceeds the upload limit.`) @@ -136,7 +142,10 @@ } const folderPaths = new Set(); - for (const { path } of filesWithPaths) { + for (const { + path + } + of filesWithPaths) { if (path) { const parts = path.split('/').filter(Boolean); let currentPath = ''; @@ -175,12 +184,17 @@ for (let i = 0; i < this.uploadQueue.length; i++) { const uploadPromise = this.uploadFile(i) - .then(() => { completedCount++; this.currentFileIndex = completedCount; + .then(() => { + completedCount++; + this.currentFileIndex = completedCount; const item = this.uploadQueue[i]; const relativePath = (item.path ? item.path.replace(/^\/+/, '') + '/' : '') + item.name; uploadedFiles.push(relativePath); }) - .catch(() => { completedCount++; this.currentFileIndex = completedCount; }); + .catch(() => { + completedCount++; + this.currentFileIndex = completedCount; + }); activeUploads.push(uploadPromise); @@ -196,19 +210,32 @@ await $wire.$refresh(); if (failed.length === 0) { - new window.FilamentNotification().title('{{ trans('server/file.actions.upload.success') }}').success().send(); + new window.FilamentNotification() + .title('{{ trans('server/file.actions.upload.success') }}') + .success() + .send(); } else if (failed.length === this.totalFiles) { - new window.FilamentNotification().title('{{ trans('server/file.actions.upload.failed') }}').danger().send(); + new window.FilamentNotification() + .title('{{ trans('server/file.actions.upload.failed') }}') + .danger() + .send(); } else { - new window.FilamentNotification().title('{{ trans('server/file.actions.upload.failed') }}').danger().send(); + new window.FilamentNotification() + .title('{{ trans('server/file.actions.upload.failed') }}') + .danger() + .send(); } if (uploadedFiles.length > 0) { this.$nextTick(() => { - try { - @this.call('logUploadedFiles', uploadedFiles); - } catch (e) { + if (typeof $wire !== 'undefined' && $wire && typeof $wire.call === 'function') { $wire.call('logUploadedFiles', uploadedFiles); + } else if (typeof window.livewire !== 'undefined' && typeof window.livewire.call === 'function') { + window.livewire.call('logUploadedFiles', uploadedFiles); + } else if (typeof Livewire !== 'undefined' && typeof Livewire.call === 'function') { + Livewire.call('logUploadedFiles', uploadedFiles); + } else { + console.warn('Could not call Livewire method logUploadedFiles; Livewire not found.'); } }); } @@ -217,10 +244,13 @@ this.autoCloseTimer = setTimeout(() => { this.isUploading = false; this.uploadQueue = []; - },1000); + }, 1000); } catch (error) { console.error('Upload error:', error); - new window.FilamentNotification().title('{{ trans('server/file.actions.upload.error') }}').danger().send(); + new window.FilamentNotification() + .title('{{ trans('server/file.actions.upload.error') }}') + .danger() + .send(); this.isUploading = false; } },