Spaces:
Running
Running
| /* ββ localStorage cache ββ */ | |
| const CACHE_PREFIX = 'nma_v1_'; | |
| const CACHE_TTL = 60 * 60 * 1000; // 1 hour | |
| function saveCache(key, prs) { | |
| try { | |
| const slim = prs.map(pr => ({ | |
| number: pr.number, title: pr.title, html_url: pr.html_url, | |
| merged_at: getMerged(pr), | |
| })); | |
| localStorage.setItem(CACHE_PREFIX + key, JSON.stringify({ ts: Date.now(), prs: slim })); | |
| } catch (e) { /* quota exceeded or unavailable */ } | |
| } | |
| function loadCache(key) { | |
| try { | |
| const raw = localStorage.getItem(CACHE_PREFIX + key); | |
| if (!raw) return null; | |
| const parsed = JSON.parse(raw); | |
| if (Date.now() - parsed.ts > CACHE_TTL) return null; | |
| return parsed; | |
| } catch { return null; } | |
| } | |
| function getCacheTs(key) { | |
| try { | |
| const raw = localStorage.getItem(CACHE_PREFIX + key); | |
| if (!raw) return null; | |
| return JSON.parse(raw).ts; | |
| } catch { return null; } | |
| } | |
| function clearAllCaches() { | |
| try { | |
| for (const k of Object.keys(localStorage)) { | |
| if (k.startsWith(CACHE_PREFIX)) localStorage.removeItem(k); | |
| } | |
| } catch {} | |
| } | |
| function fmtRelative(ts) { | |
| if (!ts) return ''; | |
| const sec = Math.round((Date.now() - ts) / 1000); | |
| if (sec < 60) return 'just now'; | |
| const min = Math.floor(sec / 60); | |
| if (min < 60) return `${min}m ago`; | |
| const hr = Math.floor(min / 60); | |
| return `${hr}h ${min % 60}m ago`; | |
| } | |