1// What is happening on the site, for the jev panel's activity tab 2// (ActivityTab.jsx), from GET /jev/data/activity (worker/src/activity.ts): 3// pages of items, newest first, each page from before the last one's 4// oldest. Every field is someone else's writing, shown as text. 5export const ACTIVITY = '/jev/data/activity'; 6export const PAGE = 30; 7 8export async function loadActivity(before = null, fetcher = fetch) { 9 const q = new URLSearchParams({ limit: String(PAGE), ...(before ? { before: String(before) } : {}) }); 10 const res = await fetcher(`${ACTIVITY}?${q}`, { headers: { Accept: 'application/json' } }); 11 if (!res.ok) throw new Error(`the site answered ${res.status}`); 12 const body = await res.json(); 13 return { items: Array.isArray(body.items) ? body.items : [], next: typeof body.next === 'number' ? body.next : null }; 14} 15 16// Pages joined: an item already shown (same kind, song or id, and time) 17// is not shown twice when a page repeats its edge. 18export function appendPage(items, page) { 19 const key = (i) => `${i.kind}:${i.id ?? i.song ?? ''}:${i.rev ?? ''}:${i.at}`; 20 const seen = new Set(items.map(key)); 21 return [...items, ...page.filter((i) => !seen.has(key(i)))]; 22} 23 24const pct = (x) => `${Math.round(x * 100)}%`; 25const VERDICT = { pending: 'waiting on Jev', fine: 'public', spam: 'held by Jev as spam', abuse: 'held by Jev as abuse' }; 26 27// An item in words, beside its author's name (a take's player's): what 28// they did, and Jev's part in it. `title(songId)` names a site song. 29export function describe(item, title = (id) => id) { 30 switch (item.kind) { 31 case 'revision': { 32 const what = item.rev > 1 ? `revised “${item.title}” (rev ${item.rev})` : `published “${item.title}”`; 33 const art = item.art !== null && item.art !== undefined ? `, ${pct(item.art)} art` : ''; 34 return `${what}: ${VERDICT[item.screen] ?? item.screen}${art}`; 35 } 36 case 'scored': 37 return `“${item.title}” (rev ${item.rev}) scored ${pct(item.art)} art by Jev`; 38 case 'comment': { 39 const on = item.on?.site ? title(item.on.site) : `“${item.on?.title ?? 'a listener’s song'}”`; 40 return item.body === null ? `commented on ${on}: ${VERDICT[item.screen] ?? item.screen}` : `commented on ${on}`; 41 } 42 case 'pitch': 43 return item.body === null ? `pitched a song: ${VERDICT[item.screen] ?? item.screen}` : 'pitched a song'; 44 case 'take': { 45 const song = item.song.startsWith('listener:') ? `“${item.title ?? 'a listener’s song'}”` : title(item.song); 46 const ended = item.ended === 'finished' ? 'to its end' : 'and stopped'; 47 return `played ${song}, ${item.sections} ${item.sections === 1 ? 'section' : 'sections'} ${ended}`; 48 } 49 default: 50 return ''; 51 } 52}