Fix tracker bar placement and dedupe in new post layout

This commit is contained in:
2026-02-25 23:58:04 +01:00
parent 5d3a165921
commit 12edc394b2

View File

@@ -189,6 +189,59 @@ function clearTrackerElementForPost(postElement, trackerElement = null) {
trackerElementsByPost.delete(postElement); trackerElementsByPost.delete(postElement);
} }
function getTrackerScopeRoot(element) {
return element ? element.closest(DIALOG_ROOT_SELECTOR) : null;
}
function getTrackersForUrlInScope(encodedUrl, scopeRoot) {
if (!encodedUrl) {
return [];
}
const selector = `.fb-tracker-ui[data-post-url="${encodedUrl}"]`;
const trackers = Array.from(document.querySelectorAll(selector)).filter((tracker) => tracker && tracker.isConnected);
return trackers.filter((tracker) => {
const trackerScopeRoot = getTrackerScopeRoot(tracker);
if (scopeRoot) {
return trackerScopeRoot === scopeRoot;
}
return !trackerScopeRoot;
});
}
function dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, preferredTracker = null) {
const trackers = getTrackersForUrlInScope(encodedUrl, scopeRoot);
if (!trackers.length) {
return null;
}
const keep = preferredTracker && trackers.includes(preferredTracker)
? preferredTracker
: trackers[trackers.length - 1];
trackers.forEach((tracker) => {
if (tracker !== keep) {
tracker.remove();
}
});
return keep;
}
function getTrackerInsertionAnchorInPost(postElement, buttonBar) {
if (!postElement || !buttonBar || !postElement.contains(buttonBar)) {
return null;
}
let anchor = buttonBar;
while (anchor.parentElement && anchor.parentElement !== postElement) {
anchor = anchor.parentElement;
}
return anchor && anchor.parentElement === postElement ? anchor : null;
}
const AI_CREDENTIAL_CACHE_TTL = 60 * 1000; // 1 minute cache const AI_CREDENTIAL_CACHE_TTL = 60 * 1000; // 1 minute cache
const aiCredentialCache = { const aiCredentialCache = {
data: null, data: null,
@@ -3074,6 +3127,18 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
console.log('[FB Tracker] Post #' + postNum + ' - Creating tracker UI for:', postUrlData.url, postElement); console.log('[FB Tracker] Post #' + postNum + ' - Creating tracker UI for:', postUrlData.url, postElement);
const encodedUrl = encodeURIComponent(postUrlData.url); const encodedUrl = encodeURIComponent(postUrlData.url);
const scopeRoot = getTrackerScopeRoot(postElement);
const existingScopedTrackers = getTrackersForUrlInScope(encodedUrl, scopeRoot);
if (existingScopedTrackers.length > 0) {
const scopedTracker = dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, existingScopedTrackers[0]);
if (scopedTracker) {
console.log('[FB Tracker] Post #' + postNum + ' - Reusing existing tracker UI in same scope');
setTrackerElementForPost(postElement, scopedTracker);
postElement.setAttribute(PROCESSED_ATTR, '1');
return;
}
}
const existingEntry = processedPostUrls.get(encodedUrl); const existingEntry = processedPostUrls.get(encodedUrl);
if (existingEntry && existingEntry.element && existingEntry.element !== postElement) { if (existingEntry && existingEntry.element && existingEntry.element !== postElement) {
@@ -3083,10 +3148,7 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
} else { } else {
processedPostUrls.delete(encodedUrl); processedPostUrls.delete(encodedUrl);
} }
const otherUI = document.querySelector(`.fb-tracker-ui[data-post-url="${encodedUrl}"]`); getTrackersForUrlInScope(encodedUrl, scopeRoot).forEach((tracker) => tracker.remove());
if (otherUI) {
otherUI.remove();
}
} }
const { likeButton: sourceLikeButton = null, isSearchResult = false, isDialogContext = false } = options; const { likeButton: sourceLikeButton = null, isSearchResult = false, isDialogContext = false } = options;
@@ -3114,6 +3176,8 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
row-gap: 6px; row-gap: 6px;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
position: relative;
z-index: 2;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 13px; font-size: 13px;
`; `;
@@ -3726,20 +3790,29 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
inserted = tryInsertBeforeReelsCommentComposer(); inserted = tryInsertBeforeReelsCommentComposer();
} }
// Strategy 1: After button bar's parent (more stable) // Strategy 1: Insert right after the action block inside the current post container
if (!inserted && buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement) { if (!inserted && buttonBar) {
const actionAnchor = getTrackerInsertionAnchorInPost(postElement, buttonBar);
if (actionAnchor && actionAnchor.parentElement) {
actionAnchor.parentElement.insertBefore(container, actionAnchor.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after in-post action block. ID: #' + container.id);
inserted = true;
}
}
// Strategy 2: Insert directly after the button bar if still inside the post container
if (!inserted && buttonBar && buttonBar.parentElement && postElement.contains(buttonBar.parentElement)) {
buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar (in-post fallback). ID: #' + container.id);
inserted = true;
}
// Strategy 3: Legacy fallback for old variants where the interaction bar is outside the post container
if (!inserted && buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {
const grandParent = buttonBar.parentElement.parentElement; const grandParent = buttonBar.parentElement.parentElement;
grandParent.insertBefore(container, buttonBar.parentElement.nextSibling); grandParent.insertBefore(container, buttonBar.parentElement.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar parent. ID: #' + container.id); console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after external button bar parent (legacy). ID: #' + container.id);
inserted = true; inserted = true;
} }
// Strategy 2: After button bar directly // Strategy 4: Append to post element
if (!inserted && buttonBar && buttonBar.parentElement) {
buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar. ID: #' + container.id);
inserted = true;
}
// Strategy 3: Append to post element
if (!inserted) { if (!inserted) {
postElement.appendChild(container); postElement.appendChild(container);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted into article (fallback). ID: #' + container.id); console.log('[FB Tracker] Post #' + postNum + ' - UI inserted into article (fallback). ID: #' + container.id);
@@ -3747,6 +3820,11 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
} }
if (inserted) { if (inserted) {
const dedupedTracker = dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, container);
if (dedupedTracker && dedupedTracker !== container) {
clearTrackerElementForPost(postElement, container);
}
processedPostUrls.set(encodedUrl, { processedPostUrls.set(encodedUrl, {
element: postElement, element: postElement,
createdAt: Date.now(), createdAt: Date.now(),
@@ -3755,7 +3833,7 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
: null, : null,
hidden: false hidden: false
}); });
setTrackerElementForPost(postElement, container); setTrackerElementForPost(postElement, dedupedTracker || container);
} }
// Monitor if the UI gets removed and re-insert it // Monitor if the UI gets removed and re-insert it
@@ -3765,12 +3843,18 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
console.log('[FB Tracker] Post #' + postNum + ' - UI was removed, re-inserting...'); console.log('[FB Tracker] Post #' + postNum + ' - UI was removed, re-inserting...');
observer.disconnect(); observer.disconnect();
// Try to re-insert // Try to re-insert
if (buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement) { const actionAnchor = getTrackerInsertionAnchorInPost(postElement, buttonBar);
if (actionAnchor && actionAnchor.parentElement) {
actionAnchor.parentElement.insertBefore(container, actionAnchor.nextSibling);
} else if (buttonBar && buttonBar.parentElement && postElement.contains(buttonBar.parentElement)) {
buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling);
} else if (buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {
buttonBar.parentElement.parentElement.insertBefore(container, buttonBar.parentElement.nextSibling); buttonBar.parentElement.parentElement.insertBefore(container, buttonBar.parentElement.nextSibling);
} else if (postElement.parentElement) { } else if (postElement) {
postElement.parentElement.appendChild(container); postElement.appendChild(container);
} }
if (container.isConnected) { if (container.isConnected) {
dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, container);
setTrackerElementForPost(postElement, container); setTrackerElementForPost(postElement, container);
} }
} }