wakatime.js (2378B)
1 const WAKATIME_TTL_MS = 60 * 60 * 1000; 2 3 const wakatimeCache = { 4 data: null, 5 timestamp: 0, 6 pending: null, 7 }; 8 const wakatimeListeners = new Set(); 9 let wakatimeRefreshTimer = 0; 10 let wakatimeRefreshing = null; 11 12 export async function getWakaTimeData(force = false) { 13 if (!force && wakatimeCache.data && isFresh(wakatimeCache.timestamp)) { 14 return wakatimeCache.data; 15 } 16 17 if (wakatimeCache.pending) { 18 return wakatimeCache.pending; 19 } 20 21 wakatimeCache.pending = fetch("/api/wakatime", { credentials: "same-origin" }) 22 .then((res) => (res.ok ? res.json() : null)) 23 .then((data) => { 24 if (data) { 25 wakatimeCache.data = data; 26 wakatimeCache.timestamp = Date.now(); 27 } 28 // a failed or empty fetch (offline right after wake, a 5xx) keeps the 29 // last good value instead of wiping it 30 return data || wakatimeCache.data; 31 }) 32 .catch(() => wakatimeCache.data) 33 .finally(() => { 34 wakatimeCache.pending = null; 35 }); 36 37 return wakatimeCache.pending; 38 } 39 40 export function subscribeWakaTimeRefresh(listener) { 41 if (typeof listener !== "function") { 42 return () => {}; 43 } 44 45 wakatimeListeners.add(listener); 46 return () => { 47 wakatimeListeners.delete(listener); 48 }; 49 } 50 51 export function startWakaTimeRefreshTimer() { 52 if (wakatimeRefreshTimer) return; 53 54 wakatimeRefreshTimer = window.setInterval(() => { 55 void refreshWakaTimeData(); 56 }, WAKATIME_TTL_MS); 57 58 // timers are throttled or frozen while the tab is hidden or the laptop 59 // sleeps; catch up as soon as it is visible and online again 60 const catchUp = () => { 61 if (document.visibilityState !== "visible") return; 62 if (isFresh(wakatimeCache.timestamp)) return; 63 void refreshWakaTimeData(); 64 }; 65 document.addEventListener("visibilitychange", catchUp); 66 window.addEventListener("online", catchUp); 67 window.addEventListener("focus", catchUp); 68 } 69 70 function isFresh(timestamp) { 71 return Date.now() - timestamp < WAKATIME_TTL_MS; 72 } 73 74 async function refreshWakaTimeData() { 75 if (wakatimeRefreshing) { 76 return wakatimeRefreshing; 77 } 78 79 wakatimeRefreshing = getWakaTimeData(true) 80 .then((data) => { 81 for (const listener of wakatimeListeners) { 82 try { 83 listener(data); 84 } catch {} 85 } 86 87 return data; 88 }) 89 .finally(() => { 90 wakatimeRefreshing = null; 91 }); 92 93 return wakatimeRefreshing; 94 }