reworked site

This commit is contained in:
2025-11-16 13:02:05 +01:00
parent 339f03e38e
commit f9d8fc5b82
8 changed files with 628 additions and 181 deletions

View File

@@ -145,6 +145,9 @@ function scheduleUpdatesReconnect() {
}
function startUpdatesStream() {
if (isBookmarksPage) {
return;
}
if (typeof EventSource === 'undefined') {
console.warn('EventSource wird von diesem Browser nicht unterstützt. Fallback auf Polling.');
return;
@@ -246,6 +249,11 @@ const bookmarkForm = document.getElementById('bookmarkForm');
const bookmarkNameInput = document.getElementById('bookmarkName');
const bookmarkQueryInput = document.getElementById('bookmarkQuery');
const bookmarkCancelBtn = document.getElementById('bookmarkCancelBtn');
const bookmarkSearchInput = document.getElementById('bookmarkSearchInput');
const bookmarkSortSelect = document.getElementById('bookmarkSortSelect');
const bookmarkSortDirectionToggle = document.getElementById('bookmarkSortDirectionToggle');
const profileSelectElement = document.getElementById('profileSelect');
const isBookmarksPage = document.body.classList.contains('bookmarks-page');
const REFRESH_SETTINGS_KEY = 'trackerRefreshSettings';
const SORT_SETTINGS_KEY = 'trackerSortSettings';
@@ -296,6 +304,9 @@ let manualPostModalPreviousOverflow = '';
let activeDeadlinePicker = null;
let bookmarkPanelVisible = false;
let bookmarkOutsideHandler = null;
let bookmarkSearchTerm = '';
let bookmarkSortMode = 'recent';
let bookmarkSortDirection = 'desc';
const INITIAL_POST_LIMIT = 10;
const POST_LOAD_INCREMENT = 10;
@@ -501,6 +512,83 @@ function sortBookmarksByRecency(list) {
});
}
function getBookmarkLabelForComparison(bookmark = {}) {
const label = typeof bookmark.label === 'string' ? bookmark.label.trim() : '';
const query = typeof bookmark.query === 'string' ? bookmark.query.trim() : '';
return label || query || '';
}
function getBookmarkClickTimestamp(bookmark = {}) {
if (bookmark.last_clicked_at) {
const ts = new Date(bookmark.last_clicked_at).getTime();
if (!Number.isNaN(ts)) {
return ts;
}
}
return 0;
}
function sortBookmarksForDisplay(list) {
const items = [...list];
if (bookmarkSortMode === 'label') {
items.sort((a, b) => {
const labelA = getBookmarkLabelForComparison(a);
const labelB = getBookmarkLabelForComparison(b);
const result = labelA.localeCompare(labelB, 'de', { sensitivity: 'base' });
return bookmarkSortDirection === 'desc' ? -result : result;
});
return items;
}
items.sort((a, b) => {
const diff = getBookmarkClickTimestamp(b) - getBookmarkClickTimestamp(a);
if (diff !== 0) {
return bookmarkSortDirection === 'desc' ? diff : -diff;
}
const fallback = getBookmarkLabelForComparison(a).localeCompare(
getBookmarkLabelForComparison(b),
'de',
{ sensitivity: 'base' }
);
return bookmarkSortDirection === 'desc' ? fallback : -fallback;
});
return items;
}
function filterBookmarksBySearch(list) {
if (!bookmarkSearchTerm) {
return [...list];
}
const term = bookmarkSearchTerm.toLowerCase();
return list.filter((bookmark) => {
const label = (bookmark.label || bookmark.query || '').toLowerCase();
const query = (bookmark.query || '').toLowerCase();
return label.includes(term) || query.includes(term);
});
}
function getRecentBookmarks(list) {
const recent = sortBookmarksByRecency(list);
const RECENT_LIMIT = 5;
return recent.filter((bookmark) => bookmark.last_clicked_at).slice(0, RECENT_LIMIT);
}
function updateBookmarkSortDirectionUI() {
if (!bookmarkSortDirectionToggle) {
return;
}
const isAsc = bookmarkSortDirection === 'asc';
bookmarkSortDirectionToggle.setAttribute('aria-pressed', isAsc ? 'true' : 'false');
bookmarkSortDirectionToggle.setAttribute('title', isAsc ? 'Älteste zuerst' : 'Neueste zuerst');
const icon = bookmarkSortDirectionToggle.querySelector('.bookmark-sort__direction-icon');
if (icon) {
icon.textContent = isAsc ? '▲' : '▼';
} else {
bookmarkSortDirectionToggle.textContent = isAsc ? '▲' : '▼';
}
}
const DEFAULT_BOOKMARK_LAST_CLICK_KEY = 'trackerDefaultBookmarkLastClickedAt';
const bookmarkState = {
@@ -954,18 +1042,9 @@ function renderBookmarks() {
isDefault: true
};
const sorted = sortBookmarksByRecency(dynamicBookmarks);
const recent = [];
const RECENT_LIMIT = 5;
sorted.forEach((bookmark) => {
if (bookmark.last_clicked_at && recent.length < RECENT_LIMIT) {
recent.push(bookmark);
}
});
const alphabeticalAll = [...dynamicBookmarks]
.sort((a, b) => a.label.localeCompare(b.label, 'de', { sensitivity: 'base' }));
const filteredBookmarks = filterBookmarksBySearch(dynamicBookmarks);
const sortedForAll = sortBookmarksForDisplay(filteredBookmarks);
const recent = bookmarkSearchTerm ? [] : getRecentBookmarks(filteredBookmarks);
const sections = [];
@@ -977,17 +1056,25 @@ function renderBookmarks() {
});
}
const allItems = bookmarkSearchTerm ? sortedForAll : [staticDefault, ...sortedForAll];
const allTitle = bookmarkSearchTerm
? `Suchergebnisse${filteredBookmarks.length ? ` (${filteredBookmarks.length})` : ''}`
: 'Alle Bookmarks';
sections.push({
id: 'all',
title: 'Alle Bookmarks',
items: [staticDefault, ...alphabeticalAll]
id: bookmarkSearchTerm ? 'search' : 'all',
title: allTitle,
items: allItems
});
let renderedAnySection = false;
sections.forEach((section) => {
if (!section.items.length) {
return;
}
renderedAnySection = true;
const sectionElement = document.createElement('section');
sectionElement.className = 'bookmark-section';
sectionElement.dataset.section = section.id;
@@ -1012,6 +1099,17 @@ function renderBookmarks() {
sectionElement.appendChild(list);
bookmarksList.appendChild(sectionElement);
});
if (!renderedAnySection) {
const empty = document.createElement('div');
empty.className = 'bookmark-empty';
if (bookmarkSearchTerm) {
empty.textContent = `Keine Bookmarks gefunden für „${bookmarkSearchTerm}“.`;
} else {
empty.textContent = 'Noch keine Bookmarks gespeichert.';
}
bookmarksList.appendChild(empty);
}
}
function resetBookmarkForm() {
@@ -1197,6 +1295,34 @@ function initializeBookmarks() {
if (bookmarkForm) {
bookmarkForm.addEventListener('submit', handleBookmarkSubmit);
}
if (bookmarkSearchInput) {
bookmarkSearchInput.addEventListener('input', () => {
bookmarkSearchTerm = typeof bookmarkSearchInput.value === 'string'
? bookmarkSearchInput.value.trim()
: '';
renderBookmarks();
});
}
if (bookmarkSortSelect) {
bookmarkSortSelect.addEventListener('change', () => {
const value = bookmarkSortSelect.value;
if (value === 'label' || value === 'recent') {
bookmarkSortMode = value;
renderBookmarks();
}
});
}
if (bookmarkSortDirectionToggle) {
bookmarkSortDirectionToggle.addEventListener('click', () => {
bookmarkSortDirection = bookmarkSortDirection === 'desc' ? 'asc' : 'desc';
updateBookmarkSortDirectionUI();
renderBookmarks();
});
updateBookmarkSortDirectionUI();
}
}
function getSortSettingsPageKey() {
@@ -2288,6 +2414,10 @@ function applyAutoRefreshSettings() {
: '';
}
if (isBookmarksPage) {
return;
}
if (!autoRefreshSettings.enabled) {
return;
}
@@ -2582,7 +2712,9 @@ function applyProfileNumber(profileNumber, { fromBackend = false } = {}) {
return;
}
document.getElementById('profileSelect').value = String(profileNumber);
if (profileSelectElement) {
profileSelectElement.value = String(profileNumber);
}
if (currentProfile === profileNumber) {
if (!fromBackend) {
@@ -2598,8 +2730,10 @@ function applyProfileNumber(profileNumber, { fromBackend = false } = {}) {
pushProfileState(currentProfile);
}
resetVisibleCount();
renderPosts();
if (!isBookmarksPage) {
resetVisibleCount();
renderPosts();
}
}
// Load profile from localStorage
@@ -2637,9 +2771,11 @@ function startProfilePolling() {
}
// Profile selector change handler
document.getElementById('profileSelect').addEventListener('change', (e) => {
saveProfile(parseInt(e.target.value, 10));
});
if (profileSelectElement) {
profileSelectElement.addEventListener('change', (e) => {
saveProfile(parseInt(e.target.value, 10));
});
}
// Tab switching
document.querySelectorAll('.tab-btn').forEach(btn => {
@@ -2673,6 +2809,9 @@ if (autoRefreshToggle) {
autoRefreshSettings.enabled = !!autoRefreshToggle.checked;
saveAutoRefreshSettings();
applyAutoRefreshSettings();
if (isBookmarksPage) {
return;
}
if (autoRefreshSettings.enabled && updatesStreamHealthy) {
console.info('Live-Updates sind aktiv; automatisches Refresh bleibt pausiert.');
}
@@ -3980,7 +4119,9 @@ function checkAutoCheck() {
} catch (error) {
console.warn('Konnte check-Parameter nicht entfernen:', error);
}
fetchPosts({ showLoader: false });
if (!isBookmarksPage) {
fetchPosts({ showLoader: false });
}
}).catch(console.error);
}
}
@@ -4049,10 +4190,12 @@ loadAutoRefreshSettings();
initializeFocusParams();
initializeTabFromUrl();
loadSortMode();
resetManualPostForm();
loadProfile();
startProfilePolling();
fetchPosts();
checkAutoCheck();
startUpdatesStream();
if (!isBookmarksPage) {
resetManualPostForm();
loadProfile();
startProfilePolling();
fetchPosts();
checkAutoCheck();
startUpdatesStream();
}
applyAutoRefreshSettings();