Add reusable DataCache class, unify frontend cache patterns

- Create DataCache class with fetch deduplication, invalidation, subscribers
- Instantiate 10 cache instances in state.js (streams, templates, sources, etc.)
- Replace inline fetch+parse+set patterns with cache.fetch() calls across modules
- Eliminate dual _scenesCache/_presetsCache sync via shared scenePresetsCache
- Remove 9 now-unused setter functions from state.js
- Clean up unused setter imports from audio-sources, value-sources, displays

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 19:35:20 +03:00
parent ff4e7f8adb
commit a34edf9650
11 changed files with 220 additions and 176 deletions

View File

@@ -8,7 +8,7 @@ import {
_deviceBrightnessCache,
kcWebSockets,
ledPreviewWebSockets,
_cachedValueSources, set_cachedValueSources,
_cachedValueSources, valueSourcesCache,
} from '../core/state.js';
import { API_BASE, getHeaders, fetchWithAuth, escapeHtml } from '../core/api.js';
import { t } from '../core/i18n.js';
@@ -241,18 +241,14 @@ function _populateBrightnessVsDropdown(selectedId = '') {
export async function showTargetEditor(targetId = null, cloneData = null) {
try {
// Load devices, CSS sources, and value sources for dropdowns
const [devicesResp, cssResp, vsResp] = await Promise.all([
const [devicesResp, cssResp] = await Promise.all([
fetch(`${API_BASE}/devices`, { headers: getHeaders() }),
fetchWithAuth('/color-strip-sources'),
fetchWithAuth('/value-sources'),
valueSourcesCache.fetch(),
]);
const devices = devicesResp.ok ? (await devicesResp.json()).devices || [] : [];
const cssSources = cssResp.ok ? (await cssResp.json()).sources || [] : [];
if (vsResp.ok) {
const vsData = await vsResp.json();
set_cachedValueSources(vsData.sources || []);
}
set_targetEditorDevices(devices);
_editorCssSources = cssSources;
@@ -478,13 +474,13 @@ export async function loadTargetsTab() {
try {
// Fetch devices, targets, CSS sources, picture sources, pattern templates, and value sources in parallel
const [devicesResp, targetsResp, cssResp, psResp, patResp, vsResp, asResp] = await Promise.all([
const [devicesResp, targetsResp, cssResp, psResp, patResp, valueSrcArr, asResp] = await Promise.all([
fetchWithAuth('/devices'),
fetchWithAuth('/picture-targets'),
fetchWithAuth('/color-strip-sources').catch(() => null),
fetchWithAuth('/picture-sources').catch(() => null),
fetchWithAuth('/pattern-templates').catch(() => null),
fetchWithAuth('/value-sources').catch(() => null),
valueSourcesCache.fetch().catch(() => []),
fetchWithAuth('/audio-sources').catch(() => null),
]);
@@ -515,10 +511,7 @@ export async function loadTargetsTab() {
}
let valueSourceMap = {};
if (vsResp && vsResp.ok) {
const vsData = await vsResp.json();
(vsData.sources || []).forEach(s => { valueSourceMap[s.id] = s; });
}
valueSrcArr.forEach(s => { valueSourceMap[s.id] = s; });
let audioSourceMap = {};
if (asResp && asResp.ok) {