app/javascript/controllers/upload_controller.js
import { Controller } from "@hotwired/stimulus";
import { DirectUpload } from "@rails/activestorage";
export default class extends Controller {
//static targets = ["input", "progress"];
static targets = ["input", "progressContainer", "template"]; // Renaming target to "progressContainer"
connect(){
this.validFiles = []
}
uploadFile() {
this.totalUploads = this.inputTarget.files.length; // Set the total number of files to be uploaded
this.maxSizeValue = 10 * 1024 * 1024 // 10 MB in bytes
if(this.totalUploads > 5){
alert(`You can only select ${5} files.`);
return
}
this.completedUploads = 0; // Counter for completed uploads
let errors = []
this.validFiles = []
Array.from(this.inputTarget.files).forEach((file, index) => {
// Check the file count
if (index >= this.limitValue) {
errors.push(`You can only select ${this.limitValue} files.`);
return;
}
// Check the file format
/*if (!file.type.startsWith("audio/")) {
errors.push(`File ${file.name} is not an audio file.`);
return;
}*/
// Check the file size
if (file.size > this.maxSizeValue) {
errors.push(`File ${file.name} exceeds the size limit of ${this.maxSizeValue / (1024 * 1024)} MB.`);
return;
}
this.validFiles.push({file: file, index: index});
});
if(errors.length > 0) {
alert(JSON.stringify(errors))
}
this.validFiles.forEach((validFile) => {
const {file, index} = validFile
const identifier = Math.floor(Math.random() * Date.now()).toString(16)
const progressBar = this.add_template(identifier, file) //this.createProgressBar(identifier);
const upload = new DirectUpload(
file,
this.inputTarget.dataset.directUploadUrl,
{
directUploadWillStoreFileWithXHR: (request)=>{
console.log(progressBar)
request.upload.addEventListener("progress", (event) => {
this.progressUpdate(event, identifier);
});
}
}
);
upload.create((error, blob) => {
if (error) {
console.log(error);
} else {
this.createHiddenBlobInput(blob);
// if you're not submitting the form after upload, you need to attach
// uploaded blob to some model here and skip hidden input.
this.completedUploads++;
// Check if all uploads are complete
this.checkAllUploadsComplete();
}
});
});
}
checkAllUploadsComplete() {
if (this.completedUploads === this.totalUploads) {
console.log("All uploads are complete!");
// Here you can perform any other actions you want when all files are uploaded
}
}
createProgressBar(index) {
const progressBar = document.createElement('div');
progressBar.setAttribute("id", `progress-bar-${index}`);
progressBar.style.width = "0%";
progressBar.style.height = "20px";
progressBar.style.backgroundColor = "green";
progressBar.style.marginTop = "10px";
this.progressContainerTarget.appendChild(progressBar); // Append to the container
return progressBar;
}
// add blob id to be submitted with the form
createHiddenBlobInput(blob) {
const hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("value", blob.signed_id);
hiddenField.name = this.inputTarget.name;
this.element.appendChild(hiddenField);
}
add_template(identifier, file) {
let doc = this.templateTarget
var content = doc.innerHTML.replace(/NEW_RECORD/g, `progress-bar-${identifier}`);
const url = URL.createObjectURL(file);
content = content.replace(/FILE/g, `<img src="${url}" class="h-20">`);
this.progressContainerTarget.insertAdjacentHTML('beforebegin', content);
}
progressUpdate(event, index) {
const progress = (event.loaded / event.total) * 100;
const fileIndex = index
const progressBar = document.querySelector(`#progress-bar-${fileIndex}`);
if (progressBar) {
const pge = `${progress}%`
progressBar.style.width = pge;
progressBar.innerHTML = pge
}
}
preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// When a file is dropped onto the designated area
handleDrop(e) {
this.preventDefaults(e);
let files = e.dataTransfer.files;
this.inputTarget.files = files;
this.uploadFile();
}
}