diff --git a/public/s/css/f0ckm.css b/public/s/css/f0ckm.css index 08a8aae..f50fe10 100644 --- a/public/s/css/f0ckm.css +++ b/public/s/css/f0ckm.css @@ -2324,6 +2324,12 @@ body.layout-modern .item-sidebar-left .tag-controls { cursor: wait; } +.submit-comment.uploading { + pointer-events: none; + opacity: 0.5; + cursor: wait; +} + .cancel-reply { background: transparent; color: var(--white); diff --git a/public/s/js/comments.js b/public/s/js/comments.js index 48c8f33..d9aec8b 100644 --- a/public/s/js/comments.js +++ b/public/s/js/comments.js @@ -2118,6 +2118,19 @@ class CommentSystem { const i18n = window.f0ckI18n || {}; const removeLabel = i18n.remove_file || 'Remove file'; + const maxAttachments = session.fileupload_comments_max || 5; + const currentCount = previewArea ? previewArea.querySelectorAll('.cf-preview-item').length : 0; + const slotsLeft = maxAttachments - currentCount; + + if (fileInput.files.length > slotsLeft) { + if (window.flashMessage) window.flashMessage( + `Maximum ${maxAttachments} attachments per comment exceeded`, + 3000, 'error' + ); + fileInput.value = ''; + return; + } + for (const file of fileInput.files) { if (file.size > maxSize) { if (window.flashMessage) window.flashMessage((i18n.file_too_large || 'File too large') + `: ${file.name}`, 3000, 'error'); @@ -2127,6 +2140,14 @@ class CommentSystem { fd.append('file', file); const csrf = session.csrf_token || ''; const uploadingText = i18n.uploading_file || 'Uploading...'; + const submitBtn = wrap.querySelector('.submit-comment'); + + // Track pending uploads + wrap._pendingUploads = (wrap._pendingUploads || 0) + 1; + if (submitBtn) { + submitBtn.disabled = true; + submitBtn.classList.add('uploading'); + } // Insert placeholder at cursor position const cursorPos = textarea.selectionStart; @@ -2207,6 +2228,16 @@ class CommentSystem { textarea.value = textarea.value.replace(placeholder, ''); if (previewItem) previewItem.remove(); if (window.flashMessage) window.flashMessage('Upload failed: ' + err.message, 3000, 'error'); + } finally { + // Decrement pending uploads + wrap._pendingUploads = Math.max(0, (wrap._pendingUploads || 1) - 1); + if (wrap._pendingUploads === 0) { + const submitBtn = wrap.querySelector('.submit-comment'); + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.classList.remove('uploading'); + } + } } } fileInput.value = ''; @@ -2565,6 +2596,7 @@ class CommentSystem { if (!text.trim()) return; if (submitBtn.classList.contains('loading')) return; + if (wrap._pendingUploads > 0) return; // Start loading state submitBtn.classList.add('loading'); diff --git a/src/index.mjs b/src/index.mjs index 5f8916e..5f09216 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -1094,6 +1094,7 @@ process.on('uncaughtException', err => { allow_fileupload_comments: cfg.websrv.allow_fileupload_comments || false, fileupload_comments_multifile: cfg.websrv.fileupload_comments_multifile || false, fileupload_comments_size: cfg.websrv.fileupload_comments_size || (10 * 1024 * 1024), + fileupload_comments_max: cfg.websrv.fileupload_comments_max || 5, fileupload_comments_mode: cfg.websrv.fileupload_comments_mode || 'attachment', get fonts() { diff --git a/views/snippets/footer.html b/views/snippets/footer.html index dab5870..9355914 100644 --- a/views/snippets/footer.html +++ b/views/snippets/footer.html @@ -410,6 +410,7 @@ allow_fileupload_comments: @if(allow_fileupload_comments) true @else false @endif, fileupload_comments_multifile: @if(fileupload_comments_multifile) true @else false @endif, fileupload_comments_size: {{ fileupload_comments_size }}, + fileupload_comments_max: {{ fileupload_comments_max }}, fileupload_comments_mode: "{{ fileupload_comments_mode }}" }; window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};