diff --git a/public/s/js/f0ckm.js b/public/s/js/f0ckm.js index 47f06e9..eca63a5 100644 --- a/public/s/js/f0ckm.js +++ b/public/s/js/f0ckm.js @@ -3667,7 +3667,7 @@ window.cancelAnimFrame = (function () { } // ── Store in item cache (stale-while-revalidate) ─────────────────────── - if (html && !options.skipCache) { + if (html && !options.noCacheStore) { itemCacheMap.set(_itemCacheKey, { html, slug: responseSlug, ts: Date.now() }); if (itemCacheMap.size > ITEM_CACHE_MAX) { // Evict oldest entry @@ -10544,6 +10544,91 @@ document.addEventListener('DOMContentLoaded', () => { // // Halls Management (Admin Only / My Halls for all logged-in users) + const escapeHtml = (str) => String(str ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + + const updateHallBadgeInDom = (hallSlug, hallName, action = 'add', isUserHall = false) => { + if (!hallSlug) return; + const liveHallBtn = document.getElementById('a_hall'); + const isModern = document.body.classList.contains('layout-modern') || !document.body.classList.contains('layout-legacy'); + + // 1. Update dataset on live #a_hall button + if (liveHallBtn) { + const datasetKey = isUserHall ? 'userHalls' : 'halls'; + const existing = (liveHallBtn.dataset[datasetKey] || '').split(',').filter(Boolean); + if (action === 'add') { + if (!existing.includes(hallSlug)) existing.push(hallSlug); + if (isUserHall) liveHallBtn.dataset.currentUserHall = hallSlug; + else liveHallBtn.dataset.currentHall = hallSlug; + } else if (action === 'remove') { + const idx = existing.indexOf(hallSlug); + if (idx !== -1) existing.splice(idx, 1); + if (isUserHall && liveHallBtn.dataset.currentUserHall === hallSlug) { + liveHallBtn.dataset.currentUserHall = existing[0] || ''; + } else if (!isUserHall && liveHallBtn.dataset.currentHall === hallSlug) { + liveHallBtn.dataset.currentHall = existing[0] || ''; + } + } + liveHallBtn.dataset[datasetKey] = existing.join(','); + } + + // 2. Main item header badge reflects site halls + if (isUserHall) return; + + let container = document.getElementById('hall-badge-container'); + if (!container) { + // Check for existing .hall-badge-wrap without wrapper + const existingWrap = document.querySelector('.hall-badge-wrap'); + if (existingWrap) { + if (action === 'remove') { + existingWrap.remove(); + return; + } + existingWrap.outerHTML = ` ${escapeHtml(hallName || hallSlug)}`; + return; + } + // Dynamically create container and insert at the appropriate place + container = document.createElement('span'); + container.id = 'hall-badge-container'; + if (isModern) { + const tsLink = document.querySelector('.blahlol .timestamp-link'); + const tsBadge = tsLink ? tsLink.closest('.badge') : null; + if (tsBadge && tsBadge.parentNode) { + tsBadge.parentNode.insertBefore(container, tsBadge); + } else { + const blahlol = document.querySelector('.blahlol'); + if (blahlol) blahlol.appendChild(container); + } + } else { + const gapRight = document.querySelector('.blahlol .gapRight'); + if (gapRight && gapRight.parentNode) { + gapRight.parentNode.insertBefore(container, gapRight); + } else { + const blahlol = document.querySelector('.blahlol'); + if (blahlol) blahlol.appendChild(container); + } + } + } + + if (action === 'add') { + const badgeHtml = ` ${escapeHtml(hallName || hallSlug)}`; + container.innerHTML = isModern ? ` — ${badgeHtml} — ` : ` — ${badgeHtml}`; + } else if (action === 'remove') { + const remaining = liveHallBtn ? (liveHallBtn.dataset.halls || '').split(',').filter(Boolean) : []; + if (remaining.length > 0) { + const nextSlug = remaining[0]; + const badgeHtml = ` ${escapeHtml(nextSlug)}`; + container.innerHTML = isModern ? ` — ${badgeHtml} — ` : ` — ${badgeHtml}`; + } else { + container.innerHTML = ''; + } + } + }; + document.addEventListener('click', async (e) => { // Add to Hall const hallBtn = e.target.closest('#a_hall'); @@ -10709,8 +10794,11 @@ document.addEventListener('DOMContentLoaded', () => { }); const data = await resp.json(); if (data.success) { + const hallName = (select.selectedOptions && select.selectedOptions[0]) ? select.selectedOptions[0].textContent : hallSlug; + updateHallBadgeInDom(hallSlug, hallName, 'add', false); + if (window.invalidateItemCache) window.invalidateItemCache(itemid); if (window.flashMessage) window.flashMessage((window.f0ckI18n && window.f0ckI18n.hall_added) || 'Added to hall!'); - if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true }); + if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true, skipCache: true }); close(); } else { throw new Error(data.msg || 'Failed to add to hall'); @@ -10771,17 +10859,13 @@ document.addEventListener('DOMContentLoaded', () => { if (data.success) { myHallsConfirmBtn.disabled = false; myHallsConfirmBtn.textContent = window.f0ckI18n?.hall_add_btn || 'Add'; - // Patch the live #a_hall button NOW so next modal open pre-selects correctly - // (before loadItemAjax finishes re-rendering the DOM) - const liveHallBtn = document.getElementById('a_hall'); - if (liveHallBtn) { - liveHallBtn.dataset.currentUserHall = hallSlug; - const existing = (liveHallBtn.dataset.userHalls || '').split(',').filter(Boolean); - if (!existing.includes(hallSlug)) existing.push(hallSlug); - liveHallBtn.dataset.userHalls = existing.join(','); - } + const hallName = (myHallsSelect && myHallsSelect.selectedOptions && myHallsSelect.selectedOptions[0]) + ? myHallsSelect.selectedOptions[0].textContent.replace(/\s*🔒\s*$/, '') + : hallSlug; + updateHallBadgeInDom(hallSlug, hallName, 'add', true); + if (window.invalidateItemCache) window.invalidateItemCache(itemid); if (window.flashMessage) window.flashMessage((window.f0ckI18n && window.f0ckI18n.hall_added) || 'Added to hall!'); - if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true }); + if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true, skipCache: true }); close(); } else { throw new Error(data.message || data.msg || 'Failed to add'); @@ -10814,8 +10898,10 @@ document.addEventListener('DOMContentLoaded', () => { }); const data = await resp.json(); if (data.success) { + updateHallBadgeInDom(hallSlug, '', 'remove', false); + if (window.invalidateItemCache) window.invalidateItemCache(itemid); if (window.flashMessage) window.flashMessage((window.f0ckI18n && window.f0ckI18n.hall_removed) || 'Removed from hall!'); - if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true }); + if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true, skipCache: true }); close(); } else { throw new Error(data.msg || 'Failed to remove from hall'); @@ -10845,8 +10931,10 @@ document.addEventListener('DOMContentLoaded', () => { if (data.success) { myHallsRemoveBtn.disabled = false; myHallsRemoveBtn.textContent = 'Remove'; + updateHallBadgeInDom(slug, '', 'remove', true); + if (window.invalidateItemCache) window.invalidateItemCache(itemid); window.flashMessage((window.f0ckI18n && window.f0ckI18n.hall_removed) || 'Removed from hall!'); - if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true }); + if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true, skipCache: true }); close(); } else { throw new Error(data.message || data.msg || 'Failed to remove'); @@ -10869,20 +10957,25 @@ document.addEventListener('DOMContentLoaded', () => { if (!confirm('Remove from this hall?')) return; const container = document.getElementById('comments-container'); - const itemid = container ? container.dataset.itemId : null; + const itemid = container ? container.dataset.itemId : (document.querySelector('.id-link')?.dataset.itemId || null); const hallSlug = removeBtn.dataset.hall; if (!itemid || !hallSlug) return window.flashMessage('Error: Missing itemid or hallSlug', 3000, 'error'); + const token = (window.f0ckSession && window.f0ckSession.csrf_token) || ''; const resp = await fetch('/mod/halls/remove', { method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: `id=${itemid}&hall=${encodeURIComponent(hallSlug)}` + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-Token': token + }, + body: `id=${itemid}&hall=${encodeURIComponent(hallSlug)}&csrf_token=${encodeURIComponent(token)}` }); const data = await resp.json(); if (data.success) { - // Refresh item view to remove hall badge - if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true }); + updateHallBadgeInDom(hallSlug, '', 'remove', false); + if (window.invalidateItemCache) window.invalidateItemCache(itemid); + if (window.loadItemAjax) window.loadItemAjax(window.location.href, true, { keepMedia: true, skipCache: true }); } else { window.flashMessage(data.msg || 'Failed to remove from hall', 3000, 'error'); } diff --git a/views/item-partial-legacy.html b/views/item-partial-legacy.html index 85b1c86..dee6f44 100644 --- a/views/item-partial-legacy.html +++ b/views/item-partial-legacy.html @@ -131,14 +131,11 @@ @if(item.is_oc) — OC@endif @endif - @if(!user_alternative_infobox) — @if(item.expires_at) — Expiring ({{ item.expires_in }})@endif@if(halls_enabled && item.primaryHall) — @endif @endif + @if(!user_alternative_infobox) — @if(item.expires_at) — Expiring ({{ item.expires_in }})@endif @endif - - @if(halls_enabled && item.primaryHall) - + @if(halls_enabled && item.primaryHall) — {{ item.primaryHall.name }}@if(item.otherHalls && item.otherHalls.length)+{{ item.otherHalls.length }}@each(item.otherHalls as oh){{ oh.name }}@endeach@endif - - @endif + @endif
diff --git a/views/item-partial-modern.html b/views/item-partial-modern.html index 1e17774..ff8b595 100644 --- a/views/item-partial-modern.html +++ b/views/item-partial-modern.html @@ -120,12 +120,10 @@
{{ (enable_item_slugs && item.slug) ? item.slug : item.id }} — [{!! item.author_display_name || item.username || 'unknown' !!}] @if(item.is_oc) — OC@endif - @if(halls_enabled && item.primaryHall) — @endif - @if(halls_enabled && item.primaryHall) - + + @if(halls_enabled && item.primaryHall) — {{ item.primaryHall.name }}@if(item.otherHalls && item.otherHalls.length)+{{ item.otherHalls.length }}@each(item.otherHalls as oh){{ oh.name }}@endeach@endif - — - @endif + — @endif @if(item.expires_at) — Expiring ({{ item.expires_in }})