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
56 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor1import type { Invite, InvitesOverview, User } from "@g1t/contracts";
2
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)3import { type BringInto, bringIntoChoices, inviteDraft, peoplePages } from "./invites";
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)4import { readCookie } from "./mission";
5import { billing, identity } from "./services.server";
6import { WORKSPACE_COOKIE, chosenWorkspace } from "./workspace-choice";
The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor7
8/** What the Invites section's forms answer. */
9export type InviteActionData = { inviteCreated?: Invite; inviteError?: string; inviteRevoked?: boolean } | null;
10
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)11/**
12 * The workspaces an invite to g1t can also invite its person to
13 * (`bringIntoChoices`), and the People pages where a workspace invitation
14 * is made instead (`peoplePages`).
15 */
16export type BringIntoChoices = {
17 options: BringInto[];
18 note: string | null;
19 people: { slug: string; name: string; to: string }[];
20};
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)21
The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor22/** A person's invites, or null when they cannot make any yet (email unconfirmed). */
23export async function loadInvites(user: User): Promise<InvitesOverview | null> {
24 if (!user.verified) return null;
25 return identity.listInvites(user).catch(() => null);
26}
27
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)28/**
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)29 * The workspaces an invite can also invite its person to, and the People
30 * pages to invite someone to a workspace from, the one the person is in
31 * (the shell's current workspace, from its cookie) first. A workspace
32 * whose plan cannot be checked is not offered: identity would refuse it.
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)33 */
34export async function loadBringInto(user: User, request: Request): Promise<BringIntoChoices> {
35 const memberships = user.workspaces ?? [];
36 const owned = memberships.filter((m) => m.role === "owner").map((m) => m.slug);
37 const free = owned.length > 0 ? await billing.freeWorkspaces(owned).catch(() => owned) : [];
38 const current = chosenWorkspace(memberships, readCookie(request.headers.get("cookie"), WORKSPACE_COOKIE))?.slug ?? null;
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)39 return { ...bringIntoChoices(memberships, free, current), people: peoplePages(memberships, current) };
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)40}
41
The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor42/** Handles the Invites section's intents; null for any other intent. */
43export async function inviteAction(user: User, form: FormData): Promise<InviteActionData | undefined> {
44 switch (form.get("intent")) {
45 case "create-invite": {
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)46 // A workspace goes with it only when its box is ticked (`inviteDraft`).
47 const result = await identity.createInvite(user, inviteDraft(form));
The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor48 return result.ok ? { inviteCreated: result.value } : { inviteError: result.error.message };
49 }
50 case "revoke-invite": {
51 const result = await identity.revokeInvite(user, String(form.get("id") ?? ""));
52 return result.ok ? { inviteRevoked: true } : { inviteError: result.error.message };
53 }
54 }
55 return undefined;
56}

This file's history is long; its oldest lines are credited to the oldest commit read.