gfdsgfds
This commit is contained in:
+308
-152
@@ -1924,6 +1924,39 @@ window.cancelAnimFrame = (function () {
|
||||
// Clear onara-active from any and all elements to guarantee only 1 item is selected
|
||||
document.querySelectorAll('.onara-active').forEach(el => el.classList.remove('onara-active'));
|
||||
|
||||
// Check for 4chan thread post card in the background thread
|
||||
let postNo = null;
|
||||
if (url) {
|
||||
const m = new URL(url, window.location.origin).pathname.match(/^\/4\/[a-z0-9_]+\/\d+\/(\d+)/i);
|
||||
if (m) postNo = m[1];
|
||||
}
|
||||
if (!postNo && itemid && /^\d+$/.test(String(itemid))) {
|
||||
postNo = String(itemid);
|
||||
}
|
||||
|
||||
let targetChanCard = null;
|
||||
if (postNo) {
|
||||
targetChanCard = document.getElementById('p' + postNo) || document.querySelector(`.chan-post-card[data-no="${postNo}"]`);
|
||||
}
|
||||
|
||||
if (targetChanCard) {
|
||||
document.querySelectorAll('.chan-post-card.active-thread-post').forEach(el => el.classList.remove('active-thread-post'));
|
||||
targetChanCard.classList.add('active-thread-post', 'onara-active');
|
||||
const chanThumb = targetChanCard.querySelector('.chan-thumb-link');
|
||||
if (chanThumb) chanThumb.classList.add('onara-active');
|
||||
|
||||
if (document.activeElement && document.activeElement !== targetChanCard && typeof document.activeElement.blur === 'function') {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
try {
|
||||
targetChanCard.scrollIntoView({ block: 'center', behavior: forceScroll ? 'instant' : 'smooth' });
|
||||
} catch (_) {
|
||||
targetChanCard.scrollIntoView();
|
||||
}
|
||||
return targetChanCard;
|
||||
}
|
||||
|
||||
let targetThumb = null;
|
||||
if (url) {
|
||||
try {
|
||||
@@ -2177,6 +2210,47 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
const currentSeq = ++_onaraSyncSeq;
|
||||
const urlObj = new URL(url || window.location.href, window.location.origin);
|
||||
if (urlObj.pathname.startsWith('/4/')) {
|
||||
const chanStream = document.querySelector('.chan-posts-stream');
|
||||
const chanM = urlObj.pathname.match(/^\/4\/([a-z0-9_]+)\/(\d+)/i);
|
||||
let sameThread = false;
|
||||
if (chanStream && chanM) {
|
||||
const currentUrlMatch = window.location.pathname.match(/^\/4\/([a-z0-9_]+)\/(\d+)/i);
|
||||
if (currentUrlMatch && currentUrlMatch[1].toLowerCase() === chanM[1].toLowerCase() && currentUrlMatch[2] === chanM[2]) {
|
||||
sameThread = true;
|
||||
} else if (document.querySelector('.chan-back-btn, .sidebar-chan-overview-btn, a[href*="/4/' + chanM[1] + '/' + chanM[2] + '"]')) {
|
||||
sameThread = true;
|
||||
}
|
||||
}
|
||||
if (sameThread) {
|
||||
updateOnaraActiveItem(itemid, url, false, slug, extraMeta);
|
||||
return;
|
||||
}
|
||||
if (chanM) {
|
||||
const threadUrl = `/4/${chanM[1]}/${chanM[2]}`;
|
||||
try {
|
||||
const pageRes = await fetch(threadUrl, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
if (pageRes.ok) {
|
||||
const html = await pageRes.text();
|
||||
const doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
const main = document.getElementById('main');
|
||||
const newMain = doc.querySelector('#main');
|
||||
if (main && newMain) {
|
||||
main.className = newMain.className;
|
||||
main.innerHTML = newMain.innerHTML;
|
||||
window._onaraReturnUrl = threadUrl;
|
||||
if (doc.title) window._onaraReturnTitle = doc.title;
|
||||
updateOnaraActiveItem(itemid, url, true, slug, extraMeta);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[ONARA] Failed to sync chan thread:', e);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const feedBasePath = urlObj.pathname.replace(/\/+$/, '').replace(/\/(?:\d+|[a-zA-Z0-9_-]{11})$/, '') || '/';
|
||||
|
||||
let targetPage = (typeof knownPage === 'number' && knownPage >= 1) ? knownPage : null;
|
||||
@@ -2364,6 +2438,10 @@ window.cancelAnimFrame = (function () {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (path.startsWith('/4/')) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const postsEl = document.querySelector('.posts');
|
||||
const activePagSpan = document.querySelector('.pagination span.btn.disabled, .pagination span.disabled');
|
||||
const pagPage = activePagSpan ? parseInt(activePagSpan.textContent.trim(), 10) : 0;
|
||||
@@ -2452,7 +2530,7 @@ window.cancelAnimFrame = (function () {
|
||||
window._onaraReturnTitle = null;
|
||||
|
||||
// Maintain focus on active item so user TAB navigation seamlessly resumes from current progress
|
||||
const activeThumb = document.querySelector('.posts > a.thumb.onara-active');
|
||||
const activeThumb = document.querySelector('.posts > a.thumb.onara-active, .chan-post-card.active-thread-post .chan-thumb-link, .chan-post-card.onara-active .chan-thumb-link');
|
||||
if (activeThumb) {
|
||||
try {
|
||||
activeThumb.focus({ preventScroll: true });
|
||||
@@ -4437,7 +4515,7 @@ window.cancelAnimFrame = (function () {
|
||||
const doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
const gridContent = doc.querySelector('#main') || doc.body;
|
||||
if (gridContent) {
|
||||
main.className = '';
|
||||
main.className = gridContent.className || '';
|
||||
main.innerHTML = gridContent.innerHTML;
|
||||
if (typeof window.initLazyLoading === 'function') {
|
||||
window.initLazyLoading();
|
||||
@@ -4452,7 +4530,7 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
|
||||
if (activeKey) {
|
||||
updateOnaraActiveItem(activeKey, window.location.href);
|
||||
updateOnaraActiveItem(activeKey, window.location.href, true);
|
||||
if (typeof window.trackVisit === 'function') {
|
||||
window.trackVisit(activeKey);
|
||||
}
|
||||
@@ -4473,7 +4551,7 @@ window.cancelAnimFrame = (function () {
|
||||
const keySegments = pathSegments.filter(s => /^\d+$/.test(s) || /^[a-zA-Z0-9_-]{11}$/.test(s));
|
||||
const activeKey = keySegments.length ? keySegments[keySegments.length - 1] : null;
|
||||
if (activeKey) {
|
||||
updateOnaraActiveItem(activeKey, window.location.href);
|
||||
updateOnaraActiveItem(activeKey, window.location.href, true);
|
||||
if (typeof window.trackVisit === 'function') {
|
||||
window.trackVisit(activeKey);
|
||||
}
|
||||
@@ -11261,7 +11339,10 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
if (isItem) {
|
||||
isNavigating = false;
|
||||
return loadItemAjax(url, replace, options);
|
||||
const shouldInherit = (options.skipInherit || options.inheritContext === false) ? false : (options.inheritContext !== undefined ? !!options.inheritContext : false);
|
||||
const itemOptions = { ...options };
|
||||
if (replace) itemOptions.replace = true;
|
||||
return loadItemAjax(url, shouldInherit, itemOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -12369,7 +12450,12 @@ window.cancelAnimFrame = (function () {
|
||||
// Prev/next arrows in item view always use explicit /h/foo/<id> or /tag/foo/<id> hrefs
|
||||
// (via link.main), so they are unaffected by this guard.
|
||||
// Note: Notification links call loadItemAjax(href, false) to skip inheritance directly.
|
||||
if (inheritContext && !isChanUrl) {
|
||||
const targetUrlObj = new URL(url, window.location.origin);
|
||||
const targetPath = targetUrlObj.pathname;
|
||||
const hasContextPrefix = /^\/(?:tag|h|user|favs|4)\//.test(targetPath);
|
||||
const isBareItem = !hasContextPrefix;
|
||||
|
||||
if (inheritContext && !isChanUrl && !isBareItem && !options.skipInherit) {
|
||||
if (!tag) {
|
||||
const wTagMatch = window.location.href.match(/\/tag\/([^/?]+)/);
|
||||
if (wTagMatch) tag = decodeURIComponent(wTagMatch[1]);
|
||||
@@ -12592,6 +12678,8 @@ window.cancelAnimFrame = (function () {
|
||||
promoteOnaraModals();
|
||||
const onaraModal = document.getElementById('onara-modal');
|
||||
if (onaraModal) onaraModal.scrollTo(0, 0);
|
||||
updateOnaraActiveItem(itemid, url, false, _cachedItem.slug, null);
|
||||
syncOnaraBackgroundGrid(itemid, url, _cachedItem.page, _cachedItem.slug, null);
|
||||
} else if (options.skipPush && history.state?.scroll !== undefined) {
|
||||
requestAnimationFrame(() => window.scrollTo(0, history.state.scroll));
|
||||
} else if (!options.keepMedia) {
|
||||
@@ -12601,7 +12689,10 @@ window.cancelAnimFrame = (function () {
|
||||
const _hash = new URL(url, window.location.origin).hash;
|
||||
const itemKey = (window.f0ckSession?.enable_item_slugs !== false && _cachedItem.slug) ? _cachedItem.slug : (_cachedItem.slug || itemid);
|
||||
let _pushUrl = `/${itemKey}`;
|
||||
if (userHall && userHallOwner) _pushUrl = `/user/${encodeURIComponent(userHallOwner)}/hall/${encodeURIComponent(userHall)}/${itemKey}`;
|
||||
if (isChanUrl) {
|
||||
const u = new URL(url, window.location.origin);
|
||||
_pushUrl = u.pathname + u.search + u.hash;
|
||||
} else if (userHall && userHallOwner) _pushUrl = `/user/${encodeURIComponent(userHallOwner)}/hall/${encodeURIComponent(userHall)}/${itemKey}`;
|
||||
else if (user) { _pushUrl = `/user/${encodeURIComponent(user)}/${itemKey}`; if (isFavs) _pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemKey}`; }
|
||||
else if (isFavs) _pushUrl = `/favs/${itemKey}`;
|
||||
else if (tag) _pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g,',').replace(/%20/g,' ')}/${itemKey}`;
|
||||
@@ -12952,7 +13043,11 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
if (!options.keepMedia && !options.skipPush) {
|
||||
// We overwrite proper URL even if the link clicked was "naked"
|
||||
history.pushState({ onara: useOnara }, '', pushUrl);
|
||||
if (options.replace || options.replaceState) {
|
||||
history.replaceState({ onara: useOnara }, '', pushUrl);
|
||||
} else {
|
||||
history.pushState({ onara: useOnara }, '', pushUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.keepMedia) {
|
||||
@@ -13507,121 +13602,195 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
window.openChanRehostModal = function({ itemId, isPlayerView = false, onSave = null }) {
|
||||
window.openChanRehostModal = function({ itemId, onSave = null, anchorEl = null }) {
|
||||
if (!itemId) return;
|
||||
|
||||
let modal = document.getElementById('chan-rehost-modal');
|
||||
if (!modal) {
|
||||
modal = document.createElement('div');
|
||||
modal.id = 'chan-rehost-modal';
|
||||
modal.className = 'modal-overlay chan-rehost-modal';
|
||||
modal.style.display = 'none';
|
||||
document.body.appendChild(modal);
|
||||
}
|
||||
|
||||
if (!anchorEl) {
|
||||
anchorEl = document.querySelector(`.chan-rehost-edit-btn[data-item-id="${itemId}"]`) ||
|
||||
document.querySelector(`.chan-rehost-btn[href="/${itemId}"]`) ||
|
||||
document.querySelector(`.chan-rehost-action-btn[href="/${itemId}"]`) ||
|
||||
document.querySelector(`#rehost-box-${itemId} .chan-rehosted-wrap`) ||
|
||||
document.querySelector(`#rehost-box-${itemId}`) ||
|
||||
document.getElementById('chan-item-rehost-btn');
|
||||
}
|
||||
|
||||
modal.className = 'modal-overlay chan-rehost-modal chan-rehost-popup-mode';
|
||||
modal.style.display = 'block';
|
||||
document.body.classList.remove('modal-open');
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="modal-content chan-rehost-modal-content">
|
||||
<div class="chan-rehost-modal-header">
|
||||
<h3 class="chan-rehost-modal-title">
|
||||
<i class="fa-solid fa-cloud-arrow-up" style="color: #4ade80;"></i>
|
||||
<span>Rehost Settings</span>
|
||||
<a href="/${itemId}" target="_blank" class="chan-rehost-modal-post-link" title="Open post in new tab">#${itemId} <i class="fa-solid fa-arrow-up-right-from-square"></i></a>
|
||||
</h3>
|
||||
<button type="button" class="chan-rehost-modal-close" aria-label="Close">×</button>
|
||||
<div class="modal-content chan-rehost-modal-content chan-rehost-popup-content" role="dialog" aria-label="Rehost Rating">
|
||||
<div class="chan-rehost-popup-header">
|
||||
<span class="chan-rehost-popup-title">
|
||||
<i class="fa-solid fa-shield-halved"></i>
|
||||
<span>Rating</span>
|
||||
</span>
|
||||
<button type="button" class="chan-rehost-modal-close chan-rehost-popup-close" aria-label="Close">×</button>
|
||||
</div>
|
||||
<div class="chan-rehost-modal-body">
|
||||
<div class="chan-rehost-form-group">
|
||||
<label><i class="fa-solid fa-shield-halved"></i> Rating</label>
|
||||
<div class="chan-rehost-ratings" id="chan-rehost-rating-group">
|
||||
<button type="button" class="chan-rehost-pill untagged active" data-rating="untagged"><i class="fa-solid fa-question"></i> Untagged</button>
|
||||
<button type="button" class="chan-rehost-pill sfw" data-rating="sfw"><i class="fa-solid fa-shield"></i> SFW</button>
|
||||
<button type="button" class="chan-rehost-pill nsfw" data-rating="nsfw"><i class="fa-solid fa-eye-slash"></i> NSFW</button>
|
||||
<button type="button" class="chan-rehost-pill nsfl" data-rating="nsfl"><i class="fa-solid fa-skull"></i> NSFL</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chan-rehost-form-group">
|
||||
<label><i class="fa-solid fa-tags"></i> Tags (optional)</label>
|
||||
<input type="text" id="chan-rehost-tags-input" class="chan-rehost-input" placeholder="e.g. music, webm, anime (comma-separated)" autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="chan-rehost-form-group">
|
||||
<label><i class="fa-solid fa-comment"></i> First Comment (optional)</label>
|
||||
<textarea id="chan-rehost-comment-input" class="chan-rehost-input chan-rehost-textarea" placeholder="Add a comment on this post..." rows="2"></textarea>
|
||||
</div>
|
||||
<div class="chan-rehost-ratings chan-rehost-popup-ratings" id="chan-rehost-rating-group">
|
||||
<button type="button" class="chan-rehost-pill untagged active" data-rating="untagged" title="Untagged"><i class="fa-solid fa-question"></i> Untagged</button>
|
||||
<button type="button" class="chan-rehost-pill sfw" data-rating="sfw" title="Safe for Work"><i class="fa-solid fa-shield"></i> SFW</button>
|
||||
<button type="button" class="chan-rehost-pill nsfw" data-rating="nsfw" title="Not Safe for Work"><i class="fa-solid fa-eye-slash"></i> NSFW</button>
|
||||
<button type="button" class="chan-rehost-pill nsfl" data-rating="nsfl" title="Not Safe for Life"><i class="fa-solid fa-skull"></i> NSFL</button>
|
||||
</div>
|
||||
<div class="chan-rehost-modal-actions">
|
||||
<button type="button" class="btn btn-secondary chan-rehost-modal-cancel">Close</button>
|
||||
${isPlayerView ? `<button type="button" class="btn btn-info chan-rehost-modal-save-view" style="background:#0284c7;color:#fff;border:none;border-radius:6px;padding:8px 14px;cursor:pointer;font-weight:600;"><i class="fa-solid fa-play"></i> Save & View Post</button>` : ''}
|
||||
<button type="button" class="btn btn-primary chan-rehost-modal-save" style="background:#16a34a;color:#fff;border:none;border-radius:6px;padding:8px 14px;cursor:pointer;font-weight:600;"><i class="fa-solid fa-check"></i> Save Details</button>
|
||||
<div class="chan-rehost-popup-actions">
|
||||
<button type="button" class="btn btn-primary chan-rehost-modal-save chan-rehost-popup-save-btn">
|
||||
<i class="fa-solid fa-check"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
modal.style.display = 'flex';
|
||||
document.body.classList.add('modal-open');
|
||||
const content = modal.querySelector('.chan-rehost-modal-content');
|
||||
let positionHandler = null;
|
||||
|
||||
const updatePopupPosition = () => {
|
||||
if (!content) return;
|
||||
const cRect = content.getBoundingClientRect();
|
||||
const margin = 8;
|
||||
const pad = 8;
|
||||
|
||||
if (anchorEl && typeof anchorEl.getBoundingClientRect === 'function') {
|
||||
const rect = anchorEl.getBoundingClientRect();
|
||||
let left = rect.left + (rect.width / 2) - (cRect.width / 2);
|
||||
if (left < pad) left = pad;
|
||||
if (left + cRect.width > window.innerWidth - pad) {
|
||||
left = window.innerWidth - cRect.width - pad;
|
||||
}
|
||||
|
||||
let top;
|
||||
if (rect.top >= cRect.height + margin + pad) {
|
||||
top = rect.top - cRect.height - margin;
|
||||
content.dataset.pos = 'above';
|
||||
} else {
|
||||
top = rect.bottom + margin;
|
||||
content.dataset.pos = 'below';
|
||||
}
|
||||
|
||||
if (top + cRect.height > window.innerHeight - pad) {
|
||||
top = window.innerHeight - cRect.height - pad;
|
||||
}
|
||||
if (top < pad) top = pad;
|
||||
|
||||
content.style.position = 'fixed';
|
||||
content.style.left = `${Math.round(left)}px`;
|
||||
content.style.top = `${Math.round(top)}px`;
|
||||
content.style.right = 'auto';
|
||||
content.style.bottom = 'auto';
|
||||
content.style.margin = '0';
|
||||
} else {
|
||||
content.style.position = 'fixed';
|
||||
content.style.left = `${Math.round((window.innerWidth - cRect.width) / 2)}px`;
|
||||
content.style.top = `${Math.round((window.innerHeight - cRect.height) / 2)}px`;
|
||||
}
|
||||
};
|
||||
|
||||
updatePopupPosition();
|
||||
requestAnimationFrame(updatePopupPosition);
|
||||
setTimeout(updatePopupPosition, 25);
|
||||
setTimeout(updatePopupPosition, 100);
|
||||
|
||||
positionHandler = () => requestAnimationFrame(updatePopupPosition);
|
||||
window.addEventListener('scroll', positionHandler, { passive: true, capture: true });
|
||||
window.addEventListener('resize', positionHandler, { passive: true });
|
||||
|
||||
const ratingGroup = modal.querySelector('#chan-rehost-rating-group');
|
||||
let selectedRating = 'untagged';
|
||||
let userSelectedRating = false;
|
||||
let userEditedTags = false;
|
||||
|
||||
ratingGroup.addEventListener('click', (e) => {
|
||||
const pill = e.target.closest('.chan-rehost-pill');
|
||||
if (!pill) return;
|
||||
userSelectedRating = true;
|
||||
ratingGroup.querySelectorAll('.chan-rehost-pill').forEach(b => b.classList.remove('active'));
|
||||
pill.classList.add('active');
|
||||
selectedRating = pill.dataset.rating;
|
||||
});
|
||||
if (ratingGroup) {
|
||||
ratingGroup.addEventListener('click', (e) => {
|
||||
const pill = e.target.closest('.chan-rehost-pill');
|
||||
if (!pill) return;
|
||||
userSelectedRating = true;
|
||||
ratingGroup.querySelectorAll('.chan-rehost-pill').forEach(b => b.classList.remove('active'));
|
||||
pill.classList.add('active');
|
||||
selectedRating = pill.dataset.rating;
|
||||
});
|
||||
}
|
||||
|
||||
const tagsInput = modal.querySelector('#chan-rehost-tags-input');
|
||||
const commentInput = modal.querySelector('#chan-rehost-comment-input');
|
||||
|
||||
tagsInput.addEventListener('input', () => {
|
||||
userEditedTags = true;
|
||||
});
|
||||
|
||||
// Pre-fill current rating and tags if available (don't overwrite if user already interacted)
|
||||
// Pre-fill current rating if available
|
||||
fetch(`/api/v2/scroller/rehost/details/${itemId}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data && data.success) {
|
||||
if (data.rating && data.rating !== 'untagged' && !userSelectedRating) {
|
||||
if (data.rating && data.rating !== 'untagged' && !userSelectedRating && ratingGroup) {
|
||||
selectedRating = data.rating;
|
||||
ratingGroup.querySelectorAll('.chan-rehost-pill').forEach(b => {
|
||||
b.classList.toggle('active', b.dataset.rating === data.rating);
|
||||
});
|
||||
}
|
||||
if (data.tags && data.tags.length && !userEditedTags && !tagsInput.value) {
|
||||
tagsInput.value = data.tags.map(t => (typeof t === 'string' ? t : t.tag)).join(', ');
|
||||
}
|
||||
}
|
||||
}).catch(() => {});
|
||||
|
||||
setTimeout(() => tagsInput.focus(), 100);
|
||||
|
||||
let isClosing = false;
|
||||
const closeModal = () => {
|
||||
modal.style.display = 'none';
|
||||
document.body.classList.remove('modal-open');
|
||||
if (isClosing) return;
|
||||
isClosing = true;
|
||||
document.removeEventListener('keydown', handleKeydown);
|
||||
document.removeEventListener('click', handleDocClick);
|
||||
if (positionHandler) {
|
||||
window.removeEventListener('scroll', positionHandler, { capture: true });
|
||||
window.removeEventListener('resize', positionHandler);
|
||||
positionHandler = null;
|
||||
}
|
||||
document.body.classList.remove('modal-open');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
if (modal.parentNode) {
|
||||
modal.parentNode.removeChild(modal);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeydown = (e) => {
|
||||
if (e.key === 'Escape') closeModal();
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
closeModal();
|
||||
}
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
saveDetails();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleKeydown);
|
||||
|
||||
modal.querySelector('.chan-rehost-modal-close').onclick = closeModal;
|
||||
modal.querySelector('.chan-rehost-modal-cancel').onclick = closeModal;
|
||||
const closeBtn = modal.querySelector('.chan-rehost-modal-close');
|
||||
if (closeBtn) {
|
||||
closeBtn.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeModal();
|
||||
};
|
||||
}
|
||||
modal.onclick = (e) => {
|
||||
if (e.target === modal) closeModal();
|
||||
if (e.target === modal) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
const handleDocClick = (e) => {
|
||||
if (!modal || isClosing) return;
|
||||
if (!content.contains(e.target)) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', handleDocClick);
|
||||
}, 50);
|
||||
|
||||
const updateDomAfterSave = (resData) => {
|
||||
try {
|
||||
const finalRating = resData.rating || selectedRating || 'untagged';
|
||||
const finalTags = Array.isArray(resData.tags) ? resData.tags : [];
|
||||
|
||||
// 1. Update #tags container
|
||||
const tagsContainer = document.querySelector('#tags');
|
||||
@@ -13646,29 +13815,6 @@ window.cancelAnimFrame = (function () {
|
||||
ratingEl.dataset.itemId = itemId;
|
||||
ratingEl.dataset.rating = finalRating;
|
||||
ratingEl.innerHTML = `<span class="rating-label">${rMeta.label}</span>`;
|
||||
|
||||
// Remove previous tag badges and re-render new ones
|
||||
tagsContainer.querySelectorAll('.tag-badge').forEach(el => el.remove());
|
||||
finalTags.forEach(t => {
|
||||
const tagName = typeof t === 'string' ? t : (t.tag || '');
|
||||
const tagNorm = typeof t === 'string' ? tagName.toLowerCase().replace(/[^a-z0-9_-]/g, '') : (t.normalized || tagName);
|
||||
const tagBadge = (typeof t === 'object' && t.badge) ? t.badge : 'badge-light';
|
||||
const tagId = (typeof t === 'object' && t.id) ? t.id : '';
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.className = `badge ${tagBadge} tag-badge`;
|
||||
if (tagId) span.dataset.tagId = tagId;
|
||||
span.dataset.tag = tagName;
|
||||
span.dataset.tagNormalized = tagNorm;
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = `/tag/${encodeURIComponent(tagNorm)}`;
|
||||
a.className = 'tag-name';
|
||||
a.textContent = tagName;
|
||||
span.appendChild(a);
|
||||
|
||||
tagsContainer.appendChild(span);
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Update .item-layout-container
|
||||
@@ -13717,14 +13863,21 @@ window.cancelAnimFrame = (function () {
|
||||
<a href="/${itemId}" class="chan-rehost-action-btn rehosted" title="View Post #${itemId}" style="display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:4px 12px;border-radius:6px;font-size:0.8rem;background:rgba(74,222,128,0.2);color:#4ade80;border:1px solid rgba(74,222,128,0.4);text-decoration:none;font-weight:600;white-space:nowrap;flex-shrink:0;">
|
||||
<i class="fa-solid fa-check"></i> #${itemId}
|
||||
</a>
|
||||
<button type="button" class="chan-rehost-edit-btn" data-item-id="${itemId}" title="Edit rating, tags & comment" style="display:inline-flex;align-items:center;padding:3px 7px;border-radius:6px;font-size:0.8rem;background:rgba(255,255,255,0.1);color:#fff;border:1px solid rgba(255,255,255,0.2);cursor:pointer;">
|
||||
<button type="button" class="chan-rehost-edit-btn" data-item-id="${itemId}" title="Edit rating" style="display:inline-flex;align-items:center;padding:3px 7px;border-radius:6px;font-size:0.8rem;background:rgba(255,255,255,0.1);color:#fff;border:1px solid rgba(255,255,255,0.2);cursor:pointer;">
|
||||
<i class="fa-solid fa-pen"></i>
|
||||
</button>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
// 7. Invalidate caches
|
||||
// 7. Update thread edit buttons if present on page
|
||||
const threadEditBtns = document.querySelectorAll(`.chan-rehost-edit-btn[data-item-id="${itemId}"]`);
|
||||
threadEditBtns.forEach(b => {
|
||||
b.dataset.rating = finalRating;
|
||||
b.title = 'Edit rating (' + finalRating.toUpperCase() + ')';
|
||||
});
|
||||
|
||||
// 8. Invalidate caches
|
||||
if (typeof window.invalidateItemCache === 'function') {
|
||||
window.invalidateItemCache(itemId);
|
||||
window.invalidateItemCache(window.location.pathname);
|
||||
@@ -13734,11 +13887,12 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
const saveDetails = async (andView = false) => {
|
||||
const saveDetails = async () => {
|
||||
const saveBtn = modal.querySelector('.chan-rehost-modal-save');
|
||||
const saveViewBtn = modal.querySelector('.chan-rehost-modal-save-view');
|
||||
if (saveBtn) saveBtn.disabled = true;
|
||||
if (saveViewBtn) saveViewBtn.disabled = true;
|
||||
if (saveBtn) {
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Saving…';
|
||||
}
|
||||
|
||||
try {
|
||||
const csrfToken = window.f0ckSession?.csrf_token || '';
|
||||
@@ -13746,57 +13900,46 @@ window.cancelAnimFrame = (function () {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {})
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
item_id: itemId,
|
||||
item_id: String(itemId),
|
||||
rating: selectedRating,
|
||||
tags: tagsInput.value.trim(),
|
||||
comment: commentInput.value.trim()
|
||||
tags: '',
|
||||
comment: ''
|
||||
})
|
||||
});
|
||||
|
||||
const resData = await resp.json();
|
||||
if (resData.success) {
|
||||
if (typeof window.showToast === 'function') {
|
||||
window.showToast('Details saved for #' + itemId + '!');
|
||||
}
|
||||
closeModal();
|
||||
if (typeof window.showToast === 'function') {
|
||||
window.showToast('Rating saved for #' + itemId + '!');
|
||||
}
|
||||
updateDomAfterSave(resData);
|
||||
if (typeof onSave === 'function') {
|
||||
onSave(resData);
|
||||
}
|
||||
if (andView) {
|
||||
if (typeof window.loadItemAjax === 'function') {
|
||||
window.loadItemAjax('/' + itemId, true, { skipCache: true });
|
||||
} else {
|
||||
window.location.href = '/' + itemId;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(resData.msg || 'Failed to save details');
|
||||
if (saveBtn) saveBtn.disabled = false;
|
||||
if (saveViewBtn) saveViewBtn.disabled = false;
|
||||
alert(resData.msg || 'Failed to save rating');
|
||||
if (saveBtn) {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.innerHTML = '<i class="fa-solid fa-check"></i> Save';
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[REHOST-MODAL] Save error:', err);
|
||||
alert('Error saving details: network error');
|
||||
if (saveBtn) saveBtn.disabled = false;
|
||||
if (saveViewBtn) saveViewBtn.disabled = false;
|
||||
alert('Error saving rating: network error');
|
||||
if (saveBtn) {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.innerHTML = '<i class="fa-solid fa-check"></i> Save';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
modal.querySelector('.chan-rehost-modal-save').onclick = () => saveDetails(false);
|
||||
if (isPlayerView && modal.querySelector('.chan-rehost-modal-save-view')) {
|
||||
modal.querySelector('.chan-rehost-modal-save-view').onclick = () => saveDetails(true);
|
||||
}
|
||||
|
||||
tagsInput.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
saveDetails(false);
|
||||
}
|
||||
});
|
||||
const saveBtn = modal.querySelector('.chan-rehost-modal-save');
|
||||
if (saveBtn) saveBtn.onclick = () => saveDetails();
|
||||
};
|
||||
|
||||
if (target.closest('#chan-item-rehost-btn')) {
|
||||
@@ -13845,7 +13988,7 @@ window.cancelAnimFrame = (function () {
|
||||
<a href="/${data.item_id}" class="chan-rehost-action-btn rehosted" title="View Post #${data.item_id}" style="display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:4px 12px;border-radius:6px;font-size:0.8rem;background:rgba(74,222,128,0.2);color:#4ade80;border:1px solid rgba(74,222,128,0.4);text-decoration:none;font-weight:600;white-space:nowrap;flex-shrink:0;">
|
||||
<i class="fa-solid fa-check"></i> #${data.item_id}
|
||||
</a>
|
||||
<button type="button" class="chan-rehost-edit-btn" data-item-id="${data.item_id}" title="Edit rating, tags & comment" style="display:inline-flex;align-items:center;padding:3px 7px;border-radius:6px;font-size:0.8rem;background:rgba(255,255,255,0.1);color:#fff;border:1px solid rgba(255,255,255,0.2);cursor:pointer;">
|
||||
<button type="button" class="chan-rehost-edit-btn" data-item-id="${data.item_id}" title="Edit rating" style="display:inline-flex;align-items:center;padding:3px 7px;border-radius:6px;font-size:0.8rem;background:rgba(255,255,255,0.1);color:#fff;border:1px solid rgba(255,255,255,0.2);cursor:pointer;">
|
||||
<i class="fa-solid fa-pen"></i>
|
||||
</button>
|
||||
</span>
|
||||
@@ -13854,7 +13997,8 @@ window.cancelAnimFrame = (function () {
|
||||
window.showToast(data.repost ? 'Already on site — linked to existing post' : 'Rehosted successfully!');
|
||||
}
|
||||
if (typeof window.openChanRehostModal === 'function') {
|
||||
window.openChanRehostModal({ itemId: data.item_id, isPlayerView: true });
|
||||
const newAnchor = document.querySelector(`.chan-rehost-edit-btn[data-item-id="${data.item_id}"]`) || document.querySelector(`.chan-rehost-action-btn[href="/${data.item_id}"]`);
|
||||
window.openChanRehostModal({ itemId: data.item_id, anchorEl: newAnchor });
|
||||
}
|
||||
} else {
|
||||
btn.classList.remove('loading');
|
||||
@@ -13875,8 +14019,7 @@ window.cancelAnimFrame = (function () {
|
||||
const editBtn = target.closest('.chan-rehost-edit-btn');
|
||||
const itemId = editBtn?.dataset?.itemId;
|
||||
if (itemId && typeof window.openChanRehostModal === 'function') {
|
||||
const isPlayer = !!document.querySelector('.media-object');
|
||||
window.openChanRehostModal({ itemId: Number(itemId), isPlayerView: isPlayer });
|
||||
window.openChanRehostModal({ itemId: Number(itemId), anchorEl: editBtn });
|
||||
}
|
||||
} else if (target.closest('#togglebg')) {
|
||||
e.preventDefault();
|
||||
@@ -19403,20 +19546,33 @@ class NotificationSystem {
|
||||
const currentUserId = window.f0ckSession?.id ? Number(window.f0ckSession.id) : null;
|
||||
const favoBtns = document.querySelectorAll('#a_favo');
|
||||
if (favoBtns.length && (currentUserId || currentUser)) {
|
||||
const isNowFav = Array.isArray(data.favs) && data.favs.some(f => {
|
||||
if (currentUserId && f.user_id && Number(f.user_id) === currentUserId) return true;
|
||||
const fUser = (f.user || '').toLowerCase();
|
||||
const fLogin = (f.login || '').toLowerCase();
|
||||
if (currentUser && currentUser !== 'anonymous' && (fUser === currentUser || fLogin === currentUser)) return true;
|
||||
if (currentLogin && (fUser === currentLogin || fLogin === currentLogin)) return true;
|
||||
return false;
|
||||
});
|
||||
favoBtns.forEach(favoBtn => {
|
||||
favoBtn.classList.remove('fa-spinner', 'fa-spin');
|
||||
favoBtn.classList.add('iconset', 'fa-heart');
|
||||
favoBtn.classList.toggle('fa-solid', isNowFav);
|
||||
favoBtn.classList.toggle('fa-regular', !isNowFav);
|
||||
});
|
||||
if (currentUserId && data.user_id && Number(data.user_id) === currentUserId && data.favorited !== undefined) {
|
||||
const isNowFav = !!data.favorited;
|
||||
favoBtns.forEach(favoBtn => {
|
||||
favoBtn.classList.remove('fa-spinner', 'fa-spin');
|
||||
favoBtn.classList.add('iconset', 'fa-heart');
|
||||
favoBtn.classList.toggle('fa-solid', isNowFav);
|
||||
favoBtn.classList.toggle('fa-regular', !isNowFav);
|
||||
});
|
||||
} else if (!data.user_id || (currentUserId && Number(data.user_id) !== currentUserId)) {
|
||||
// Notification is from someone else: only update to favorited if current user is found in list
|
||||
const foundSelf = Array.isArray(data.favs) && data.favs.some(f => {
|
||||
if (currentUserId && f.user_id && Number(f.user_id) === currentUserId) return true;
|
||||
const fUser = (f.user || '').toLowerCase();
|
||||
const fLogin = (f.login || '').toLowerCase();
|
||||
if (currentUser && currentUser !== 'anonymous' && (fUser === currentUser || fLogin === currentUser)) return true;
|
||||
if (currentLogin && (fUser === currentLogin || fLogin === currentLogin)) return true;
|
||||
return false;
|
||||
});
|
||||
if (foundSelf) {
|
||||
favoBtns.forEach(favoBtn => {
|
||||
favoBtn.classList.remove('fa-spinner', 'fa-spin');
|
||||
favoBtn.classList.add('iconset', 'fa-heart');
|
||||
favoBtn.classList.add('fa-solid');
|
||||
favoBtn.classList.remove('fa-regular');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.favs) || data.favs.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user