Add support for disabling broken profiles
This commit is contained in:
171
web/app.js
171
web/app.js
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user