window.App = window.App || {}; App.storage = App.storage || {}; App.session = App.session || {}; (function() { const { FAVORITES_KEY, FAVORITES_VISIBILITY_KEY } = App.constants; // Basic localStorage helpers. App.storage.getConfig = function() { return JSON.parse(localStorage.getItem('config')) || { servers: [] }; }; App.storage.setConfig = function(nextConfig) { localStorage.setItem('config', JSON.stringify(nextConfig)); }; App.storage.getSession = function() { return JSON.parse(localStorage.getItem('session')) || null; }; App.storage.setSession = function(nextSession) { localStorage.setItem('session', JSON.stringify(nextSession)); }; App.storage.getPreferences = function() { return JSON.parse(localStorage.getItem('preferences')) || {}; }; App.storage.setPreferences = function(nextPreferences) { localStorage.setItem('preferences', JSON.stringify(nextPreferences)); }; App.storage.getServerEntries = function() { const config = App.storage.getConfig(); if (!config.servers || !Array.isArray(config.servers)) return []; return config.servers.map((serverObj) => { const server = Object.keys(serverObj)[0]; return { url: server, data: serverObj[server] || null }; }); }; // Options/session helpers that power channel selection and filters. App.session.serializeOptions = function(options) { const serialized = {}; Object.entries(options || {}).forEach(([key, value]) => { if (Array.isArray(value)) { serialized[key] = value.map((entry) => entry.id); } else if (value && value.id) { serialized[key] = value.id; } }); return serialized; }; App.session.hydrateOptions = function(channel, savedOptions) { const hydrated = {}; if (!channel || !Array.isArray(channel.options)) return hydrated; const saved = savedOptions || {}; channel.options.forEach((optionGroup) => { const allOptions = optionGroup.options || []; const savedValue = saved[optionGroup.id]; if (optionGroup.multiSelect) { const selectedIds = Array.isArray(savedValue) ? savedValue : []; const selected = allOptions.filter((opt) => selectedIds.includes(opt.id)); hydrated[optionGroup.id] = selected.length > 0 ? selected : allOptions.slice(0, 1); } else { const selected = allOptions.find((opt) => opt.id === savedValue) || allOptions[0]; if (selected) hydrated[optionGroup.id] = selected; } }); return hydrated; }; App.session.savePreference = function(session) { if (!session || !session.server || !session.channel) return; const prefs = App.storage.getPreferences(); const serverPrefs = prefs[session.server] || {}; serverPrefs.channelId = session.channel.id; serverPrefs.optionsByChannel = serverPrefs.optionsByChannel || {}; serverPrefs.optionsByChannel[session.channel.id] = App.session.serializeOptions(session.options); prefs[session.server] = serverPrefs; App.storage.setPreferences(prefs); }; App.session.buildDefaultOptions = function(channel) { const selected = {}; if (!channel || !Array.isArray(channel.options)) return selected; channel.options.forEach((optionGroup) => { if (!optionGroup.options || optionGroup.options.length === 0) return; if (optionGroup.multiSelect) { selected[optionGroup.id] = [optionGroup.options[0]]; } else { selected[optionGroup.id] = optionGroup.options[0]; } }); return selected; }; // Ensures defaults exist and refreshes server status. App.storage.ensureDefaults = async function() { if (!localStorage.getItem('config')) { localStorage.setItem('config', JSON.stringify({ servers: [ { "https://getfigleaf.com": {} }, { "https://hottubapp.io": {} }, { "https://hottub.spacemoehre.de": {} } ] })); } if (!localStorage.getItem('theme')) { localStorage.setItem('theme', 'dark'); } if (!localStorage.getItem(FAVORITES_KEY)) { localStorage.setItem(FAVORITES_KEY, JSON.stringify([])); } if (!localStorage.getItem(FAVORITES_VISIBILITY_KEY)) { localStorage.setItem(FAVORITES_VISIBILITY_KEY, 'true'); } await App.storage.initializeServerStatus(); }; // Fetches server status and keeps the session pointing to a valid channel/options. App.storage.initializeServerStatus = async function() { const config = JSON.parse(localStorage.getItem('config')); if (!config || !config.servers) return; const statusPromises = config.servers.map(async (serverObj) => { const server = Object.keys(serverObj)[0]; try { const response = await fetch(`/api/status`, { method: "POST", body: JSON.stringify({ server: server }), headers: { "Content-Type": "application/json" }, }); const status = await response.json(); serverObj[server] = status; } catch (err) { serverObj[server] = { online: false, channels: [] }; } }); await Promise.all(statusPromises); localStorage.setItem('config', JSON.stringify(config)); const existingSession = App.storage.getSession(); const serverKeys = config.servers.map((serverObj) => Object.keys(serverObj)[0]); if (serverKeys.length === 0) return; const selectedServerKey = existingSession && serverKeys.includes(existingSession.server) ? existingSession.server : serverKeys[0]; const serverEntry = config.servers.find((serverObj) => Object.keys(serverObj)[0] === selectedServerKey); const serverData = serverEntry ? serverEntry[selectedServerKey] : null; if (serverData && serverData.channels && serverData.channels.length > 0) { const prefs = App.storage.getPreferences(); const serverPrefs = prefs[selectedServerKey] || {}; const preferredChannelId = serverPrefs.channelId; const channel = serverData.channels.find((ch) => ch.id === preferredChannelId) || serverData.channels[0]; const savedOptions = serverPrefs.optionsByChannel ? serverPrefs.optionsByChannel[channel.id] : null; const options = savedOptions ? App.session.hydrateOptions(channel, savedOptions) : App.session.buildDefaultOptions(channel); const sessionData = { server: selectedServerKey, channel: channel, options: options, }; App.storage.setSession(sessionData); App.session.savePreference(sessionData); } }; })();