fixing upload
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
const fileInput = document.getElementById('file-input');
|
const fileInput = document.getElementById('file-input');
|
||||||
const dropZone = document.getElementById('drop-zone');
|
const dropZone = document.getElementById('drop-zone');
|
||||||
const filePreview = document.getElementById('file-preview');
|
const filePreview = document.getElementById('file-preview');
|
||||||
|
// Note: prompt is now a label, but accessible via class
|
||||||
const dropZonePrompt = dropZone.querySelector('.drop-zone-prompt');
|
const dropZonePrompt = dropZone.querySelector('.drop-zone-prompt');
|
||||||
const fileName = document.getElementById('file-name');
|
const fileName = document.getElementById('file-name');
|
||||||
const fileSize = document.getElementById('file-size');
|
const fileSize = document.getElementById('file-size');
|
||||||
@@ -23,6 +24,43 @@
|
|||||||
let tags = [];
|
let tags = [];
|
||||||
let selectedFile = null;
|
let selectedFile = null;
|
||||||
|
|
||||||
|
// Flash Message Logic
|
||||||
|
const showFlash = (msg, type = 'success') => {
|
||||||
|
const existing = document.querySelector('.flash-message');
|
||||||
|
if (existing) existing.remove();
|
||||||
|
|
||||||
|
const flash = document.createElement('div');
|
||||||
|
flash.className = `flash-message ${type}`;
|
||||||
|
flash.textContent = msg;
|
||||||
|
|
||||||
|
Object.assign(flash.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
top: '20px',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
padding: '15px 30px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
color: '#fff',
|
||||||
|
fontWeight: '600',
|
||||||
|
zIndex: '9999',
|
||||||
|
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
||||||
|
background: type === 'success' ? '#51cf66' : '#ff6b6b',
|
||||||
|
opacity: '0',
|
||||||
|
transition: 'opacity 0.3s'
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(flash);
|
||||||
|
|
||||||
|
// Fade in
|
||||||
|
requestAnimationFrame(() => flash.style.opacity = '1');
|
||||||
|
|
||||||
|
// Remove after 5s
|
||||||
|
setTimeout(() => {
|
||||||
|
flash.style.opacity = '0';
|
||||||
|
setTimeout(() => flash.remove(), 300);
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
const formatSize = (bytes) => {
|
const formatSize = (bytes) => {
|
||||||
const units = ['B', 'KB', 'MB', 'GB'];
|
const units = ['B', 'KB', 'MB', 'GB'];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
@@ -59,7 +97,11 @@
|
|||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
const validTypes = ['video/mp4', 'video/webm'];
|
const validTypes = ['video/mp4', 'video/webm'];
|
||||||
if (!validTypes.includes(file.type)) {
|
// Check extensions as fallback
|
||||||
|
const ext = file.name.split('.').pop().toLowerCase();
|
||||||
|
const validExts = ['mp4', 'webm'];
|
||||||
|
|
||||||
|
if (!validTypes.includes(file.type) && !validExts.includes(ext)) {
|
||||||
statusDiv.textContent = 'Only mp4 and webm files are allowed';
|
statusDiv.textContent = 'Only mp4 and webm files are allowed';
|
||||||
statusDiv.className = 'upload-status error';
|
statusDiv.className = 'upload-status error';
|
||||||
return;
|
return;
|
||||||
@@ -72,9 +114,56 @@
|
|||||||
filePreview.style.display = 'flex';
|
filePreview.style.display = 'flex';
|
||||||
statusDiv.textContent = '';
|
statusDiv.textContent = '';
|
||||||
statusDiv.className = 'upload-status';
|
statusDiv.className = 'upload-status';
|
||||||
|
|
||||||
|
// Video Preview
|
||||||
|
const itemPreview = filePreview.querySelector('.item-preview') || document.createElement('div');
|
||||||
|
itemPreview.className = 'item-preview';
|
||||||
|
itemPreview.style.marginRight = '15px';
|
||||||
|
|
||||||
|
// Clear previous
|
||||||
|
const existingVid = filePreview.querySelector('video');
|
||||||
|
if (existingVid) existingVid.remove();
|
||||||
|
|
||||||
|
const vid = document.createElement('video');
|
||||||
|
vid.src = URL.createObjectURL(file);
|
||||||
|
vid.controls = false;
|
||||||
|
vid.autoplay = true;
|
||||||
|
vid.muted = true;
|
||||||
|
vid.loop = true;
|
||||||
|
vid.style.maxHeight = '100px';
|
||||||
|
vid.style.maxWidth = '150px';
|
||||||
|
vid.style.borderRadius = '4px';
|
||||||
|
|
||||||
|
filePreview.prepend(vid);
|
||||||
|
|
||||||
updateSubmitButton();
|
updateSubmitButton();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const preventDefaults = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attach drag events only to dropZone now (Input is hidden)
|
||||||
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
|
dropZone.addEventListener(eventName, preventDefaults, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
['dragenter', 'dragover'].forEach(eventName => {
|
||||||
|
dropZone.addEventListener(eventName, () => dropZone.classList.add('dragover'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
['dragleave', 'drop'].forEach(eventName => {
|
||||||
|
dropZone.addEventListener(eventName, () => dropZone.classList.remove('dragover'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
dropZone.addEventListener('drop', (e) => {
|
||||||
|
const dt = e.dataTransfer;
|
||||||
|
const files = dt.files;
|
||||||
|
handleFile(files[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Native change listener on hidden input
|
||||||
fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
|
fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
|
||||||
|
|
||||||
removeFile.addEventListener('click', (e) => {
|
removeFile.addEventListener('click', (e) => {
|
||||||
@@ -84,24 +173,13 @@
|
|||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
dropZonePrompt.style.display = 'block';
|
dropZonePrompt.style.display = 'block';
|
||||||
filePreview.style.display = 'none';
|
filePreview.style.display = 'none';
|
||||||
|
// Clear preview video
|
||||||
|
const vid = filePreview.querySelector('video');
|
||||||
|
if (vid) vid.remove();
|
||||||
|
|
||||||
updateSubmitButton();
|
updateSubmitButton();
|
||||||
});
|
});
|
||||||
|
|
||||||
dropZone.addEventListener('dragover', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
dropZone.classList.add('dragover');
|
|
||||||
});
|
|
||||||
|
|
||||||
dropZone.addEventListener('dragleave', () => {
|
|
||||||
dropZone.classList.remove('dragover');
|
|
||||||
});
|
|
||||||
|
|
||||||
dropZone.addEventListener('drop', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
dropZone.classList.remove('dragover');
|
|
||||||
handleFile(e.dataTransfer.files[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const addTag = (tagName) => {
|
const addTag = (tagName) => {
|
||||||
tagName = tagName.trim().toLowerCase();
|
tagName = tagName.trim().toLowerCase();
|
||||||
if (!tagName || tags.includes(tagName)) return;
|
if (!tagName || tags.includes(tagName)) return;
|
||||||
@@ -215,18 +293,24 @@
|
|||||||
if (res.success) {
|
if (res.success) {
|
||||||
statusDiv.innerHTML = '✓ ' + res.msg;
|
statusDiv.innerHTML = '✓ ' + res.msg;
|
||||||
statusDiv.className = 'upload-status success';
|
statusDiv.className = 'upload-status success';
|
||||||
|
// Flash Message
|
||||||
|
showFlash(res.msg, 'success');
|
||||||
|
|
||||||
form.reset();
|
form.reset();
|
||||||
tags = [];
|
tags = [];
|
||||||
tagsList.innerHTML = '';
|
tagsList.innerHTML = '';
|
||||||
selectedFile = null;
|
selectedFile = null;
|
||||||
dropZonePrompt.style.display = 'block';
|
dropZonePrompt.style.display = 'block'; // label is actually flex/block via CSS
|
||||||
filePreview.style.display = 'none';
|
filePreview.style.display = 'none';
|
||||||
|
const vid = filePreview.querySelector('video');
|
||||||
|
if (vid) vid.remove();
|
||||||
} else {
|
} else {
|
||||||
statusDiv.textContent = '✕ ' + res.msg;
|
statusDiv.textContent = '✕ ' + res.msg;
|
||||||
statusDiv.className = 'upload-status error';
|
statusDiv.className = 'upload-status error';
|
||||||
if (res.repost) {
|
if (res.repost) {
|
||||||
statusDiv.innerHTML += ' <a href="/' + res.repost + '">View existing</a>';
|
statusDiv.innerHTML += ' <a href="/' + res.repost + '">View existing</a>';
|
||||||
}
|
}
|
||||||
|
showFlash('Upload failed: ' + res.msg, 'error');
|
||||||
}
|
}
|
||||||
|
|
||||||
submitBtn.querySelector('.btn-text').style.display = 'inline';
|
submitBtn.querySelector('.btn-text').style.display = 'inline';
|
||||||
@@ -239,6 +323,7 @@
|
|||||||
xhr.onerror = () => {
|
xhr.onerror = () => {
|
||||||
statusDiv.textContent = '✕ Upload failed. Please try again.';
|
statusDiv.textContent = '✕ Upload failed. Please try again.';
|
||||||
statusDiv.className = 'upload-status error';
|
statusDiv.className = 'upload-status error';
|
||||||
|
showFlash('Upload failed network error', 'error');
|
||||||
submitBtn.querySelector('.btn-text').style.display = 'inline';
|
submitBtn.querySelector('.btn-text').style.display = 'inline';
|
||||||
submitBtn.querySelector('.btn-loading').style.display = 'none';
|
submitBtn.querySelector('.btn-loading').style.display = 'none';
|
||||||
progressContainer.style.display = 'none';
|
progressContainer.style.display = 'none';
|
||||||
@@ -252,6 +337,7 @@
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
statusDiv.textContent = '✕ Upload failed: ' + err.message;
|
statusDiv.textContent = '✕ Upload failed: ' + err.message;
|
||||||
statusDiv.className = 'upload-status error';
|
statusDiv.className = 'upload-status error';
|
||||||
|
showFlash('Upload failed: ' + err.message, 'error');
|
||||||
submitBtn.querySelector('.btn-text').style.display = 'inline';
|
submitBtn.querySelector('.btn-text').style.display = 'inline';
|
||||||
submitBtn.querySelector('.btn-loading').style.display = 'none';
|
submitBtn.querySelector('.btn-loading').style.display = 'none';
|
||||||
updateSubmitButton();
|
updateSubmitButton();
|
||||||
|
|||||||
@@ -36,15 +36,16 @@
|
|||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<label>Video File <span class="required">*</span></label>
|
<label>Video File <span class="required">*</span></label>
|
||||||
<div class="drop-zone" id="drop-zone">
|
<div class="drop-zone" id="drop-zone">
|
||||||
<input type="file" id="file-input" name="file" accept="video/mp4,video/webm" required>
|
<input type="file" id="file-input" name="file" accept="video/mp4,video/webm" style="display: none;">
|
||||||
<div class="drop-zone-prompt">
|
<label for="file-input" class="drop-zone-prompt"
|
||||||
|
style="cursor: pointer; display: block; width: 100%; height: 100%;">
|
||||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||||
<polyline points="17 8 12 3 7 8"></polyline>
|
<polyline points="17 8 12 3 7 8"></polyline>
|
||||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||||
</svg>
|
</svg>
|
||||||
<p>Drop your mp4 or webm here<br>or click to browse</p>
|
<p>Drop your mp4 or webm here<br>or click to browse</p>
|
||||||
</div>
|
</label>
|
||||||
<div class="file-preview" id="file-preview" style="display: none;">
|
<div class="file-preview" id="file-preview" style="display: none;">
|
||||||
<span class="file-name" id="file-name"></span>
|
<span class="file-name" id="file-name"></span>
|
||||||
<span class="file-size" id="file-size"></span>
|
<span class="file-size" id="file-size"></span>
|
||||||
@@ -104,9 +105,7 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.upload-container {
|
.upload-container {
|
||||||
max-width: 700px;
|
margin: 0px 25px 0px 25px
|
||||||
margin: 2rem auto;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-container h2 {
|
.upload-container h2 {
|
||||||
|
|||||||
Reference in New Issue
Block a user