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.
| 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) | 1 | import { redirect } from "react-router"; |
| 2 | ||
| 3 | import type { User, WorkspaceInvitation } from "@g1t/contracts"; | |
| 4 | ||
| 5 | import { identity } from "./services.server"; | |
| 6 | import { rememberWorkspace } from "./workspace-choice"; | |
| 7 | import { safeNext } from "./next"; | |
| 8 | ||
| 9 | /** The workspace invitations waiting for `user`'s answer; empty when they cannot be read. */ | |
| 10 | export async function loadInvitations(user: User): Promise<WorkspaceInvitation[]> { | |
| 11 | if (!user.verified) return []; | |
| 12 | return identity.listInvitations(user).catch(() => []); | |
| 13 | } | |
| 14 | ||
| 15 | /** What answering an invitation says back, when it does not move on. */ | |
| 16 | export type InvitationAnswer = { invitationError?: string; invitationId?: string; declined?: string } | undefined; | |
| 17 | ||
| 18 | /** | |
| 19 | * Answers an invitation from a posted form (`intent` accept or decline, | |
| 20 | * `id`). Accepting lands in the workspace joined, made the one the sidebar | |
| 21 | * is about, or in `next` when they were on their way somewhere. Undefined | |
| 22 | * for any other intent. | |
| 23 | */ | |
| 24 | export async function answerInvitation(user: User, form: FormData, request: Request): Promise<InvitationAnswer> { | |
| 25 | const intent = form.get("intent"); | |
| 26 | const id = String(form.get("id") ?? ""); | |
| 27 | if (intent === "accept-invitation") { | |
| 28 | const result = await identity.acceptInvitation(user, id); | |
| 29 | if (!result.ok) return { invitationError: result.error.message, invitationId: id }; | |
| 30 | const secure = new URL(request.url).protocol === "https:"; | |
| 31 | const next = safeNext(String(form.get("next") ?? "")); | |
| 32 | throw redirect(next !== "/" ? next : `/${result.value}`, { | |
| 33 | headers: { "set-cookie": rememberWorkspace(result.value, secure) }, | |
| 34 | }); | |
| 35 | } | |
| 36 | if (intent === "decline-invitation") { | |
| 37 | const result = await identity.declineInvitation(user, id); | |
| 38 | if (!result.ok) return { invitationError: result.error.message, invitationId: id }; | |
| 39 | return { declined: String(form.get("workspace") ?? "") }; | |
| 40 | } | |
| 41 | return undefined; | |
| 42 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.