From e55f17f0c8dc2824c54fb590609d3fca6837a769 Mon Sep 17 00:00:00 2001 From: Meik Date: Fri, 21 Nov 2025 13:47:19 +0100 Subject: [PATCH] =?UTF-8?q?tabs=20in=20beitr=C3=A4gen=20gemerget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app.js | 93 ++++++++++++++++++++++++++++++------------------- web/index.html | 25 +++++++++---- web/settings.js | 4 ++- web/style.css | 21 +++++++++++ 4 files changed, 100 insertions(+), 43 deletions(-) diff --git a/web/app.js b/web/app.js index 151a03e..fd45fc6 100644 --- a/web/app.js +++ b/web/app.js @@ -18,6 +18,7 @@ let focusTabAdjusted = null; let currentProfile = 1; let currentTab = 'pending'; let posts = []; +let includeExpiredPosts = loadIncludeExpiredPreference(); let profilePollTimer = null; const UPDATES_RECONNECT_DELAY = 5000; let updatesEventSource = null; @@ -235,6 +236,7 @@ const bookmarkSearchInput = document.getElementById('bookmarkSearchInput'); const bookmarkSortSelect = document.getElementById('bookmarkSortSelect'); const bookmarkSortDirectionToggle = document.getElementById('bookmarkSortDirectionToggle'); const profileSelectElement = document.getElementById('profileSelect'); +const includeExpiredToggle = document.getElementById('includeExpiredToggle'); const REFRESH_SETTINGS_KEY = 'trackerRefreshSettings'; const SORT_SETTINGS_KEY = 'trackerSortSettings'; @@ -246,6 +248,37 @@ const BOOKMARKS_BASE_URL = 'https://www.facebook.com/search/top'; const BOOKMARK_WINDOW_DAYS = 28; const BOOKMARK_SUFFIXES = ['Gewinnspiel', 'gewinnen', 'verlosen']; const BOOKMARK_PREFS_KEY = 'trackerBookmarkPreferences'; +const INCLUDE_EXPIRED_STORAGE_KEY = 'trackerIncludeExpired'; + +function loadIncludeExpiredPreference() { + try { + const stored = localStorage.getItem(INCLUDE_EXPIRED_STORAGE_KEY); + if (stored === 'true') { + return true; + } + if (stored === 'false') { + return false; + } + } catch (error) { + console.warn('Konnte Anzeigepräferenz für abgelaufene Beiträge nicht laden:', error); + } + return false; +} + +function persistIncludeExpiredPreference(value) { + try { + localStorage.setItem(INCLUDE_EXPIRED_STORAGE_KEY, value ? 'true' : 'false'); + } catch (error) { + console.warn('Konnte Anzeigepräferenz für abgelaufene Beiträge nicht speichern:', error); + } +} + +function updateIncludeExpiredToggleUI() { + if (!includeExpiredToggle) { + return; + } + includeExpiredToggle.checked = includeExpiredPosts; +} function initializeFocusParams() { try { @@ -333,12 +366,10 @@ const INITIAL_POST_LIMIT = 10; const POST_LOAD_INCREMENT = 10; const tabVisibleCounts = { pending: INITIAL_POST_LIMIT, - expired: INITIAL_POST_LIMIT, all: INITIAL_POST_LIMIT }; const tabFilteredCounts = { pending: 0, - expired: 0, all: 0 }; let loadMoreObserver = null; @@ -414,9 +445,6 @@ function toTimestamp(value, fallback = null) { } function getSortTabKey(tab = currentTab) { - if (tab === 'expired') { - return 'expired'; - } if (tab === 'all') { return 'all'; } @@ -1391,8 +1419,6 @@ function updateTabInUrl() { const url = new URL(window.location.href); if (currentTab === 'pending') { url.searchParams.set('tab', 'pending'); - } else if (currentTab === 'expired') { - url.searchParams.set('tab', 'expired'); } else { url.searchParams.set('tab', 'all'); } @@ -1401,9 +1427,6 @@ function updateTabInUrl() { } function getTabKey(tab = currentTab) { - if (tab === 'expired') { - return 'expired'; - } if (tab === 'all') { return 'all'; } @@ -1440,9 +1463,6 @@ function cleanupLoadMoreObserver() { } function getTabDisplayLabel(tab = currentTab) { - if (tab === 'expired') { - return 'Abgelaufen/Abgeschlossen'; - } if (tab === 'all') { return 'Alle Beiträge'; } @@ -1535,8 +1555,6 @@ function loadMorePosts(tab = currentTab, { triggeredByScroll = false } = {}) { function setTab(tab, { updateUrl = true } = {}) { if (tab === 'all') { currentTab = 'all'; - } else if (tab === 'expired') { - currentTab = 'expired'; } else { currentTab = 'pending'; } @@ -1553,7 +1571,7 @@ function initializeTabFromUrl() { try { const params = new URLSearchParams(window.location.search); const tabParam = params.get('tab'); - if (tabParam === 'all' || tabParam === 'pending' || tabParam === 'expired') { + if (tabParam === 'all' || tabParam === 'pending') { currentTab = tabParam; tabResolved = true; } else if (initialTabOverride) { @@ -2773,6 +2791,16 @@ if (profileSelectElement) { }); } +if (includeExpiredToggle) { + updateIncludeExpiredToggleUI(); + includeExpiredToggle.addEventListener('change', () => { + includeExpiredPosts = includeExpiredToggle.checked; + persistIncludeExpiredPreference(includeExpiredPosts); + resetVisibleCount('all'); + renderPosts(); + }); +} + // Tab switching document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { @@ -3087,10 +3115,10 @@ function renderPosts() { if (currentTab === 'pending') { filteredItems = sortedItems.filter((item) => !item.status.isExpired && item.status.canCurrentProfileCheck && !item.status.isComplete); - } else if (currentTab === 'expired') { - filteredItems = sortedItems.filter((item) => item.status.isExpired || item.status.isComplete); - } else if (currentTab === 'all') { - filteredItems = sortedItems.filter((item) => !item.status.isExpired && !item.status.isComplete); + } else { + filteredItems = includeExpiredPosts + ? sortedItems + : sortedItems.filter((item) => !item.status.isExpired && !item.status.isComplete); } const tabTotalCount = filteredItems.length; @@ -3115,22 +3143,17 @@ function renderPosts() { if (!focusHandled && focusCandidateEntry && !searchActive) { const candidateVisibleInCurrentTab = filteredItems.some(({ post }) => doesPostMatchFocus(post)); if (!candidateVisibleInCurrentTab) { - let desiredTab = 'all'; - if (focusCandidateEntry.status.isExpired || focusCandidateEntry.status.isComplete) { - desiredTab = 'expired'; - } else if (focusCandidateEntry.status.canCurrentProfileCheck && !focusCandidateEntry.status.isExpired && !focusCandidateEntry.status.isComplete) { - desiredTab = 'pending'; - } - - if (currentTab !== desiredTab && focusTabAdjusted !== desiredTab) { - focusTabAdjusted = desiredTab; - setTab(desiredTab); + if (currentTab !== 'all' && focusTabAdjusted !== 'all') { + focusTabAdjusted = 'all'; + setTab('all'); return; } - if (desiredTab !== 'all' && currentTab !== 'all' && focusTabAdjusted !== 'all') { - focusTabAdjusted = 'all'; - setTab('all'); + if (!includeExpiredPosts && (focusCandidateEntry.status.isExpired || focusCandidateEntry.status.isComplete)) { + includeExpiredPosts = true; + persistIncludeExpiredPreference(includeExpiredPosts); + updateIncludeExpiredToggleUI(); + renderPosts(); return; } } @@ -3163,8 +3186,8 @@ function renderPosts() { let emptyIcon = '🎉'; if (currentTab === 'pending') { emptyMessage = 'Keine offenen Beiträge!'; - } else if (currentTab === 'expired') { - emptyMessage = 'Keine abgelaufenen oder abgeschlossenen Beiträge.'; + } else { + emptyMessage = 'Keine Beiträge vorhanden.'; } if (searchActive) { diff --git a/web/index.html b/web/index.html index 358c563..413054c 100644 --- a/web/index.html +++ b/web/index.html @@ -20,10 +20,10 @@

📋 Post Tracker

@@ -82,11 +82,14 @@
-
+
@@ -663,7 +666,11 @@ buttons.forEach((button) => { const isActive = button.dataset.viewTarget === view; button.classList.toggle('nav-active', isActive); - button.setAttribute('aria-pressed', isActive ? 'true' : 'false'); + if (isActive) { + button.setAttribute('aria-current', 'page'); + } else { + button.removeAttribute('aria-current'); + } }); if (options.updateHistory) { @@ -683,7 +690,11 @@ } buttons.forEach((button) => { - button.addEventListener('click', () => { + button.addEventListener('click', (event) => { + if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) { + return; + } + event.preventDefault(); setView(button.dataset.viewTarget); }); }); diff --git a/web/settings.js b/web/settings.js index a367e4c..9ac1f5e 100644 --- a/web/settings.js +++ b/web/settings.js @@ -515,8 +515,10 @@ async function deleteCredential(id) { showSuccess('Anmeldedaten gelöscht'); } -// Make function globally accessible +// Make functions globally accessible for inline handlers window.toggleCredentialActive = toggleCredentialActive; +window.editCredential = editCredential; +window.deleteCredential = deleteCredential; // ============================================================================ // DRAG AND DROP diff --git a/web/style.css b/web/style.css index e88a65b..b6783c6 100644 --- a/web/style.css +++ b/web/style.css @@ -113,6 +113,7 @@ header { letter-spacing: 0.01em; padding: 10px 16px; position: relative; + text-decoration: none; } .site-nav__btn::after { @@ -132,6 +133,7 @@ header { .site-nav__btn:hover { color: #0f172a; transform: translateY(-1px); + text-decoration: none; } .site-nav__btn.nav-active { @@ -443,6 +445,25 @@ h1 { .search-container { margin-left: auto; + display: flex; + align-items: center; + gap: 12px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.search-filter-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 13px; + font-weight: 600; + color: #1f2937; +} + +.search-filter-toggle input { + width: 16px; + height: 16px; } .tab-btn {