Files
jacuzzi/frontend/js/storage.js
2026-02-09 13:27:08 +00:00

183 lines
7.3 KiB
JavaScript

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);
}
};
})();