gsdf
This commit is contained in:
+456
-32
@@ -1030,26 +1030,53 @@
|
||||
|
||||
let lastBoundMode = typeof window.activeMode !== 'undefined' ? window.activeMode : null;
|
||||
|
||||
const getCurrentMimeFilter = () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const qMime = urlParams.get('mime');
|
||||
if (qMime !== null) return qMime.trim();
|
||||
const cookieMime = document.cookie.split('; ').find(row => row.startsWith('mime='));
|
||||
if (cookieMime) {
|
||||
const val = cookieMime.split('=')[1];
|
||||
if (typeof val === 'string') return decodeURIComponent(val).trim();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
let lastBoundMime = getCurrentMimeFilter();
|
||||
|
||||
// Handle AJAX item loads
|
||||
document.addEventListener('f0ck:contentLoaded', () => {
|
||||
const currentMode = typeof window.activeMode !== 'undefined' ? window.activeMode : null;
|
||||
const modeChanged = lastBoundMode !== null && lastBoundMode !== currentMode;
|
||||
lastBoundMode = currentMode;
|
||||
|
||||
window.f0ckDebug("Sidebar Activity: Page transition detected", modeChanged ? "(Mode changed)" : "");
|
||||
const currentMime = getCurrentMimeFilter();
|
||||
const mimeChanged = lastBoundMime !== null && lastBoundMime !== currentMime;
|
||||
lastBoundMime = currentMime;
|
||||
|
||||
if (modeChanged) {
|
||||
window._sidebarActivityCache = [];
|
||||
lastRenderedIds = '';
|
||||
currentPage = 1;
|
||||
hasMore = true;
|
||||
loadActivity(false); // Force reload with loading state
|
||||
window.f0ckDebug("Sidebar Activity: Page transition detected", modeChanged ? "(Mode changed)" : "", mimeChanged ? "(Mime changed)" : "");
|
||||
|
||||
if (modeChanged || mimeChanged) {
|
||||
if (modeChanged) {
|
||||
window._sidebarActivityCache = [];
|
||||
lastRenderedIds = '';
|
||||
currentPage = 1;
|
||||
hasMore = true;
|
||||
loadActivity(false); // Force reload with loading state
|
||||
}
|
||||
|
||||
recommendationsLoaded = false;
|
||||
tagFeedLoaded = false;
|
||||
tagFeedNeedsReload = true;
|
||||
const activeTabEl = document.querySelector('.sidebar-tab.active')?.dataset.tab;
|
||||
let savedTab = 'comments';
|
||||
try { savedTab = localStorage.getItem('sidebar_active_tab'); } catch (_) {}
|
||||
if (savedTab === 'videos') {
|
||||
loadVideoRecommendations(false);
|
||||
const currentTabName = activeTabEl || savedTab;
|
||||
|
||||
if (currentTabName === 'videos' || currentTabName === 'recommendations') {
|
||||
loadRecommendations(false);
|
||||
} else if (currentTabName === 'tag' && currentTag) {
|
||||
loadTagFeed(true);
|
||||
}
|
||||
} else {
|
||||
// Immediately render from cache to avoid flicker
|
||||
@@ -1059,6 +1086,27 @@
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('f0ck:mimeChanged', (e) => {
|
||||
const newMime = (e.detail && typeof e.detail.mime !== 'undefined') ? e.detail.mime : getCurrentMimeFilter();
|
||||
window.f0ckDebug("Sidebar Activity: MIME filter changed", newMime);
|
||||
lastBoundMime = newMime;
|
||||
|
||||
recommendationsLoaded = false;
|
||||
tagFeedLoaded = false;
|
||||
tagFeedNeedsReload = true;
|
||||
|
||||
const activeTabEl = document.querySelector('.sidebar-tab.active')?.dataset.tab;
|
||||
let savedTab = 'comments';
|
||||
try { savedTab = localStorage.getItem('sidebar_active_tab'); } catch (_) {}
|
||||
const currentTabName = activeTabEl || savedTab;
|
||||
|
||||
if (currentTabName === 'videos' || currentTabName === 'recommendations') {
|
||||
loadRecommendations(false);
|
||||
} else if (currentTabName === 'tag' && currentTag) {
|
||||
loadTagFeed(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Sync sidebar and comments-list layout on initial page load (Legacy View Only)
|
||||
if (typeof syncSidebarAndComments === 'function') {
|
||||
syncSidebarAndComments();
|
||||
@@ -1074,7 +1122,7 @@
|
||||
let recIoSentinel = null;
|
||||
let recObserver = null;
|
||||
|
||||
const renderVideoCard = (video) => {
|
||||
const renderVideoCard = (video, options = {}) => {
|
||||
const videoKey = (window.f0ckSession?.enable_item_slugs && video.slug) ? video.slug : video.id;
|
||||
const rClass = video.rating_class || 'untagged';
|
||||
const blurNsfw = localStorage.getItem('blurNsfw') === 'true';
|
||||
@@ -1140,7 +1188,7 @@
|
||||
`;
|
||||
} else {
|
||||
thumbContentHtml = `
|
||||
<img src="${thumbUrl}" class="sidebar-video-thumb" alt="${escapeHtml(displayTitle)}" loading="lazy" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.classList.remove('hidden');" />
|
||||
<img src="${thumbUrl}" class="sidebar-video-thumb" alt="${escapeHtml(displayTitle)}" loading="lazy" draggable="false" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.classList.remove('hidden');" />
|
||||
<div class="sidebar-media-placeholder ${isAudio ? 'audio' : ''} hidden">
|
||||
<i class="${overlayIcon}"></i>
|
||||
</div>
|
||||
@@ -1151,12 +1199,18 @@
|
||||
? `<span class="sidebar-personalized-pill" title="Personalized recommendation based on your interests"><i class="fa-solid fa-wand-magic-sparkles"></i> For You</span>`
|
||||
: '';
|
||||
|
||||
const isTagFeed = options.isTagFeed || false;
|
||||
const tagContext = options.tag || null;
|
||||
const targetHref = (isTagFeed && tagContext)
|
||||
? `/tag/${encodeURIComponent(tagContext).replace(/%2C/g, ',').replace(/%20/g, ' ')}/${videoKey}`
|
||||
: `/${videoKey}`;
|
||||
const activeClass = options.isActive ? ' active-tag-item' : '';
|
||||
|
||||
return `
|
||||
<div class="sidebar-video-card" data-id="${video.id}" data-slug="${escapeHtml(video.slug || '')}" data-personalized="${video.personalized ? 'true' : 'false'}">
|
||||
<a href="/${videoKey}" class="sidebar-video-link" data-mode="${rClass}" data-inherit-context="false">
|
||||
<div class="sidebar-video-thumb-wrap">
|
||||
<div class="sidebar-video-card${activeClass}" data-id="${video.id}" data-slug="${escapeHtml(video.slug || '')}" data-file="${escapeHtml(video.dest || '')}" data-mime="${escapeHtml(video.mime || '')}" data-ext="${escapeHtml(ext ? ext.toLowerCase() : '')}" data-mode="${rClass}" data-personalized="${video.personalized ? 'true' : 'false'}">
|
||||
<a href="${targetHref}" class="sidebar-video-link" data-mode="${rClass}" data-inherit-context="false">
|
||||
<div class="sidebar-video-thumb-wrap" data-file="${escapeHtml(video.dest || '')}" data-mime="${escapeHtml(video.mime || '')}" data-ext="${escapeHtml(ext ? ext.toLowerCase() : '')}" data-mode="${rClass}">
|
||||
${thumbContentHtml}
|
||||
<div class="sidebar-video-play-overlay"><i class="${overlayIcon}"></i></div>
|
||||
${formatBadge}
|
||||
<span class="sidebar-video-badge rating-${rClass}">${rClass.toUpperCase()}</span>
|
||||
</div>
|
||||
@@ -1215,7 +1269,9 @@
|
||||
seenRecommendationIds.clear();
|
||||
const mode = typeof window.activeMode !== 'undefined' ? window.activeMode : '';
|
||||
const affParams = getSessionAffinityParams();
|
||||
const res = await fetch(`/api/v2/recommendations?limit=${RECOMMENDATIONS_LIMIT}&mode=${mode}${affParams}`, {
|
||||
const mime = getCurrentMimeFilter();
|
||||
const mimeParam = mime ? `&mime=${encodeURIComponent(mime)}` : '';
|
||||
const res = await fetch(`/api/v2/recommendations?limit=${RECOMMENDATIONS_LIMIT}&mode=${mode}${mimeParam}${affParams}`, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
const data = await res.json();
|
||||
@@ -1272,7 +1328,9 @@
|
||||
const mode = typeof window.activeMode !== 'undefined' ? window.activeMode : '';
|
||||
const excludeArr = Array.from(seenRecommendationIds).slice(-100);
|
||||
const affParams = getSessionAffinityParams();
|
||||
const res = await fetch(`/api/v2/recommendations?limit=15&mode=${mode}&exclude_ids=${excludeArr.join(',')}${affParams}`, {
|
||||
const mime = getCurrentMimeFilter();
|
||||
const mimeParam = mime ? `&mime=${encodeURIComponent(mime)}` : '';
|
||||
const res = await fetch(`/api/v2/recommendations?limit=15&mode=${mode}${mimeParam}&exclude_ids=${excludeArr.join(',')}${affParams}`, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
const data = await res.json();
|
||||
@@ -1349,13 +1407,335 @@
|
||||
|
||||
const loadVideoRecommendations = loadRecommendations;
|
||||
|
||||
// ── Dedicated Tag Feed (Ordered for Tag Views) ───────────────────────
|
||||
let currentTag = null;
|
||||
let tagOrder = 'desc'; // 'desc' = newest first (default), 'asc' = oldest first (chronological)
|
||||
let tagOffset = 0;
|
||||
let tagTotal = 0;
|
||||
let tagFeedLoaded = false;
|
||||
let tagFeedLoading = false;
|
||||
let tagFeedNeedsReload = false;
|
||||
let tagHasMore = false;
|
||||
let tagSentinel = null;
|
||||
let tagObserver = null;
|
||||
|
||||
const getCurrentTag = () => {
|
||||
const pathMatch = window.location.pathname.match(/^\/tag\/([^/?#]+)/);
|
||||
if (pathMatch) return decodeURIComponent(pathMatch[1]);
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if (searchParams.get('tag')) return searchParams.get('tag');
|
||||
if (window.currentTag) return window.currentTag;
|
||||
return null;
|
||||
};
|
||||
|
||||
const getCurrentItemIdentifiers = () => {
|
||||
const ids = new Set();
|
||||
|
||||
// 1. From DOM elements on the page (primary/direct)
|
||||
const idElem = document.querySelector('[data-item-id]');
|
||||
if (idElem && idElem.dataset.itemId) {
|
||||
ids.add(String(idElem.dataset.itemId).trim());
|
||||
}
|
||||
const idLink = document.querySelector('.id-link');
|
||||
if (idLink) {
|
||||
if (idLink.dataset.itemId) ids.add(String(idLink.dataset.itemId).trim());
|
||||
const txt = idLink.textContent.trim();
|
||||
if (txt) ids.add(txt);
|
||||
}
|
||||
|
||||
// 2. From URL pathname (/tag/:tag/:slugOrId or /:slugOrId)
|
||||
const segments = window.location.pathname.split('/').filter(Boolean);
|
||||
if (segments.length >= 2 && segments[0] === 'tag') {
|
||||
if (segments[2]) {
|
||||
ids.add(decodeURIComponent(segments[2]).trim());
|
||||
}
|
||||
} else if (segments.length === 1) {
|
||||
const forbidden = ['s', 'b', 't', 'ca', 'a', 'login', 'register', 'settings', 'about', 'terms', 'rules', 'api', 'logout', 'auth', 'admin', 'mod', 'comments', 'notifications', 'feed', 'upload', 'tags', 'halls', 'ranking', 'abyss', 'random', 'scroller', 'p'];
|
||||
if (!forbidden.includes(segments[0])) {
|
||||
ids.add(decodeURIComponent(segments[0]).trim());
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(ids).filter(Boolean);
|
||||
};
|
||||
|
||||
const getCurrentItemKey = () => {
|
||||
const ids = getCurrentItemIdentifiers();
|
||||
return ids.length > 0 ? ids[0] : null;
|
||||
};
|
||||
|
||||
let isReCenteringFeed = false;
|
||||
|
||||
const highlightActiveTagCard = (scrollIntoView = false) => {
|
||||
const identifiers = getCurrentItemIdentifiers();
|
||||
const container = document.getElementById('sidebar-tag-container');
|
||||
if (!container) return;
|
||||
|
||||
let matchedCard = null;
|
||||
|
||||
container.querySelectorAll('.sidebar-video-card').forEach(card => {
|
||||
const cardId = card.dataset.id ? String(card.dataset.id).trim() : '';
|
||||
const cardSlug = card.dataset.slug ? String(card.dataset.slug).trim() : '';
|
||||
|
||||
const isMatch = identifiers.length > 0 && (
|
||||
(cardId && identifiers.includes(cardId)) ||
|
||||
(cardSlug && identifiers.includes(cardSlug))
|
||||
);
|
||||
|
||||
if (isMatch) {
|
||||
card.classList.add('active-tag-item');
|
||||
if (!matchedCard) matchedCard = card;
|
||||
} else {
|
||||
card.classList.remove('active-tag-item');
|
||||
}
|
||||
});
|
||||
|
||||
if (matchedCard && scrollIntoView) {
|
||||
const isTagTabActive = document.querySelector('.sidebar-tab.active')?.dataset.tab === 'tag' &&
|
||||
container.style.display !== 'none';
|
||||
if (isTagTabActive) {
|
||||
try {
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const cardRect = matchedCard.getBoundingClientRect();
|
||||
const targetScrollTop = container.scrollTop + (cardRect.top - containerRect.top) - (container.clientHeight / 2) + (cardRect.height / 2);
|
||||
container.scrollTo({ top: Math.max(0, targetScrollTop), behavior: 'smooth' });
|
||||
} catch (_) {
|
||||
matchedCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}
|
||||
} else if (!matchedCard && identifiers.length > 0 && currentTag && !isReCenteringFeed && !tagFeedLoading) {
|
||||
// Active item belongs to current tag context but is not in currently rendered slice
|
||||
// (e.g. after pressing Random in tag). Center feed around this active item!
|
||||
isReCenteringFeed = true;
|
||||
loadTagFeed(true, identifiers[0]).finally(() => {
|
||||
isReCenteringFeed = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let userManuallySelectedNonTagTab = false;
|
||||
|
||||
const updateTagTabVisibility = (forceSwitchToTag = false) => {
|
||||
const tag = getCurrentTag();
|
||||
const tagBtn = document.getElementById('sidebar-tab-tag');
|
||||
if (!tagBtn) return;
|
||||
|
||||
if (tag) {
|
||||
tagBtn.style.display = 'inline-flex';
|
||||
tagBtn.title = `#${tag}`;
|
||||
tagBtn.setAttribute('aria-label', `#${tag}`);
|
||||
|
||||
const titleNameEl = document.querySelector('.sidebar-tag-name');
|
||||
if (titleNameEl) titleNameEl.textContent = `#${tag}`;
|
||||
|
||||
if (currentTag !== tag) {
|
||||
currentTag = tag;
|
||||
tagOffset = 0;
|
||||
tagFeedLoaded = false;
|
||||
tagFeedNeedsReload = true;
|
||||
userManuallySelectedNonTagTab = false;
|
||||
|
||||
// When entering or switching tag view (e.g. /tag/cat), automatically jump to the tag tab
|
||||
switchSidebarTab('tag');
|
||||
} else {
|
||||
if (forceSwitchToTag || (!userManuallySelectedNonTagTab && document.querySelector('.sidebar-tab.active')?.dataset.tab !== 'tag')) {
|
||||
switchSidebarTab('tag');
|
||||
} else {
|
||||
highlightActiveTagCard(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tagBtn.style.display = 'none';
|
||||
currentTag = null;
|
||||
tagFeedLoaded = false;
|
||||
userManuallySelectedNonTagTab = false;
|
||||
|
||||
const activeTab = document.querySelector('.sidebar-tab.active')?.dataset.tab;
|
||||
if (activeTab === 'tag') {
|
||||
switchSidebarTab('recommendations');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadTagFeed = async (reset = false, focusId = null) => {
|
||||
const container = document.getElementById('sidebar-tag-container');
|
||||
const itemsContainer = container?.querySelector('.sidebar-tag-items-container');
|
||||
if (!container || !itemsContainer || tagFeedLoading) return;
|
||||
if (!currentTag) return;
|
||||
|
||||
tagFeedLoading = true;
|
||||
tagFeedNeedsReload = false;
|
||||
|
||||
if (reset) {
|
||||
tagOffset = 0;
|
||||
itemsContainer.innerHTML = `
|
||||
<div class="sidebar-loading-state">
|
||||
<i class="fa-solid fa-circle-notch fa-spin"></i>
|
||||
<span>Loading #${escapeHtml(currentTag)} items...</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
try {
|
||||
const mode = typeof window.activeMode !== 'undefined' ? window.activeMode : '';
|
||||
const mime = getCurrentMimeFilter();
|
||||
let url = `/api/v2/tag-feed?tag=${encodeURIComponent(currentTag)}&order=${tagOrder}&offset=${tagOffset}&limit=20&mode=${mode}`;
|
||||
if (mime) {
|
||||
url += `&mime=${encodeURIComponent(mime)}`;
|
||||
}
|
||||
if (focusId) {
|
||||
url += `&focus_id=${encodeURIComponent(focusId)}`;
|
||||
}
|
||||
const res = await fetch(url, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
const data = await res.json();
|
||||
const items = data.items || [];
|
||||
tagTotal = data.total || 0;
|
||||
tagOffset = (data.offset ?? tagOffset) + items.length;
|
||||
tagHasMore = typeof data.hasMore !== 'undefined' ? !!data.hasMore : (tagOffset < tagTotal);
|
||||
|
||||
// Update counter in header
|
||||
const countEl = container.querySelector('.sidebar-tag-count');
|
||||
if (countEl) countEl.textContent = `(${tagTotal})`;
|
||||
|
||||
// Update order button icon and tooltip
|
||||
const orderBtn = container.querySelector('.sidebar-tag-order-btn');
|
||||
if (orderBtn) {
|
||||
if (tagOrder === 'asc') {
|
||||
orderBtn.innerHTML = '<i class="fa-solid fa-arrow-down-1-9"></i>';
|
||||
orderBtn.title = 'Chronological (Oldest first) — Click for Newest first';
|
||||
} else {
|
||||
orderBtn.innerHTML = '<i class="fa-solid fa-arrow-down-9-1"></i>';
|
||||
orderBtn.title = 'Newest first (default) — Click for Oldest first';
|
||||
}
|
||||
}
|
||||
|
||||
if (data.success && items.length > 0) {
|
||||
tagFeedLoaded = true;
|
||||
const identifiers = getCurrentItemIdentifiers();
|
||||
|
||||
let html = '';
|
||||
items.forEach(v => {
|
||||
const vId = String(v.id).trim();
|
||||
const vSlug = String(v.slug || '').trim();
|
||||
const isActive = identifiers.length > 0 && (
|
||||
(vId && identifiers.includes(vId)) ||
|
||||
(vSlug && identifiers.includes(vSlug))
|
||||
);
|
||||
html += renderVideoCard(v, { isTagFeed: true, tag: currentTag, isActive });
|
||||
});
|
||||
|
||||
if (reset) {
|
||||
itemsContainer.innerHTML = html;
|
||||
setTimeout(() => highlightActiveTagCard(true), 60);
|
||||
} else {
|
||||
itemsContainer.insertAdjacentHTML('beforeend', html);
|
||||
highlightActiveTagCard(false);
|
||||
}
|
||||
|
||||
initTagSentinel();
|
||||
} else if (reset) {
|
||||
itemsContainer.innerHTML = `
|
||||
<div class="sidebar-empty-state" style="text-align:center;padding:30px 15px;color:var(--text-muted,#888);font-size:0.88em;">
|
||||
<i class="fa-solid fa-tag" style="font-size:1.8em;opacity:0.4;margin-bottom:8px;display:block;"></i>
|
||||
No uploads found for #${escapeHtml(currentTag)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[TAG-FEED] Failed to load tag items:', err);
|
||||
if (reset) {
|
||||
itemsContainer.innerHTML = `
|
||||
<div class="sidebar-empty-state" style="text-align:center;padding:25px;color:var(--danger,#ff4444);font-size:0.88em;">
|
||||
Failed to load tag items
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} finally {
|
||||
tagFeedLoading = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadMoreTagFeed = async () => {
|
||||
if (tagFeedLoading || !tagHasMore || !currentTag) return;
|
||||
tagOffset += 20;
|
||||
await loadTagFeed(false);
|
||||
};
|
||||
|
||||
const initTagSentinel = () => {
|
||||
const container = document.getElementById('sidebar-tag-container');
|
||||
const itemsContainer = container?.querySelector('.sidebar-tag-items-container');
|
||||
if (!container || !itemsContainer) return;
|
||||
|
||||
if (tagSentinel && tagSentinel.parentNode) {
|
||||
tagSentinel.remove();
|
||||
}
|
||||
|
||||
if (!tagHasMore) return;
|
||||
|
||||
tagSentinel = document.createElement('div');
|
||||
tagSentinel.className = 'sidebar-tag-sentinel';
|
||||
tagSentinel.style.cssText = 'height: 20px; margin: 10px 0;';
|
||||
itemsContainer.appendChild(tagSentinel);
|
||||
|
||||
if (tagObserver) tagObserver.disconnect();
|
||||
|
||||
tagObserver = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && !tagFeedLoading && tagHasMore) {
|
||||
loadMoreTagFeed();
|
||||
}
|
||||
}, {
|
||||
root: container,
|
||||
rootMargin: '200px'
|
||||
});
|
||||
|
||||
tagObserver.observe(tagSentinel);
|
||||
};
|
||||
|
||||
const initTagOrderButton = () => {
|
||||
const container = document.getElementById('sidebar-tag-container');
|
||||
if (!container || container.dataset.orderBound === 'true') return;
|
||||
container.dataset.orderBound = 'true';
|
||||
|
||||
const orderBtn = container.querySelector('.sidebar-tag-order-btn');
|
||||
if (orderBtn) {
|
||||
orderBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
tagOrder = (tagOrder === 'desc') ? 'asc' : 'desc';
|
||||
loadTagFeed(true);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const bindTagContainerEvents = () => {
|
||||
const container = document.getElementById('sidebar-tag-container');
|
||||
if (!container || container.dataset.eventsBound === 'true') return;
|
||||
container.dataset.eventsBound = 'true';
|
||||
|
||||
container.addEventListener('click', (e) => {
|
||||
const card = e.target.closest('.sidebar-video-card');
|
||||
if (card) {
|
||||
container.querySelectorAll('.sidebar-video-card').forEach(c => c.classList.remove('active-tag-item'));
|
||||
card.classList.add('active-tag-item');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const switchSidebarTab = (tabName) => {
|
||||
const normalizedTab = (tabName === 'videos' || tabName === 'recommendations') ? 'recommendations' : tabName;
|
||||
if (window.clearHoverPreview) {
|
||||
window.clearHoverPreview();
|
||||
}
|
||||
const normalizedTab = (tabName === 'videos' || tabName === 'recommendations')
|
||||
? 'recommendations'
|
||||
: (tabName === 'tag' ? 'tag' : tabName);
|
||||
const tabs = document.querySelectorAll('.sidebar-tab');
|
||||
const contents = document.querySelectorAll('.sidebar-tab-content');
|
||||
|
||||
tabs.forEach(t => {
|
||||
const tTab = (t.dataset.tab === 'videos' || t.dataset.tab === 'recommendations') ? 'recommendations' : t.dataset.tab;
|
||||
const tTab = (t.dataset.tab === 'videos' || t.dataset.tab === 'recommendations')
|
||||
? 'recommendations'
|
||||
: (t.dataset.tab === 'tag' ? 'tag' : t.dataset.tab);
|
||||
if (tTab === normalizedTab) {
|
||||
t.classList.add('active');
|
||||
} else {
|
||||
@@ -1364,7 +1744,9 @@
|
||||
});
|
||||
|
||||
contents.forEach(c => {
|
||||
const cTab = (c.dataset.tabContent === 'videos' || c.dataset.tabContent === 'recommendations') ? 'recommendations' : c.dataset.tabContent;
|
||||
const cTab = (c.dataset.tabContent === 'videos' || c.dataset.tabContent === 'recommendations')
|
||||
? 'recommendations'
|
||||
: (c.dataset.tabContent === 'tag' ? 'tag' : c.dataset.tabContent);
|
||||
if (cTab === normalizedTab) {
|
||||
c.classList.add('active');
|
||||
c.style.display = 'block';
|
||||
@@ -1382,6 +1764,12 @@
|
||||
if (!recommendationsLoaded) {
|
||||
loadRecommendations();
|
||||
}
|
||||
} else if (normalizedTab === 'tag') {
|
||||
if (tagFeedNeedsReload || !tagFeedLoaded) {
|
||||
loadTagFeed(true);
|
||||
} else {
|
||||
setTimeout(() => highlightActiveTagCard(true), 60);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1392,6 +1780,11 @@
|
||||
const btn = e.target.closest('.sidebar-tab');
|
||||
if (btn && btn.dataset.tab) {
|
||||
e.preventDefault();
|
||||
if (btn.dataset.tab !== 'tag') {
|
||||
userManuallySelectedNonTagTab = true;
|
||||
} else {
|
||||
userManuallySelectedNonTagTab = false;
|
||||
}
|
||||
switchSidebarTab(btn.dataset.tab);
|
||||
}
|
||||
});
|
||||
@@ -1403,13 +1796,20 @@
|
||||
savedTab = localStorage.getItem('sidebar_active_tab') || 'comments';
|
||||
} catch (_) {}
|
||||
|
||||
if (savedTab === 'videos' || savedTab === 'recommendations') {
|
||||
if (getCurrentTag()) {
|
||||
switchSidebarTab('tag');
|
||||
} else if (savedTab === 'videos' || savedTab === 'recommendations') {
|
||||
switchSidebarTab('recommendations');
|
||||
} else {
|
||||
switchSidebarTab(savedTab);
|
||||
}
|
||||
};
|
||||
|
||||
const replaceCardWithNewRandom = async (card) => {
|
||||
if (!card || card.dataset.swapping === 'true') return;
|
||||
if (window.clearHoverPreview) {
|
||||
window.clearHoverPreview();
|
||||
}
|
||||
card.dataset.swapping = 'true';
|
||||
card.classList.add('sidebar-card-swapping');
|
||||
|
||||
@@ -1436,7 +1836,9 @@
|
||||
const excludeArr = Array.from(excludeSet).slice(-120);
|
||||
|
||||
const affParams = getSessionAffinityParams();
|
||||
const res = await fetch(`/api/v2/recommendations?limit=1&mode=${mode}&exclude_ids=${excludeArr.join(',')}${affParams}`, {
|
||||
const mime = getCurrentMimeFilter();
|
||||
const mimeParam = mime ? `&mime=${encodeURIComponent(mime)}` : '';
|
||||
const res = await fetch(`/api/v2/recommendations?limit=1&mode=${mode}${mimeParam}&exclude_ids=${excludeArr.join(',')}${affParams}`, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
const data = await res.json();
|
||||
@@ -1458,11 +1860,7 @@
|
||||
const newCard = temp.firstElementChild;
|
||||
|
||||
if (newCard && card.parentNode) {
|
||||
newCard.classList.add('sidebar-card-entering');
|
||||
card.replaceWith(newCard);
|
||||
setTimeout(() => {
|
||||
newCard.classList.remove('sidebar-card-entering');
|
||||
}, 2200);
|
||||
}
|
||||
} else {
|
||||
card.classList.remove('sidebar-card-swapping');
|
||||
@@ -1496,13 +1894,19 @@
|
||||
container.addEventListener('auxclick', handleCardAction);
|
||||
};
|
||||
|
||||
// Reload recommendations when user triggers Random (#random, #nav-random, or 'r' key)
|
||||
// Reload recommendations or sync tag feed when user triggers Random (#random, #nav-random, or 'r' key)
|
||||
const handleRandomAction = () => {
|
||||
recommendationsLoaded = false;
|
||||
let savedTab = 'comments';
|
||||
try { savedTab = localStorage.getItem('sidebar_active_tab'); } catch (_) {}
|
||||
if (savedTab === 'videos' || savedTab === 'recommendations') {
|
||||
loadRecommendations(false);
|
||||
const tag = getCurrentTag();
|
||||
if (tag) {
|
||||
userManuallySelectedNonTagTab = false;
|
||||
switchSidebarTab('tag');
|
||||
} else {
|
||||
let savedTab = 'comments';
|
||||
try { savedTab = localStorage.getItem('sidebar_active_tab'); } catch (_) {}
|
||||
if (savedTab === 'videos' || savedTab === 'recommendations') {
|
||||
loadRecommendations(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1525,10 +1929,17 @@
|
||||
loadActivity(false);
|
||||
|
||||
recommendationsLoaded = false;
|
||||
tagFeedLoaded = false;
|
||||
tagFeedNeedsReload = true;
|
||||
const activeTabEl = document.querySelector('.sidebar-tab.active')?.dataset.tab;
|
||||
let savedTab = 'comments';
|
||||
try { savedTab = localStorage.getItem('sidebar_active_tab'); } catch (_) {}
|
||||
if (savedTab === 'videos' || savedTab === 'recommendations') {
|
||||
const currentTabName = activeTabEl || savedTab;
|
||||
|
||||
if (currentTabName === 'videos' || currentTabName === 'recommendations') {
|
||||
loadRecommendations(false);
|
||||
} else if (currentTabName === 'tag' && currentTag) {
|
||||
loadTagFeed(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1573,6 +1984,19 @@
|
||||
const _origInit = init;
|
||||
const initWithScroll = async () => {
|
||||
initSidebarTabs();
|
||||
initTagOrderButton();
|
||||
bindTagContainerEvents();
|
||||
updateTagTabVisibility();
|
||||
document.addEventListener('f0ck:contentLoaded', () => {
|
||||
updateTagTabVisibility();
|
||||
setTimeout(() => highlightActiveTagCard(true), 60);
|
||||
});
|
||||
window.addEventListener('popstate', () => {
|
||||
setTimeout(() => {
|
||||
updateTagTabVisibility();
|
||||
highlightActiveTagCard(true);
|
||||
}, 60);
|
||||
});
|
||||
bindRecommendationEvents();
|
||||
bindRecommendationScrollListener();
|
||||
await _origInit();
|
||||
|
||||
Reference in New Issue
Block a user