// State shared between views: who is signed in, the user list, and the open // queue that drives the badges. import * as api from './api.js'; export const state = { me: null, // { user, has_password } // How the server can be signed in to. The defaults are an older server's // answer: passwords, no single sign-on. auth: { password_login: true, oidc: { enabled: false, name: '' } }, open: [], // the default queue: open, not snoozed teams: [], // the teams the viewer belongs to, each with their role // Which team the whole app is scoped to right now; null means "All teams". // Set only through setSelectedTeam below, never assigned directly, so every // view stays in sync and the choice is remembered across reloads. selectedTeamID: loadSelectedTeam(), }; const SELECTED_TEAM_KEY = 'terdut.selectedTeam'; function loadSelectedTeam() { try { const raw = localStorage.getItem(SELECTED_TEAM_KEY); return raw ? Number(raw) : null; } catch { return null; // storage unavailable, or nothing saved yet } } // Callbacks to run whenever the selected team changes, so every view that // cares — the queue's filter, the Team settings page, the selector's own // trigger — stays in sync without a general event bus, following the one // precedent for this in the codebase: onboarding.js's onRerender. const teamListeners = []; export function onTeamChange(cb) { teamListeners.push(cb); } // setSelectedTeam changes which team the app is scoped to (id, or null for // "All teams"), persists it — a durable preference, unlike the per-tab // sessionStorage filter this replaces — and tells every registered listener. export function setSelectedTeam(id) { state.selectedTeamID = id; try { if (id == null) localStorage.removeItem(SELECTED_TEAM_KEY); else localStorage.setItem(SELECTED_TEAM_KEY, String(id)); } catch { /* storage unavailable */ } for (const cb of teamListeners) cb(); } // The team whose schedule and settings the views act on: the selected team, // falling back to the first one the viewer belongs to — which is everybody's // only team until somebody makes a second, or the stored selection naming a // team the account has since left. export function currentTeam() { const teams = state.teams || []; return teams.find((t) => t.id === state.selectedTeamID) || teams[0] || null; } export function myID() { return state.me ? state.me.user.id : null; } // The user list changes rarely; it is fetched once and then at most every // five minutes, for the assign sheet and for display names. let usersCache = null; let usersAt = 0; export async function users() { if (!usersCache || Date.now() - usersAt > 5 * 60 * 1000) { usersCache = await api.users(); usersAt = Date.now(); } return usersCache; } export async function loadTeams() { state.teams = await api.teams(); // A stored id that no longer names one of the account's teams — left it, or // this is simply a different account signed in on the same browser — is as // good as unset. if (state.selectedTeamID != null && !state.teams.some((t) => t.id === state.selectedTeamID)) { state.selectedTeamID = null; } return state.teams; } export function reset() { state.me = null; state.open = []; state.teams = []; usersCache = null; }