Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
30 linesCodeBlameRaw
1/**
2 * What the account menu shows under the avatar, fetched when it opens so
3 * no page waits on it: the name people see and the primary address. Each
4 * part is optional: a service that cannot answer leaves its part out.
5 */
6import type { Route } from "./+types/settings-menu-json";
7import { accounts, identity } from "../lib/services.server";
8import { getViewer } from "../lib/session.server";
9
10export type AccountMenuData = {
11 name: string | null;
12 email: string | null;
13};
14
15export async function loader({ context }: Route.LoaderArgs) {
16 const user = getViewer(context);
17 if (!user) return Response.json(null, { status: 401, headers: { "cache-control": "private, no-store" } });
18 const [profile, emails] = await Promise.all([
19 identity.profile(user.username).catch(() => null),
20 accounts.listEmails(user).catch(() => null),
21 ]);
22 const primary = emails?.ok ? (emails.value.emails.find((e) => e.primary)?.email ?? null) : null;
23 const data: AccountMenuData = {
24 name: profile?.name?.trim() || null,
25 email: primary,
26 };
27 // Not kept by the browser: the menu asks again after a name or address
28 // changes, and a kept copy would show the old one for a minute.
29 return Response.json(data, { headers: { "cache-control": "private, no-store" } });
30}