diff --git a/extension/content.js b/extension/content.js index 157b666..cbdaa67 100644 --- a/extension/content.js +++ b/extension/content.js @@ -13,7 +13,7 @@ const LAST_SELECTION_MAX_AGE = 5000; let selectionCacheTimeout = null; let lastGlobalSelection = { text: '', timestamp: 0 }; const processedPostUrls = new Map(); -const SEARCH_RESULTS_PATH = '/search/top'; +const SEARCH_RESULTS_PATH_PREFIX = '/search'; const FEED_HOME_PATHS = ['/', '/home.php']; const sessionSearchRecordedUrls = new Set(); const sessionSearchInfoCache = new Map(); @@ -869,6 +869,41 @@ function matchesKeyword(label, keywords) { return keywords.some((keyword) => label.includes(keyword)); } +function styleIndicatesLiked(styleValue) { + if (!styleValue || typeof styleValue !== 'string') { + return false; + } + const normalized = styleValue.toLowerCase(); + return ( + normalized.includes('reaction-like') + || normalized.includes('#0866ff') + || normalized.includes('rgb(8, 102, 255)') + || normalized.includes('--reaction-like') + ); +} + +function elementIndicatesLiked(element) { + if (!element) { + return false; + } + + const inlineStyle = (element.getAttribute('style') || '').trim(); + if (styleIndicatesLiked(inlineStyle)) { + return true; + } + + try { + const computed = window.getComputedStyle(element); + if (computed && computed.color && styleIndicatesLiked(computed.color)) { + return true; + } + } catch (error) { + console.debug('[FB Tracker] Unable to inspect computed style:', error); + } + + return false; +} + function isPostLikedByCurrentUser(likeButton, postElement) { const candidates = []; if (likeButton) { @@ -887,27 +922,28 @@ function isPostLikedByCurrentUser(likeButton, postElement) { continue; } + if (elementIndicatesLiked(candidate)) { + return true; + } + const styleTarget = candidate.matches('[data-ad-rendering-role*="gefällt" i]') ? candidate : candidate.querySelector && candidate.querySelector('[data-ad-rendering-role*="gefällt" i]'); if (styleTarget) { - const inlineStyle = (styleTarget.getAttribute('style') || '').trim(); - if (inlineStyle && /(#0?866ff|reaction-like)/i.test(inlineStyle)) { + if (elementIndicatesLiked(styleTarget)) { return true; } + } - try { - const computed = window.getComputedStyle(styleTarget); - if (computed && computed.color) { - const color = computed.color.toLowerCase(); - if (color.includes('rgb(8, 102, 255)') || color.includes('rgba(8, 102, 255')) { - return true; - } - } - } catch (error) { - console.debug('[FB Tracker] Unable to inspect computed style for like button:', error); - } + const styledDescendant = candidate.querySelector && candidate.querySelector('[style*="reaction-like"], [style*="#0866FF"], [style*="rgb(8, 102, 255)"], [style*="--reaction-like"]'); + if (styledDescendant && elementIndicatesLiked(styledDescendant)) { + return true; + } + + const pressedAncestor = candidate.closest && candidate.closest('[aria-pressed="true"]'); + if (pressedAncestor && pressedAncestor !== candidate) { + return true; } const ariaPressed = candidate.getAttribute && candidate.getAttribute('aria-pressed'); @@ -1722,7 +1758,7 @@ function extractDeadlineFromPostText(postElement) { const extractTimeAfterIndex = (text, index) => { const tail = text.slice(index, index + 80); - const timeMatch = /^\s*(?:[,;:-]|\b)?\s*(?:um|ab|bis|gegen|spätestens)?\s*(?:den|dem|am)?\s*(?:ca\.?)?\s*(\d{1,2})(?:[:.](\d{2}))?\s*(?:uhr|h)?/i.exec(tail); + const timeMatch = /^\s*(?:\(|\[)?\s*(?:[,;:-]|\b)?\s*(?:um|ab|bis|gegen|spätestens)?\s*(?:den|dem|am)?\s*(?:ca\.?)?\s*(\d{1,2})(?:[:.](\d{2}))?\s*(?:uhr|h)?\s*(?:\)|\])?/i.exec(tail); if (!timeMatch) { return null; } @@ -1783,7 +1819,7 @@ function extractDeadlineFromPostText(postElement) { } // Pattern for "12. Oktober" or "12 Oktober" - const monthPattern = /\b(\d{1,2})\.?\s+(januar|jan|februar|feb|märz|mär|maerz|april|apr|mai|juni|jun|juli|jul|august|aug|september|sep|sept|oktober|okt|november|nov|dezember|dez)\b/gi; + const monthPattern = /\b(\d{1,2})\.?\s*(januar|jan|februar|feb|märz|mär|maerz|april|apr|mai|juni|jun|juli|jul|august|aug|september|sep|sept|oktober|okt|november|nov|dezember|dez)\b/gi; let monthMatch; while ((monthMatch = monthPattern.exec(fullText)) !== null) { const day = parseInt(monthMatch[1], 10); @@ -2682,7 +2718,8 @@ function findPosts() { console.log('[FB Tracker] Found', postContainers.length, 'candidate containers'); let processed = 0; - const isSearchResultsPage = window.location.pathname.startsWith(SEARCH_RESULTS_PATH); + const pathname = window.location.pathname || ''; + const isSearchResultsPage = typeof pathname === 'string' && pathname.startsWith(SEARCH_RESULTS_PATH_PREFIX); for (const { container: originalContainer, likeButton, buttonBar: precomputedButtonBar } of postContainers) { let container = ensurePrimaryPostElement(originalContainer); diff --git a/web/app.js b/web/app.js index a5cb93d..ab1cb27 100644 --- a/web/app.js +++ b/web/app.js @@ -1526,6 +1526,27 @@ function calculateUrgencyScore(postItem) { } } + // Recent participation (<24h) should lower urgency regardless of deadline pressure + if (hoursSinceLastCheck < 24) { + const freshnessRatio = (24 - hoursSinceLastCheck) / 24; // 0 (at 24h) to 1 (just now) + const recencyPenalty = 60 + Math.round(freshnessRatio * 120); // 60-180 point penalty + score += recencyPenalty; + } + + // Give extra priority to posts with deadlines approaching soon + if (hoursUntilDeadline < Infinity && remaining > 0) { + const urgencyWindowHours = 72; + const cappedHours = Math.min(hoursUntilDeadline, urgencyWindowHours); + const closenessRatio = 1 - (cappedHours / urgencyWindowHours); // 0-1 + + if (closenessRatio > 0) { + const baseBoost = Math.round(closenessRatio * 120); // Up to 120 points + const remainingFactor = Math.min(1.6, 0.7 + remaining * 0.3); // 1.0 (1 remaining) .. 1.6 (>=3 remaining) + const deadlineBoost = Math.round(baseBoost * remainingFactor); + score -= deadlineBoost; + } + } + return score; }