All checks were successful
Frontend Deployment / deploy-frontend (push) Successful in 35s
285 lines
7.8 KiB
Vue
285 lines
7.8 KiB
Vue
<template>
|
|
<section class="upload-files">
|
|
<div class="files-header">
|
|
<h2><Icon icon="fa-folder"/> Mes fichiers</h2>
|
|
<Box box-class="area-container" padding="close" level="second">
|
|
<div class="upload-area">
|
|
<Button :color-lower="status === 'download'" :disabled="status === 'download'" class="upload-button" @click="uploadFile()" icon="fa-upload"> Ajouter des fichiers</Button>
|
|
<p v-if="status === 'download'" class="upload-file-name" ref="uploadStatus"><Icon icon='fa-spinner' spin-pulse/> Téléchargement en cours...</p>
|
|
<p v-else-if="status === 'error'" class="upload-status" ref="uploadStatus"><Error>Erreur lors du téléchargement</Error></p>
|
|
<p v-else-if="status === 'toohigh'" class="upload-status" ref="uploadStatus"><Error>Le fichier est trop volumineux</Error></p>
|
|
<p v-else-if="status === 'success'" class="upload-status" ref="uploadStatus"><Success>Fichier téléchargé avec succès</Success></p>
|
|
<p v-else class="upload-status" ref="uploadStatus">Aucun fichier séléctionné</p>
|
|
</div>
|
|
<p class="text-secondary infosup"><Icon icon="fa-circle-info"/> Ce système n'est pas un stockage permanent de données car il dépend du CDN de Discord. Vos fichiers peuvent exprirer à tout moment.</p>
|
|
</Box>
|
|
</div>
|
|
<div v-if="myFiles && myFiles.length > 0" class="uploaded-files-container">
|
|
<div class="uploaded-files">
|
|
<span v-for="file in myFiles" :key="file.id"><VideoComposable :video="file" delete/></span>
|
|
</div>
|
|
</div>
|
|
<p v-else-if="isLoading" class="none"><Icon icon="fa-spinner" spin-pulse/> Chargement des fichiers...</p>
|
|
<p v-else class="none"><Icon icon="fa-circle-xmark"/> Aucun fichier enregistré</p>
|
|
<Modal icon="fa-upload" title="Uploader un fichier" ref="uploadModal">
|
|
<p>Etes-vous sûr de vouloir uploader ces fichiers ?</p>
|
|
<p class="text-secondary">Ces fichiers seront stockés sur le CDN de Discord et seront à jamais accessibles. Ne diffusez rien de sensible.</p>
|
|
<p v-if="fileSelected.length > 0" v-for="value in fileSelected" class="upload-modal-name"><Icon icon="fa-file"/> {{ value.name }}</p>
|
|
<div class="upload-actions">
|
|
<Button @click="closeModal()">Annuler</Button>
|
|
<Button @click="confirmUpload()">Confirmer</Button>
|
|
</div>
|
|
</Modal>
|
|
</section>
|
|
</template>
|
|
<script setup>
|
|
import Box from '@/components/UI/Box.vue';
|
|
import Button from '@/components/UI/Button.vue';
|
|
import Modal from '@/components/UI/Modal.vue';
|
|
import Success from '@/components/UI/Success.vue';
|
|
import Error from '@/components/UI/Error.vue';
|
|
import VideoComposable from '@/components/Widget/VideoComposable.vue';
|
|
import { IORequest } from '@/utils/IORequest';
|
|
import { onMounted, onUnmounted, ref } from 'vue';
|
|
import Events from '@/utils/Events';
|
|
|
|
const fileSelected = ref([]);
|
|
const status = ref(false);
|
|
const uploadModal = ref(null);
|
|
const myFiles = ref([]);
|
|
const isLoading = ref(true);
|
|
|
|
onMounted(() => {
|
|
refreshUploadedFiles();
|
|
Events.on("video:delete", deleteFile);
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
Events.off("video:delete", deleteFile);
|
|
});
|
|
|
|
function deleteFile(data) {
|
|
IORequest('/UPLOAD/FILE/DELETE', (response) => {
|
|
refreshUploadedFiles();
|
|
}, data.video);
|
|
}
|
|
|
|
function uploadFile() {
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = '.mp3,.wav,.ogg'; // Accept audio files
|
|
input.multiple = true;
|
|
input.onchange = (event) => {
|
|
const files = Array.from(event.target.files);
|
|
if (files.length > 0) {
|
|
fileSelected.value = files;
|
|
console.log(`Files selected:`, files.map(f => f.name));
|
|
input.value = '';
|
|
input.remove();
|
|
uploadModal.value.open();
|
|
} else {
|
|
fileSelected.value = [];
|
|
}
|
|
};
|
|
input.click();
|
|
}
|
|
|
|
async function confirmUpload() {
|
|
if (fileSelected.value.length === 0) return;
|
|
status.value = 'download';
|
|
uploadModal.value.close();
|
|
|
|
let errorOccurred = false;
|
|
|
|
for (const file of fileSelected.value) {
|
|
try {
|
|
const fileBuffer = await readFileAsArrayBuffer(file);
|
|
|
|
if (fileBuffer.byteLength > 300 * 1024 * 1024) {
|
|
status.value = 'toohigh';
|
|
errorOccurred = true;
|
|
continue;
|
|
}
|
|
|
|
await new Promise(resolve => {
|
|
IORequest('/UPLOAD/FILE', (response) => {
|
|
if (!response || response === "TOOHIGH") {
|
|
errorOccurred = true;
|
|
status.value = (response === "TOOHIGH") ? 'toohigh' : 'error';
|
|
}
|
|
resolve();
|
|
}, { name: file.name, file: fileBuffer });
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error("Erreur upload fichier:", file.name, err);
|
|
errorOccurred = true;
|
|
}
|
|
}
|
|
|
|
status.value = errorOccurred ? status.value : 'success';
|
|
refreshUploadedFiles();
|
|
fileSelected.value = [];
|
|
}
|
|
|
|
// Petite fonction utilitaire pour transformer FileReader en promesse
|
|
function readFileAsArrayBuffer(file) {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onload = () => resolve(reader.result);
|
|
reader.onerror = reject;
|
|
reader.readAsArrayBuffer(file);
|
|
});
|
|
}
|
|
|
|
|
|
|
|
function closeModal() {
|
|
uploadModal.value.close();
|
|
fileSelected.value = null;
|
|
status.value = false;
|
|
}
|
|
|
|
function refreshUploadedFiles() {
|
|
myFiles.value = [];
|
|
isLoading.value = true;
|
|
IORequest('/UPLOAD/FILES', (response) => {
|
|
if (response) {
|
|
myFiles.value = response;
|
|
} else {
|
|
myFiles.value = [];
|
|
}
|
|
isLoading.value = false;
|
|
});
|
|
}
|
|
|
|
</script>
|
|
<style scoped>
|
|
|
|
.infosup {
|
|
margin: 0 10px 10px 10px;
|
|
text-align: justify;
|
|
}
|
|
|
|
.uploaded-files {
|
|
flex: 1;
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
gap: 10px;
|
|
position: absolute;
|
|
width: 100%;
|
|
}
|
|
|
|
.uploaded-files-container {
|
|
position: relative;
|
|
flex: 1;
|
|
display: flex;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.none {
|
|
text-align: center;
|
|
font-size: 1.5em;
|
|
color: var(--text-secondary);
|
|
opacity: 0.8;
|
|
flex: 1;
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.files-header {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
padding: 5px;
|
|
}
|
|
|
|
.text-secondary {
|
|
font-size: 0.8em;
|
|
color: var(--text-secondary);
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.upload-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.upload-actions Button {
|
|
width: 100%;
|
|
}
|
|
|
|
.upload-status {
|
|
font-size: 0.9em;
|
|
color: var(--text-secondary);
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.upload-file-name {
|
|
font-size: 1em;
|
|
color: var(--text-primary);
|
|
font-weight: bold;
|
|
}
|
|
|
|
.upload-modal-name {
|
|
font-size: 0.8em;
|
|
color: var(--text-primary);
|
|
background-color: var(--tertiary);
|
|
padding: 5px 10px;
|
|
border-radius: 5px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
}
|
|
|
|
.upload-area {
|
|
display: flex;
|
|
align-items: center;
|
|
border-radius: 10px;
|
|
padding: 10px 10px;
|
|
gap: 10px;
|
|
}
|
|
|
|
.upload-files {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
flex: 1;
|
|
}
|
|
|
|
@media screen and (max-width: 768px),
|
|
screen and (max-height: 607px) {
|
|
.upload-area {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.upload-button {
|
|
order: 1;
|
|
}
|
|
|
|
.area-container {
|
|
width: 100%;
|
|
}
|
|
|
|
.upload-button {
|
|
width: 100%;
|
|
}
|
|
|
|
.upload-actions {
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
p {
|
|
margin: 0;
|
|
}
|
|
|
|
.files-header h2 {
|
|
margin: 0;
|
|
font-size: 1.5em;
|
|
}
|
|
</style> |