comment safe guards

This commit is contained in:
2026-05-24 10:11:41 +02:00
parent bb4125601f
commit ca4a722029
2 changed files with 110 additions and 23 deletions

View File

@@ -4,6 +4,16 @@
const _f0ckDebug = (...args) => (typeof window.f0ckDebug === 'function' ? window.f0ckDebug(...args) : void 0);
class CommentSystem {
get isMainSubmitting() {
return this._globalState ? this._globalState.isMainSubmitting : false;
}
set isMainSubmitting(val) {
if (this._globalState) this._globalState.isMainSubmitting = val;
}
get pendingSubmissions() {
return this._globalState ? this._globalState.pendingSubmissions : new Set();
}
constructor() {
this.container = document.getElementById('comments-container');
this.itemId = this.container ? this.container.dataset.itemId : null;
@@ -47,8 +57,24 @@ class CommentSystem {
}
this.initialLoadDone = false;
this.pendingSubmissions = new Set();
this.isMainSubmitting = false;
// Retrieve or initialize global submission state for this item to survive f0ck:contentLoaded re-inits
if (this.itemId) {
window._f0ckActiveSubmissions = window._f0ckActiveSubmissions || {};
if (!window._f0ckActiveSubmissions[this.itemId]) {
window._f0ckActiveSubmissions[this.itemId] = {
isMainSubmitting: false,
pendingSubmissions: new Set()
};
}
this._globalState = window._f0ckActiveSubmissions[this.itemId];
} else {
this._globalState = {
isMainSubmitting: false,
pendingSubmissions: new Set()
};
}
this.scrollListenerAdded = false;
this.commentCache = new Map();
this._anchorScrollDone = false; // true after the first hash-anchor scroll on initial load
@@ -280,6 +306,7 @@ class CommentSystem {
saveState() {
const state = {
mainText: '',
isMainSubmitting: this.isMainSubmitting,
openReplies: [],
focused: null
};
@@ -288,7 +315,7 @@ class CommentSystem {
// 1. Save main input
const mainInput = this.container.querySelector('.main-input textarea');
if (mainInput && !this.isMainSubmitting) {
if (mainInput) {
state.mainText = mainInput.value;
if (document.activeElement === mainInput) {
state.focused = { type: 'main', start: mainInput.selectionStart, end: mainInput.selectionEnd };
@@ -298,17 +325,19 @@ class CommentSystem {
// 2. Save open replies
this.container.querySelectorAll('.reply-input').forEach(form => {
const parentId = form.dataset.parent;
if (!parentId || this.pendingSubmissions.has(parentId)) return;
if (!parentId) return;
const textarea = form.querySelector('textarea');
const text = textarea ? textarea.value : '';
if (parentId) {
const replyState = { parentId, text };
const replyState = {
parentId,
text,
isPending: this.pendingSubmissions.has(parentId)
};
if (document.activeElement === textarea) {
state.focused = { type: 'reply', parentId, start: textarea.selectionStart, end: textarea.selectionEnd };
}
state.openReplies.push(replyState);
}
});
return state;
@@ -318,7 +347,7 @@ class CommentSystem {
if (!this.container) return;
// 1. Restore open replies
state.openReplies.forEach(({ parentId, text }) => {
state.openReplies.forEach(({ parentId, text, isPending }) => {
const commentBody = this.container.querySelector(`#c${parentId} > .comment-body`);
if (commentBody && !commentBody.querySelector('.reply-input')) {
const div = document.createElement('div');
@@ -327,16 +356,37 @@ class CommentSystem {
const newForm = commentBody.querySelector('.reply-input');
if (newForm) {
const textarea = newForm.querySelector('textarea');
if (textarea) textarea.value = text;
if (textarea) {
textarea.value = text;
if (isPending) textarea.disabled = true;
}
if (isPending) {
const submitBtn = newForm.querySelector('.submit-comment');
if (submitBtn) {
submitBtn.classList.add('loading');
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
}
}
this.setupEmojiPicker(newForm);
}
}
});
// 2. Restore main input text
// 2. Restore main input text & submitting state
const mainInput = this.container.querySelector('.main-input textarea');
if (mainInput && state.mainText) {
if (mainInput) {
mainInput.value = state.mainText;
if (state.isMainSubmitting) {
mainInput.disabled = true;
const wrap = mainInput.closest('.comment-input');
const submitBtn = wrap ? wrap.querySelector('.submit-comment') : null;
if (submitBtn) {
submitBtn.classList.add('loading');
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
}
}
}
// 3. Restore focus
@@ -348,7 +398,7 @@ class CommentSystem {
targetEl = this.container.querySelector(`#c${state.focused.parentId} .reply-input textarea`);
}
if (targetEl) {
if (targetEl && !targetEl.disabled) {
targetEl.focus();
// Ensure selection is restored after focus
if (typeof targetEl.setSelectionRange === 'function') {
@@ -2149,11 +2199,15 @@ class CommentSystem {
this.container.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && e.ctrlKey) {
const textarea = e.target.closest('textarea');
if (!textarea) return;
if (!textarea || textarea.disabled) return;
const wrap = textarea.closest('.comment-input');
if (!wrap) return;
const submitBtn = wrap.querySelector('.submit-comment');
if (submitBtn) submitBtn.click();
if (submitBtn && !submitBtn.disabled && !submitBtn.classList.contains('loading')) {
e.preventDefault();
e.stopPropagation();
submitBtn.click();
}
} else if (e.key === 'Escape') {
const textarea = e.target.closest('textarea');
if (textarea) textarea.blur();
@@ -2760,11 +2814,13 @@ class CommentSystem {
const parentId = wrap.dataset.parent || null;
if (!text.trim()) return;
if (submitBtn.classList.contains('loading')) return;
if (submitBtn.classList.contains('loading') || submitBtn.disabled) return;
if (wrap._pendingUploads > 0) return;
// Start loading state
submitBtn.classList.add('loading');
submitBtn.disabled = true;
textarea.disabled = true;
const originalBtnHtml = submitBtn.innerHTML;
submitBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
@@ -3038,15 +3094,35 @@ class CommentSystem {
}
_finishSubmit(btn, originalHtml, parentId) {
if (btn) {
btn.classList.remove('loading');
btn.innerHTML = originalHtml;
}
if (parentId) {
this.pendingSubmissions.delete(parentId);
} else {
this.isMainSubmitting = false;
}
// Surgical lookup of the active wrap in the live DOM
let activeWrap = null;
if (this.container) {
if (parentId) {
activeWrap = this.container.querySelector(`#c${parentId} .reply-input`);
} else {
activeWrap = this.container.querySelector('.main-input');
}
}
const wrap = activeWrap || (btn ? btn.closest('.comment-input') : null);
if (wrap) {
const activeBtn = wrap.querySelector('.submit-comment');
const activeTextarea = wrap.querySelector('textarea');
if (activeBtn) {
activeBtn.classList.remove('loading');
activeBtn.disabled = false;
activeBtn.innerHTML = originalHtml;
}
if (activeTextarea) {
activeTextarea.disabled = false;
}
}
}

View File

@@ -110,6 +110,8 @@ window.initUploadForm = (selector) => {
if (form._f0ckInit) return form._f0ckUploader;
form._f0ckInit = true;
let isUploading = false;
// Use querySelector to find elements within this specific form instance
const fileInput = form.querySelector('.file-input');
const dropZone = form.querySelector('.drop-zone');
@@ -643,6 +645,11 @@ window.initUploadForm = (selector) => {
};
const updateSubmitButton = () => {
if (isUploading) {
if (submitBtn) submitBtn.disabled = true;
return;
}
const isShitpost = !!window.f0ckShitpostMode;
const rating = form.querySelector('input[name="rating"]:checked');
@@ -1795,7 +1802,7 @@ window.initUploadForm = (selector) => {
if (e && e.preventDefault) e.preventDefault();
// If already uploading, don't start again
if (submitBtn && submitBtn.disabled && submitBtn.querySelector('.btn-loading')?.style.display === 'inline') {
if (isUploading) {
return;
}
@@ -1826,6 +1833,7 @@ window.initUploadForm = (selector) => {
const isOc = form.querySelector('#upload-oc-checkbox')?.checked || false;
const setBtnLoading = (text) => {
isUploading = true;
if (!submitBtn) return;
submitBtn.disabled = true;
const btnText = submitBtn.querySelector('.btn-text');
@@ -1838,12 +1846,14 @@ window.initUploadForm = (selector) => {
};
const restoreBtn = () => {
isUploading = false;
if (!submitBtn) return;
submitBtn.disabled = false;
const btnText = submitBtn.querySelector('.btn-text');
const btnLoading = submitBtn.querySelector('.btn-loading');
if (btnText) btnText.style.display = 'inline';
if (btnLoading) btnLoading.style.display = 'none';
updateSubmitButton();
};
if (activeMode === 'url') {
@@ -2106,6 +2116,7 @@ window.initUploadForm = (selector) => {
handleFile: handleFile,
performUpload: performUpload,
reset: () => {
isUploading = false;
form.reset();
tags = [];
selectedFiles = [];