Skip to main content

max / goingson

3.0 KB · 88 lines History Blame Raw
1 /**
2 * GoingsOn - Groups (shared-workspace) client cache + badge helpers.
3 *
4 * A thin, lazily-populated cache of the groups you belong to, keyed by id, so
5 * the UI can resolve a project's `groupId` to a human name without a round-trip
6 * per render. Sharing is whole-project, so a task's share state is its project's
7 * share state; the badge helpers reflect that.
8 *
9 * The admin/list commands themselves live on `GoingsOn.api.groups`; this module
10 * is only the render-side cache. Invalidate it whenever group membership changes
11 * (a group is created, joined, or renamed).
12 */
13
14 (function() {
15 'use strict';
16 const esc = GoingsOn.utils.escapeHtml;
17 const escAttr = GoingsOn.utils.escapeAttrValue;
18
19 let cache = null; // Array of GroupDto, or null when unloaded.
20 let byId = {}; // id -> GroupDto.
21
22 /**
23 * Load the group list into the cache if it is not already loaded. Non-fatal:
24 * if sync is not configured the cache stays null and names do not
25 * resolve (badges still render as a bare "Shared").
26 * @returns {Promise<Array>} the cached groups (possibly empty)
27 */
28 async function ensureLoaded() {
29 if (cache) return cache;
30 try {
31 const list = await GoingsOn.api.groups.list();
32 cache = list;
33 byId = {};
34 list.forEach(g => { byId[g.id] = g; });
35 } catch (_) {
36 // Leave cache null so a later call retries once sync is ready.
37 }
38 return cache || [];
39 }
40
41 /** Group name for an id, or null when unknown / cache not loaded. */
42 function nameFor(groupId) {
43 if (!groupId) return null;
44 const g = byId[groupId];
45 return g ? g.name : null;
46 }
47
48 /** Drop the cache so the next ensureLoaded refetches. */
49 function invalidate() {
50 cache = null;
51 byId = {};
52 }
53
54 /**
55 * Badge HTML for a project's card meta, or '' when the project is personal.
56 * Shows the group name when known, otherwise a bare "Shared".
57 */
58 function projectCardBadge(project) {
59 if (!project || !project.groupId) return '';
60 const name = nameFor(project.groupId);
61 const label = name ? `Shared · ${name}` : 'Shared';
62 return `<span class="tag tag-shared" title="${escAttr(label)}">${esc(label)}</span>`;
63 }
64
65 /**
66 * Small inline marker for a task row whose project is shared, resolved from
67 * the projects cache (best-effort; no badge if projects are not loaded).
68 */
69 function taskSharedBadge(projectId) {
70 if (!projectId) return '';
71 const project = GoingsOn.getProjectsCache().find(p => p.id === projectId);
72 if (!project || !project.groupId) return '';
73 const name = nameFor(project.groupId);
74 const label = name ? `Shared · ${name}` : 'Shared';
75 return `<span class="shared-badge" title="${escAttr(label)}">Shared</span>`;
76 }
77
78 GoingsOn.groups = {
79 ensureLoaded,
80 nameFor,
81 invalidate,
82 projectCardBadge,
83 taskSharedBadge,
84 list: () => cache || [],
85 };
86
87 })();
88