minor changes

This commit is contained in:
2025-11-22 13:28:42 +01:00
parent 2e4a6ae7c4
commit 89ab27540d
2 changed files with 131 additions and 51 deletions

View File

@@ -242,6 +242,7 @@ const REFRESH_SETTINGS_KEY = 'trackerRefreshSettings';
const SORT_SETTINGS_KEY = 'trackerSortSettings';
const SORT_SETTINGS_LEGACY_KEY = 'trackerSortMode';
const FACEBOOK_TRACKING_PARAMS = ['__cft__', '__tn__', '__eep__', 'mibextid', 'set', 'comment_id', 'hoisted_section_header_type'];
const FACEBOOK_PROFILE_SEGMENT_BLOCKLIST = new Set(['reel', 'reels', 'watch', 'video.php', 'videos', 'photo.php', 'photos', 'story.php', 'permalink.php', 'share', 'posts']);
const VALID_SORT_MODES = new Set(['created', 'deadline', 'smart', 'lastCheck', 'lastChange']);
const DEFAULT_SORT_SETTINGS = { mode: 'created', direction: 'desc' };
const BOOKMARKS_BASE_URL = 'https://www.facebook.com/search/top';
@@ -444,6 +445,99 @@ function formatUrlForDisplay(url) {
}
}
function deriveFacebookProfileUrl(rawValue) {
if (typeof rawValue !== 'string') {
return null;
}
const trimmed = rawValue.trim();
if (!trimmed) {
return null;
}
let parsed;
try {
parsed = new URL(trimmed);
} catch (error) {
try {
parsed = new URL(trimmed, window.location.origin);
} catch (innerError) {
try {
parsed = new URL(trimmed, 'https://www.facebook.com');
} catch (fallbackError) {
return null;
}
}
}
const hostname = parsed.hostname.toLowerCase();
if (!hostname.endsWith('facebook.com')) {
return null;
}
const origin = `${parsed.protocol}//${parsed.hostname}`;
const normalizedPath = parsed.pathname.replace(/\/+$/, '') || '/';
const segments = normalizedPath.split('/').filter(Boolean);
const firstSegment = segments[0] || '';
const lowerFirst = firstSegment.toLowerCase();
const idParam = parsed.searchParams.get('id');
if (lowerFirst === 'groups' && segments[1]) {
return `${origin}/groups/${segments[1]}`;
}
if (lowerFirst === 'profile.php' && idParam) {
return `${origin}/profile.php?id=${encodeURIComponent(idParam)}`;
}
if (idParam && (lowerFirst === 'story.php' || lowerFirst === 'permalink.php')) {
return `${origin}/profile.php?id=${encodeURIComponent(idParam)}`;
}
if (lowerFirst === 'people' && segments[1] && segments[2]) {
return `${origin}/people/${segments[1]}/${segments[2]}`;
}
if (firstSegment && !FACEBOOK_PROFILE_SEGMENT_BLOCKLIST.has(lowerFirst)) {
return `${origin}/${firstSegment}`;
}
if (idParam) {
return `${origin}/profile.php?id=${encodeURIComponent(idParam)}`;
}
return null;
}
function getProfileLinkForPost(post) {
if (!post || typeof post !== 'object') {
return null;
}
const candidates = [];
if (typeof post.url === 'string') {
candidates.push(post.url);
}
if (Array.isArray(post.alternate_urls)) {
for (const alt of post.alternate_urls) {
if (typeof alt === 'string') {
candidates.push(alt);
}
}
}
for (const candidate of candidates) {
const profileUrl = deriveFacebookProfileUrl(candidate);
if (profileUrl) {
return profileUrl;
}
}
return null;
}
function toTimestamp(value, fallback = null) {
if (!value) {
return fallback;
@@ -3454,14 +3548,6 @@ function createPostCard(post, status, meta = {}) {
const tabTotalCount = typeof meta.tabTotalCount === 'number' ? meta.tabTotalCount : posts.length;
const searchActive = !!meta.searchActive;
const counterBadge = displayIndex !== null
? `
<div class="post-counter" aria-hidden="true">
<span class="post-counter__value">${String(displayIndex).padStart(2, '0')}</span>
</div>
`
: '';
const profileRowsHtml = status.profileStatuses.map((profileStatus) => {
const classes = ['profile-line', `profile-line--${profileStatus.status}`];
const isCurrentProfile = parseInt(profileStatus.profile_number, 10) === status.profileNumber;
@@ -3555,6 +3641,10 @@ function createPostCard(post, status, meta = {}) {
const creatorDisplay = creatorName || 'Unbekannt';
const titleText = (post.title && post.title.trim()) ? post.title.trim() : creatorDisplay;
const profileLink = getProfileLinkForPost(post);
const creatorContent = profileLink
? `<a href="${escapeHtml(profileLink)}" target="_blank" rel="noopener noreferrer" class="post-creator__link">${escapeHtml(creatorDisplay)}</a>`
: escapeHtml(creatorDisplay);
const deadlineText = formatDeadline(post.deadline_at);
const hasDeadline = Boolean(post.deadline_at);
@@ -3598,7 +3688,6 @@ function createPostCard(post, status, meta = {}) {
return `
<div class="post-card ${status.isComplete ? 'complete' : ''}" id="post-${post.id}">
<div class="post-header">
${counterBadge}
<div class="post-title-with-checkbox">
<div class="post-title">${escapeHtml(titleText)}</div>
<label class="success-checkbox success-checkbox--header">
@@ -3607,6 +3696,9 @@ function createPostCard(post, status, meta = {}) {
</label>
</div>
<div class="post-header-right">
<div class="post-status ${status.isComplete ? 'complete' : ''}">
${status.checkedCount}/${status.targetCount} ${status.isComplete ? '✓' : ''}
</div>
<div class="post-target">
<span>Benötigte Profile:</span>
<select class="control-select post-target__select" data-post-id="${post.id}">
@@ -3616,9 +3708,6 @@ function createPostCard(post, status, meta = {}) {
`).join('')}
</select>
</div>
<div class="post-status ${status.isComplete ? 'complete' : ''}">
${status.checkedCount}/${status.targetCount} ${status.isComplete ? '✓' : ''}
</div>
</div>
</div>
@@ -3640,9 +3729,8 @@ function createPostCard(post, status, meta = {}) {
<div>Erstellt: ${escapeHtml(createdDate)}</div>
<div>Letzte Änderung: ${escapeHtml(lastChangeDate)}</div>
</div>
<div class="post-creator">Erstellt von: ${escapeHtml(creatorDisplay)}</div>
<div class="post-creator">Erstellt von: ${creatorContent}</div>
</div>
${directLinkHtml}
<div class="post-deadline-row" data-post-id="${post.id}">
<span class="${deadlineClasses.join(' ')}" ${deadlineStyle}>Deadline: ${escapeHtml(deadlineText)}</span>
@@ -3655,6 +3743,7 @@ function createPostCard(post, status, meta = {}) {
</button>
` : ''}
</div>
${directLinkHtml}
<div class="post-profiles">
${profileRowsHtml}