Add support for disabling broken profiles

This commit is contained in:
2026-04-12 19:23:34 +02:00
parent ad673f29ad
commit 9b329e513a
5 changed files with 566 additions and 59 deletions

View File

@@ -31,6 +31,7 @@ let updatesStreamHealthy = false;
let updatesShouldResyncOnConnect = false;
const MAX_PROFILES = 5;
const DEFAULT_ACTIVE_PROFILES = Array.from({ length: MAX_PROFILES }, (_unused, index) => index + 1);
const PROFILE_NAMES = {
1: 'Profil 1',
2: 'Profil 2',
@@ -38,11 +39,42 @@ const PROFILE_NAMES = {
4: 'Profil 4',
5: 'Profil 5'
};
let activeProfileNumbers = [...DEFAULT_ACTIVE_PROFILES];
function isValidProfileNumber(value) {
return Number.isInteger(value) && value >= 1 && value <= MAX_PROFILES;
}
function normalizeActiveProfileNumbers(values) {
if (!Array.isArray(values)) {
return [...DEFAULT_ACTIVE_PROFILES];
}
const seen = new Set();
const normalized = values
.map((value) => parseInt(value, 10))
.filter((value) => isValidProfileNumber(value) && !seen.has(value) && seen.add(value))
.sort((a, b) => a - b);
return normalized.length ? normalized : [...DEFAULT_ACTIVE_PROFILES];
}
function getActiveProfileNumbers() {
return activeProfileNumbers.length ? [...activeProfileNumbers] : [...DEFAULT_ACTIVE_PROFILES];
}
function isSelectableProfileNumber(value) {
return isValidProfileNumber(value) && getActiveProfileNumbers().includes(value);
}
function getActiveTargetCountLimit() {
return Math.max(1, getActiveProfileNumbers().length);
}
function getTargetCountErrorMessage() {
return `Die Anzahl der benötigten Profile muss zwischen 1 und ${getActiveTargetCountLimit()} liegen.`;
}
function redirectToLogin() {
try {
const redirect = encodeURIComponent(window.location.href);
@@ -2142,11 +2174,12 @@ function normalizeRequiredProfiles(post) {
}
const parsedTarget = parseInt(post.target_count, 10);
const activeProfiles = getActiveProfileNumbers();
const count = Number.isNaN(parsedTarget)
? 1
: Math.min(MAX_PROFILES, Math.max(1, parsedTarget));
: Math.min(activeProfiles.length, Math.max(1, parsedTarget));
return Array.from({ length: count }, (_, index) => index + 1);
return activeProfiles.slice(0, count);
}
function getTabButtons() {
@@ -3510,7 +3543,7 @@ function applyAutoRefreshSettings() {
autoRefreshTimer = null;
}
if (!isValidProfileNumber(currentProfile)) {
if (!isSelectableProfileNumber(currentProfile)) {
return;
}
@@ -3534,7 +3567,7 @@ function applyAutoRefreshSettings() {
if (document.hidden) {
return;
}
if (!isValidProfileNumber(currentProfile)) {
if (!isSelectableProfileNumber(currentProfile)) {
return;
}
fetchPosts({ showLoader: false });
@@ -3782,6 +3815,90 @@ function applyScreenshotModalSize() {
});
}
function renderProfileSelectorOptions() {
if (!profileSelectElement) {
return;
}
const activeProfiles = getActiveProfileNumbers();
profileSelectElement.innerHTML = `
<option value="">-- Profil wählen --</option>
${activeProfiles.map((profileNumber) => `
<option value="${profileNumber}">${getProfileName(profileNumber)}</option>
`).join('')}
`;
if (isSelectableProfileNumber(currentProfile)) {
profileSelectElement.value = String(currentProfile);
}
}
function renderManualTargetOptions(selectedValue = 1) {
if (!manualPostTargetSelect) {
return;
}
const maxCount = getActiveTargetCountLimit();
const normalizedSelected = Math.max(1, Math.min(maxCount, parseInt(selectedValue, 10) || 1));
manualPostTargetSelect.innerHTML = Array.from({ length: maxCount }, (_unused, index) => {
const value = index + 1;
return `<option value="${value}">${value}</option>`;
}).join('');
manualPostTargetSelect.value = String(normalizedSelected);
}
function clearSelectedProfile(message = 'Bitte zuerst ein Profil auswählen.') {
if (profileSelectElement) {
profileSelectElement.value = '';
}
currentProfile = null;
try {
localStorage.removeItem('profileNumber');
} catch (error) {
console.warn('Konnte gespeichertes Profil nicht entfernen:', error);
}
pendingOpenCooldownMap = loadPendingOpenCooldownMap(null);
renderPosts();
applyAutoRefreshSettings();
showError(message);
}
function applyProfileSettings(settings, { rerender = true, refreshPosts = false } = {}) {
const nextActiveProfiles = normalizeActiveProfileNumbers(settings && settings.active_profiles);
activeProfileNumbers = nextActiveProfiles;
renderProfileSelectorOptions();
renderManualTargetOptions(manualPostTargetSelect ? manualPostTargetSelect.value : 1);
if (currentProfile !== null && !isSelectableProfileNumber(currentProfile)) {
clearSelectedProfile('Das gespeicherte Profil ist deaktiviert. Bitte wähle ein aktives Profil.');
} else if (profileSelectElement && isSelectableProfileNumber(currentProfile)) {
profileSelectElement.value = String(currentProfile);
}
if (rerender && currentProfile !== null) {
renderPosts();
}
if (refreshPosts) {
fetchPosts({ showLoader: false });
}
}
async function loadProfileSettings() {
try {
const response = await apiFetch(`${API_URL}/profile-settings`);
if (!response.ok) {
return null;
}
const data = await response.json();
applyProfileSettings(data, { rerender: false });
return data;
} catch (error) {
console.warn('Profil-Einstellungen konnten nicht geladen werden:', error);
return null;
}
}
async function fetchProfileState() {
try {
const response = await apiFetch(`${API_URL}/profile-state`);
@@ -3815,16 +3932,16 @@ async function pushProfileState(profileNumber) {
}
function ensureProfileSelected() {
if (isValidProfileNumber(currentProfile)) {
if (isSelectableProfileNumber(currentProfile)) {
hideError();
return true;
}
showError('Bitte zuerst ein Profil auswählen.');
showError('Bitte zuerst ein aktives Profil auswählen.');
return false;
}
function applyProfileNumber(profileNumber, { fromBackend = false } = {}) {
if (!isValidProfileNumber(profileNumber)) {
if (!isSelectableProfileNumber(profileNumber)) {
return;
}
@@ -3863,20 +3980,16 @@ function applyProfileNumber(profileNumber, { fromBackend = false } = {}) {
// Load profile from localStorage
function loadProfile() {
fetchProfileState().then((backendProfile) => {
if (isValidProfileNumber(backendProfile)) {
if (isSelectableProfileNumber(backendProfile)) {
applyProfileNumber(backendProfile, { fromBackend: true });
} else {
const saved = localStorage.getItem('profileNumber');
const parsed = saved ? parseInt(saved, 10) : NaN;
if (isValidProfileNumber(parsed)) {
if (isSelectableProfileNumber(parsed)) {
applyProfileNumber(parsed, { fromBackend: true });
return;
}
if (profileSelectElement) {
profileSelectElement.value = '';
}
currentProfile = null;
showError('Bitte zuerst ein Profil auswählen.');
clearSelectedProfile('Bitte zuerst ein aktives Profil auswählen.');
}
});
}
@@ -3893,8 +4006,12 @@ function startProfilePolling() {
profilePollTimer = setInterval(async () => {
const backendProfile = await fetchProfileState();
if (backendProfile && backendProfile !== currentProfile) {
if (isSelectableProfileNumber(backendProfile) && backendProfile !== currentProfile) {
applyProfileNumber(backendProfile, { fromBackend: true });
return;
}
if (backendProfile === null && currentProfile !== null && !isSelectableProfileNumber(currentProfile)) {
clearSelectedProfile('Das gespeicherte Profil ist deaktiviert. Bitte wähle ein aktives Profil.');
}
}, 5000);
}
@@ -3903,8 +4020,8 @@ function startProfilePolling() {
if (profileSelectElement) {
profileSelectElement.addEventListener('change', (e) => {
const parsed = parseInt(e.target.value, 10);
if (!isValidProfileNumber(parsed)) {
showError('Bitte zuerst ein Profil auswählen.');
if (!isSelectableProfileNumber(parsed)) {
showError('Bitte zuerst ein aktives Profil auswählen.');
return;
}
saveProfile(parsed);
@@ -4984,7 +5101,7 @@ function createPostCard(post, status, meta = {}) {
<div class="post-target">
<span>Benötigte Profile:</span>
<select class="control-select post-target__select" data-post-id="${post.id}">
${Array.from({ length: MAX_PROFILES }, (_, index) => index + 1)
${Array.from({ length: getActiveTargetCountLimit() }, (_, index) => index + 1)
.map((value) => `
<option value="${value}" ${value === status.targetCount ? 'selected' : ''}>${value}</option>
`).join('')}
@@ -5180,7 +5297,7 @@ function populateManualPostForm(post) {
if (manualPostTargetSelect) {
const targetValue = parseInt(post.target_count, 10);
manualPostTargetSelect.value = Number.isNaN(targetValue) ? '1' : String(targetValue);
renderManualTargetOptions(Number.isNaN(targetValue) ? 1 : targetValue);
}
if (manualPostCreatorInput) {
@@ -5206,7 +5323,7 @@ function resetManualPostForm({ keepMessages = false } = {}) {
manualPostForm.reset();
if (manualPostTargetSelect) {
manualPostTargetSelect.value = '1';
renderManualTargetOptions(1);
}
if (manualPostUrlInput) {
@@ -5271,7 +5388,7 @@ async function updateTargetInline(postId, value, selectElement) {
return;
}
if (Number.isNaN(value) || value < 1 || value > MAX_PROFILES) {
if (Number.isNaN(value) || value < 1 || value > getActiveTargetCountLimit()) {
renderPosts();
return;
}
@@ -5327,8 +5444,8 @@ async function handleManualPostSubmit(event) {
const targetValue = manualPostTargetSelect ? manualPostTargetSelect.value : '1';
const parsedTarget = parseInt(targetValue, 10);
if (Number.isNaN(parsedTarget) || parsedTarget < 1 || parsedTarget > MAX_PROFILES) {
displayManualPostMessage('Die Anzahl der benötigten Profile muss zwischen 1 und 5 liegen.', 'error');
if (Number.isNaN(parsedTarget) || parsedTarget < 1 || parsedTarget > getActiveTargetCountLimit()) {
displayManualPostMessage(getTargetCountErrorMessage(), 'error');
return;
}
@@ -5603,6 +5720,11 @@ window.addEventListener('app:view-change', (event) => {
updatePostsScrollTopButtonVisibility();
});
window.addEventListener('profile-settings-updated', (event) => {
const settings = event && event.detail ? event.detail : null;
applyProfileSettings(settings || null, { refreshPosts: true });
});
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible' && pendingAutoOpenEnabled) {
if (pendingAutoOpenTimerId) {
@@ -5629,6 +5751,9 @@ async function bootstrapApp() {
initializeTabFromUrl();
updateMergeControlsUI();
loadSortMode();
renderProfileSelectorOptions();
renderManualTargetOptions(1);
await loadProfileSettings();
resetManualPostForm();
loadProfile();
startProfilePolling();