From a3094caf7053677728d33d35dd9188d6ae5be653 Mon Sep 17 00:00:00 2001 From: HighImKevin Date: Thu, 15 Oct 2026 15:34:27 -0400 Subject: [PATCH] Add global cache setting to override all section caches --- configuration/homeScreen-configuration.js | 135 ++++++++++++++++++++-- 1 file changed, 123 insertions(+), 12 deletions(-) diff --git a/configuration/homeScreen-configuration.js b/configuration/homeScreen-configuration.js index f9a9f65..3da2490 100644 --- a/configuration/homeScreen-configuration.js +++ b/configuration/homeScreen-configuration.js @@ -1530,6 +1530,12 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; * Get enabled home sections and discovery sections * @returns {Object} Object with enabledHomeSections and enabledDiscoverySections */ + function applyGlobalCacheTtlToSections(sections, globalCacheTtl) { + if (!(Number(globalCacheTtl) >= 0) || !Array.isArray(sections)) return sections; + const ttl = Number(globalCacheTtl); + return sections.map((section) => (section ? { ...section, ttl } : section)); + } + function getSections() { const config = getConfig(); @@ -1543,10 +1549,19 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; isInSeasonalPeriod }); + const globalCacheTtl = config.CACHE?.globalCacheTtl; + const home = applyGlobalCacheTtlToSections( + [...enabledHomeSections, ...enabledSeasonalSections, ...enabledCustomSections], + globalCacheTtl + ); + const discovery = applyGlobalCacheTtlToSections( + [...enabledDiscoverySections, ...enabledCustomDiscoverySections], + globalCacheTtl + ); return { - enabledHomeSections: [...enabledHomeSections, ...enabledSeasonalSections, ...enabledCustomSections], - enabledDiscoverySections: [...enabledDiscoverySections, ...enabledCustomDiscoverySections] + enabledHomeSections: home, + enabledDiscoverySections: discovery } } @@ -2105,12 +2120,16 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; /** * Build compact section row HTML */ - function buildSectionRowHTML(section, type = 'home', groupName = null) { + function buildSectionRowHTML(section, type = 'home') { const isEnabled = section.enabled !== false; const isSeasonal = section.startDate && section.endDate; const isSpotlight = section.renderMode === 'Spotlight'; const isDiscovery = section.discoveryEnabled === true; const isCustom = type.toLowerCase() === 'custom' || section.id.startsWith('custom'); + const descriptionText = String(section.description || '').trim(); + const descriptionHtml = descriptionText + ? `${escapeHtml(descriptionText)}` + : ''; return `
@@ -2125,12 +2144,12 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
- ${groupName ? `${groupName}` : ''} Order: ${section.order || 0} ${section.cardFormat && !isSpotlight ? `${section.cardFormat}` : ''} ${isSpotlight ? `Spotlight` : ''} ${isSeasonal ? `Active from: ${section.startDate} - ${section.endDate}` : ''} ${isDiscovery ? `Discovery` : ''} + ${descriptionHtml}
@@ -2291,7 +2310,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
- ${sections.map(s => buildSectionRowHTML(s, s.sectionType ?? sectionType, s.sectionType)).join('')} + ${sections.map(s => buildSectionRowHTML(s, s.sectionType ?? sectionType)).join('')}
`; @@ -2814,6 +2833,28 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; ]; } + /** Full section-cache style presets including No Cache + Disabled (undefined). */ + function getGlobalCachePresets() { + const presets = window.KefinHomeScreenAdvancedEditor?.getCachePresets?.(); + const cache = currentConfig?.CACHE || window.KefinHomeConfig2?.CACHE || {}; + const base = Array.isArray(presets) && presets.length + ? presets.slice() + : [ + { key: 'FORCE_REFRESH', label: 'No Cache', ms: cache.FORCE_REFRESH_TTL ?? 0 }, + { key: 'VERY_SHORT', label: 'Very Short (1 min)', ms: cache.VERY_SHORT_TTL ?? 60000 }, + { key: 'SHORT', label: 'Short (5 min)', ms: cache.SHORT_TTL ?? 300000 }, + { key: 'DEFAULT', label: 'Default (1 hour)', ms: cache.DEFAULT_TTL ?? 3600000 }, + { key: 'LONG', label: 'Long (24 hours)', ms: cache.LONG_TTL ?? 86400000 }, + { key: 'STATIC', label: 'Static (1 week)', ms: cache.STATIC_TTL ?? 604800000 }, + { key: 'DISCOVERY', label: 'Discovery (6 hours)', ms: cache.DISCOVERY_TTL ?? 21600000 }, + { key: 'CUSTOM', label: 'Custom', ms: null } + ]; + return [ + { key: 'DISABLED', label: 'Disabled', ms: undefined }, + ...base + ]; + } + function matchDefaultTtlPreset(ms) { const value = Number(ms); if (!Number.isFinite(value)) { @@ -2829,6 +2870,23 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; return { preset: 'CUSTOM', customValue: String(seconds), customUnit: 'seconds' }; } + function matchGlobalCachePreset(ms) { + if (ms == null || ms === '' || !Number.isFinite(Number(ms))) { + return { preset: 'DISABLED', customValue: '', customUnit: 'minutes' }; + } + const value = Number(ms); + const exact = getGlobalCachePresets().find( + (p) => p.key !== 'CUSTOM' && p.key !== 'DISABLED' && p.ms === value + ); + if (exact) return { preset: exact.key, customValue: '', customUnit: 'minutes' }; + const minutes = Math.round(value / 60000); + if (minutes > 0 && minutes * 60000 === value) { + return { preset: 'CUSTOM', customValue: String(minutes), customUnit: 'minutes' }; + } + const seconds = Math.round(value / 1000); + return { preset: 'CUSTOM', customValue: String(seconds), customUnit: 'seconds' }; + } + function resolveDefaultTtlMsFromForm(root) { const presetEl = root.querySelector('#cache-DEFAULT_TTL-preset'); if (!presetEl) return null; @@ -2844,6 +2902,23 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; return preset?.ms ?? (currentConfig?.CACHE?.DEFAULT_TTL ?? 3600000); } + /** @returns {number|undefined} undefined = Disabled (omit / clear globalCacheTtl) */ + function resolveGlobalCacheTtlMsFromForm(root) { + const presetEl = root.querySelector('#cache-globalCacheTtl-preset'); + if (!presetEl) return null; + const presetKey = presetEl.value || 'DISABLED'; + if (presetKey === 'DISABLED') return undefined; + if (presetKey === 'CUSTOM') { + const raw = parseFloat(root.querySelector('#cache-globalCacheTtl-custom-value')?.value || ''); + const unit = root.querySelector('#cache-globalCacheTtl-custom-unit')?.value || 'minutes'; + const unitMs = TTL_UNIT_OPTIONS.find((u) => u.value === unit)?.ms || 60000; + if (!Number.isFinite(raw) || raw < 0) return 0; + return Math.round(raw * unitMs); + } + const preset = getGlobalCachePresets().find((p) => p.key === presetKey); + return preset?.ms ?? undefined; + } + /** * Build cache settings HTML */ @@ -2856,6 +2931,12 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; value: p.key, label: p.label })); + const globalMatch = matchGlobalCachePreset(cache.globalCacheTtl); + const isCustomGlobal = globalMatch.preset === 'CUSTOM'; + const globalPresetOptions = getGlobalCachePresets().map((p) => ({ + value: p.key, + label: p.label + })); return `
@@ -2863,8 +2944,16 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;

Every Home Screen Section has a cache expiration time associated to it. If the section has no explicitly defined cache value, it will use the Default setting below.

When a Section is being loaded, if the cache has not expired, the cached items will be rendered instead of requesting new data. If a Section has "No Cache" set, it will always request new data from the server.

The Cache settings listed below will appear as options when creating or editing a Home Screen Section in the "Advanced Options" at the bottom of the editor.

+

Global Cache: when set (not Disabled), individual section cache timers are ignored and every section uses this value.

+
+ ${buildSelect('cache-globalCacheTtl-preset', globalPresetOptions, globalMatch.preset, 'Global Cache')} +
+ ${buildTextInput('cache-globalCacheTtl-custom-value', globalMatch.customValue || '', 'Custom Duration', 'number')} + ${buildSelect('cache-globalCacheTtl-custom-unit', TTL_UNIT_OPTIONS.map((u) => ({ value: u.value, label: u.value })), globalMatch.customUnit || 'minutes', 'Unit')} +
+
${buildSelect('cache-DEFAULT_TTL-preset', presetOptions, defaultMatch.preset, 'Default')}
@@ -2979,9 +3068,11 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; } } - if (root.querySelector('#cache-DEFAULT_TTL-preset') || root.querySelector('#cache-VERY_SHORT_TTL')) { + if (root.querySelector('#cache-DEFAULT_TTL-preset') + || root.querySelector('#cache-VERY_SHORT_TTL') + || root.querySelector('#cache-globalCacheTtl-preset')) { const defaultTtl = resolveDefaultTtlMsFromForm(root); - currentConfig.CACHE = { + const nextCache = { ...(currentConfig.CACHE || {}), DEFAULT_TTL: defaultTtl != null ? defaultTtl : (currentConfig.CACHE?.DEFAULT_TTL ?? 1800000), VERY_SHORT_TTL: minutesToMs(parseInt(root.querySelector('#cache-VERY_SHORT_TTL')?.value || '1', 10)), @@ -2991,6 +3082,15 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; DISCOVERY_TTL: minutesToMs(parseInt(root.querySelector('#cache-DISCOVERY_TTL')?.value || '60', 10)), FORCE_REFRESH_TTL: 0 }; + if (root.querySelector('#cache-globalCacheTtl-preset')) { + const globalTtl = resolveGlobalCacheTtlMsFromForm(root); + if (globalTtl === undefined) { + delete nextCache.globalCacheTtl; + } else if (globalTtl != null) { + nextCache.globalCacheTtl = globalTtl; + } + } + currentConfig.CACHE = nextCache; } if (root.querySelector('#spotlight-cycleBackdrops') || root.querySelector('[id^="spotlight-"]')) { @@ -3100,7 +3200,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; updateSectionInGroups(currentConfig[found.groupType], sectionData.id, sectionData); await saveConfig(currentConfig); showToast('Section saved'); - if (updateSectionRowInPlace(sectionData, sectionTypeForRow, found.group?.name)) { + if (updateSectionRowInPlace(sectionData, sectionTypeForRow)) { scrollSectionRowIntoView(sectionData.id); } else { refreshMainModal({ @@ -3121,9 +3221,9 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; if (options.refreshHomeOnSave && !mainModalInstance) { try { - await window.homeScreen3?.refreshHomeSections?.(); + await window.homeScreen3?.refreshHomeSectionById?.(sectionData.id, sectionData); } catch (e) { - WARN('Failed to refresh home after section save:', e); + WARN('Failed to refresh home section after save:', e); } } }, @@ -3240,7 +3340,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; * Replace a single .section-row in the main modal without full refresh (preserves scroll). * @returns {boolean} true if row was updated */ - function updateSectionRowInPlace(sectionData, sectionType = 'home', groupName = null) { + function updateSectionRowInPlace(sectionData, sectionType = 'home') { if (!mainModalInstance?.dialogContent || !sectionData?.id) return false; const escapedId = escapeSectionIdForSelector(sectionData.id); const row = mainModalInstance.dialogContent.querySelector(`.section-row[data-section-id="${escapedId}"]`); @@ -3248,7 +3348,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; const type = sectionType || row.dataset.sectionType || 'home'; const wrap = document.createElement('div'); - wrap.innerHTML = buildSectionRowHTML(sectionData, type, groupName).trim(); + wrap.innerHTML = buildSectionRowHTML(sectionData, type).trim(); const newRow = wrap.firstElementChild; if (!newRow) return false; row.replaceWith(newRow); @@ -3527,9 +3627,19 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; row.style.display = preset.value === 'CUSTOM' ? 'grid' : 'none'; } + function syncGlobalCacheTtlCustomRow(contentRoot) { + const root = contentRoot || document.querySelector(`.dialogContainer[data-modal-id="${MAIN_MODAL_ID}"] #global-settings-content`); + if (!root) return; + const preset = root.querySelector('#cache-globalCacheTtl-preset'); + const row = root.querySelector('#cache-globalCacheTtl-custom-row'); + if (!row || !preset) return; + row.style.display = preset.value === 'CUSTOM' ? 'grid' : 'none'; + } + function wireGlobalSettingsPanelListeners(contentRoot) { if (!contentRoot) return; syncDefaultTtlCustomRow(contentRoot); + syncGlobalCacheTtlCustomRow(contentRoot); window.KefinTweaksUI?.bindToggleCards?.(contentRoot); updateHscToggleCardHints(contentRoot, GENERAL_TOGGLE_DESCRIPTIONS); updateHscToggleCardHints(contentRoot, DISCOVERY_TOGGLE_DESCRIPTIONS); @@ -3543,6 +3653,7 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`; dialog.addEventListener('change', (e) => { if (!e.target?.closest?.('#global-settings-content')) return; if (e.target.id === 'cache-DEFAULT_TTL-preset') syncDefaultTtlCustomRow(); + if (e.target.id === 'cache-globalCacheTtl-preset') syncGlobalCacheTtlCustomRow(); const content = dialog.querySelector('#global-settings-content'); updateHscToggleCardHints(content, GENERAL_TOGGLE_DESCRIPTIONS); updateHscToggleCardHints(content, DISCOVERY_TOGGLE_DESCRIPTIONS);