Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,160 @@
|
||||
// Generated, do not edit. sha256:08b414b8e046583bdfdf9c3302134a9e220a7c451265f464b9ed56dca2d3d460
|
||||
import { getPref, setPref } from '../data/preferences.js';
|
||||
import { CONFIG, targetHash } from '../../shared/config.js';
|
||||
import { esc } from './escape.js';
|
||||
|
||||
const producers = [];
|
||||
let cache = [];
|
||||
let cacheKey = '';
|
||||
|
||||
const PREF_SCOPE = 'attention';
|
||||
const PREF_ACT = 'activated';
|
||||
const PREF_SEEN = 'seen';
|
||||
const PREF_DAY = 'day';
|
||||
const activated = new Map();
|
||||
const seen = new Set();
|
||||
let loaded = false;
|
||||
let dayN = 0;
|
||||
const stateKey = a => `${a.id}|${a.severity || 'attention'}`;
|
||||
|
||||
function todayStr() {
|
||||
const d = new Date();
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
async function ensureLoaded() {
|
||||
if (loaded) return;
|
||||
loaded = true;
|
||||
try {
|
||||
const act = await getPref(PREF_SCOPE, PREF_ACT, {});
|
||||
if (act && typeof act === 'object' && !Array.isArray(act)) {
|
||||
for (const [id, s] of Object.entries(act)) activated.set(String(id), Number(s) || 0);
|
||||
}
|
||||
} catch { }
|
||||
try {
|
||||
const s = await getPref(PREF_SCOPE, PREF_SEEN, []);
|
||||
if (Array.isArray(s)) for (const k of s) seen.add(String(k));
|
||||
} catch { }
|
||||
let day = { date: '', n: 0 };
|
||||
try { const d = await getPref(PREF_SCOPE, PREF_DAY, null); if (d && typeof d === 'object') day = d; } catch { }
|
||||
const today = todayStr();
|
||||
if (day.date !== today) { day = { date: today, n: (Number(day.n) || 0) + 1 }; saveDay(day); }
|
||||
dayN = Number(day.n) || 1;
|
||||
}
|
||||
async function saveDay(day) { try { await setPref(PREF_SCOPE, PREF_DAY, day); } catch { } }
|
||||
async function saveActivated() {
|
||||
try { await setPref(PREF_SCOPE, PREF_ACT, Object.fromEntries(activated)); } catch { }
|
||||
}
|
||||
async function saveSeen() {
|
||||
try { await setPref(PREF_SCOPE, PREF_SEEN, [...seen]); } catch { }
|
||||
}
|
||||
|
||||
function fadeFor(a) {
|
||||
const k = stateKey(a);
|
||||
if (!activated.has(k)) return 'hidden';
|
||||
const age = dayN - activated.get(k);
|
||||
if (age >= CONFIG.ATTENTION.HIDE_AFTER_DAYS) return 'hidden';
|
||||
if (age >= CONFIG.ATTENTION.FADE_AFTER_DAYS || seen.has(k)) return 'dim';
|
||||
return 'full';
|
||||
}
|
||||
|
||||
function currentRouteKey() {
|
||||
const h = (typeof location !== 'undefined' ? location.hash : '') || '';
|
||||
return h.replace(/^#\/?/, '') || 'home';
|
||||
}
|
||||
|
||||
function opDezePlek(a, here) {
|
||||
const hereHead = here.split('/')[0];
|
||||
if (!a.tab) return a.route === here || a.route === hereHead;
|
||||
const plek = `${a.route}/${a.tab}`;
|
||||
return here === plek || here.startsWith(`${plek}/`);
|
||||
}
|
||||
|
||||
export function registerAttention(producer) {
|
||||
if (typeof producer === 'function') producers.push(producer);
|
||||
return () => {
|
||||
const i = producers.indexOf(producer);
|
||||
if (i >= 0) producers.splice(i, 1);
|
||||
};
|
||||
}
|
||||
|
||||
export async function refreshAttention() {
|
||||
const collected = [];
|
||||
for (const p of producers) {
|
||||
try {
|
||||
const items = await p();
|
||||
if (Array.isArray(items)) collected.push(...items.filter(Boolean));
|
||||
} catch { }
|
||||
}
|
||||
const norm = collected.map(a => ({
|
||||
id: String(a.id),
|
||||
severity: a.severity || 'attention',
|
||||
route: a.route ?? null,
|
||||
tab: a.tab ?? null,
|
||||
hash: a.hash || (a.route ? targetHash({ route: a.route }) : null),
|
||||
label: a.label || '',
|
||||
count: Number.isFinite(a.count) ? a.count : null,
|
||||
})).sort((x, y) => (x.route || '').localeCompare(y.route || '') || x.id.localeCompare(y.id));
|
||||
|
||||
cache = norm;
|
||||
|
||||
await ensureLoaded();
|
||||
const here = currentRouteKey();
|
||||
const liveKeys = new Set(norm.map(stateKey));
|
||||
let activatedChanged = false, seenChanged = false;
|
||||
for (const a of norm) {
|
||||
const k = stateKey(a);
|
||||
if (activated.has(k)) {
|
||||
if (a.route !== null && opDezePlek(a, here) && !seen.has(k)) { seen.add(k); seenChanged = true; }
|
||||
continue;
|
||||
}
|
||||
if (a.route === null || !opDezePlek(a, here)) {
|
||||
activated.set(k, dayN); activatedChanged = true;
|
||||
}
|
||||
}
|
||||
for (const k of [...activated.keys()]) {
|
||||
if (!liveKeys.has(k)) { activated.delete(k); activatedChanged = true; }
|
||||
}
|
||||
for (const k of [...seen]) {
|
||||
if (!liveKeys.has(k)) { seen.delete(k); seenChanged = true; }
|
||||
}
|
||||
if (activatedChanged) saveActivated();
|
||||
if (seenChanged) saveSeen();
|
||||
|
||||
const actKey = norm.filter(a => activated.has(stateKey(a))).map(a => `${stateKey(a)}:${fadeFor(a)}`).sort().join(',');
|
||||
const key = JSON.stringify(norm) + '|' + actKey;
|
||||
const changed = key !== cacheKey;
|
||||
cacheKey = key;
|
||||
if (changed && typeof document !== 'undefined' && typeof document.dispatchEvent === 'function') {
|
||||
document.dispatchEvent(new CustomEvent('attention-changed'));
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
export function attentionItems() { return cache; }
|
||||
|
||||
export function generalAttention() {
|
||||
return cache.filter(a => a.route === null).map(a => ({ ...a, fade: fadeFor(a) })).filter(a => a.fade !== 'hidden');
|
||||
}
|
||||
|
||||
export function attentionForRoute(route) {
|
||||
return cache.filter(a => a.route === route)
|
||||
.map(a => ({ ...a, fade: fadeFor(a) })).filter(a => a.fade !== 'hidden');
|
||||
}
|
||||
export function hasAttentionForRoute(route) { return attentionForRoute(route).length > 0; }
|
||||
|
||||
export function attentionHasProblem(items) {
|
||||
return (items || []).some(a => a.severity === 'problem');
|
||||
}
|
||||
|
||||
export function attentionDimmed(items) {
|
||||
return !!(items && items.length) && items.every(a => a.fade === 'dim');
|
||||
}
|
||||
|
||||
export function attentionDotHtml(items) {
|
||||
if (!items || !items.length) return '';
|
||||
const hint = items.map(a => a.label).filter(Boolean).join(' \u00b7 ');
|
||||
const dim = attentionDimmed(items);
|
||||
const stuk = attentionHasProblem(items);
|
||||
return ` <span class="nav-attention${stuk ? ' is-problem' : ''}${dim ? ' is-dim' : ''}" title="${esc(hint)}" aria-label="${esc(hint)}"></span>`;
|
||||
}
|
||||
Reference in new issue
Block a user