276 lines
7.4 KiB
TypeScript
276 lines
7.4 KiB
TypeScript
/**
|
|
* Invitation utility functions.
|
|
*
|
|
* Pure functions for parsing and formatting invitation data.
|
|
* These functions have no React dependencies and can be used
|
|
* in both TUI and CLI contexts.
|
|
*/
|
|
|
|
import type { Invitation } from "../services/invitation.js";
|
|
import type { XOTemplate } from "@xo-cash/types";
|
|
|
|
/**
|
|
* Color names for invitation states.
|
|
* These are semantic color names that can be mapped to actual colors
|
|
* by the consuming application (TUI or CLI).
|
|
*/
|
|
export type StateColorName = "info" | "warning" | "success" | "error" | "muted";
|
|
|
|
/**
|
|
* Input data extracted from invitation commits.
|
|
*/
|
|
export interface InvitationInput {
|
|
inputIdentifier?: string;
|
|
roleIdentifier?: string;
|
|
entityIdentifier: string;
|
|
}
|
|
|
|
/**
|
|
* Output data extracted from invitation commits.
|
|
*/
|
|
export interface InvitationOutput {
|
|
outputIdentifier?: string;
|
|
roleIdentifier?: string;
|
|
valueSatoshis?: bigint;
|
|
entityIdentifier: string;
|
|
}
|
|
|
|
/**
|
|
* Variable data extracted from invitation commits.
|
|
*/
|
|
export interface InvitationVariable {
|
|
variableIdentifier: string;
|
|
value: unknown;
|
|
roleIdentifier?: string;
|
|
entityIdentifier: string;
|
|
}
|
|
|
|
/**
|
|
* Formatted invitation list item data.
|
|
*/
|
|
export interface FormattedInvitationItem {
|
|
/** The display label for the invitation */
|
|
label: string;
|
|
/** The current status of the invitation */
|
|
status: string;
|
|
/** The semantic color name for the status */
|
|
statusColor: StateColorName;
|
|
/** Whether the invitation data is valid */
|
|
isValid: boolean;
|
|
}
|
|
|
|
/**
|
|
* Get the current state/status of an invitation.
|
|
*
|
|
* @param invitation - The invitation to get state for
|
|
* @returns The status string
|
|
*/
|
|
export function getInvitationState(invitation: Invitation): string {
|
|
return invitation.status;
|
|
}
|
|
|
|
/**
|
|
* Get the semantic color name for an invitation state.
|
|
*
|
|
* @param state - The invitation state string
|
|
* @returns A semantic color name
|
|
*/
|
|
export function getStateColorName(state: string): StateColorName {
|
|
switch (state) {
|
|
case "created":
|
|
case "published":
|
|
return "info";
|
|
case "pending":
|
|
return "warning";
|
|
case "ready":
|
|
case "signed":
|
|
case "complete":
|
|
case "broadcast":
|
|
case "completed":
|
|
return "success";
|
|
case "expired":
|
|
case "error":
|
|
return "error";
|
|
default:
|
|
return "muted";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract all inputs from invitation commits.
|
|
*
|
|
* @param invitation - The invitation to extract inputs from
|
|
* @returns Array of input data
|
|
*/
|
|
export function getInvitationInputs(invitation: Invitation): InvitationInput[] {
|
|
const inputs: InvitationInput[] = [];
|
|
for (const commit of invitation.data.commits || []) {
|
|
for (const input of commit.data?.inputs || []) {
|
|
inputs.push({
|
|
inputIdentifier: input.inputIdentifier,
|
|
roleIdentifier: input.roleIdentifier,
|
|
entityIdentifier: commit.entityIdentifier,
|
|
});
|
|
}
|
|
}
|
|
return inputs;
|
|
}
|
|
|
|
/**
|
|
* Extract all outputs from invitation commits.
|
|
*
|
|
* @param invitation - The invitation to extract outputs from
|
|
* @returns Array of output data
|
|
*/
|
|
export function getInvitationOutputs(
|
|
invitation: Invitation,
|
|
): InvitationOutput[] {
|
|
const outputs: InvitationOutput[] = [];
|
|
for (const commit of invitation.data.commits || []) {
|
|
for (const output of commit.data?.outputs || []) {
|
|
outputs.push({
|
|
outputIdentifier: output.outputIdentifier,
|
|
roleIdentifier: output.roleIdentifier,
|
|
valueSatoshis: output.valueSatoshis,
|
|
entityIdentifier: commit.entityIdentifier,
|
|
});
|
|
}
|
|
}
|
|
return outputs;
|
|
}
|
|
|
|
/**
|
|
* Extract all variables from invitation commits.
|
|
*
|
|
* @param invitation - The invitation to extract variables from
|
|
* @returns Array of variable data
|
|
*/
|
|
export function getInvitationVariables(
|
|
invitation: Invitation,
|
|
): InvitationVariable[] {
|
|
const variables: InvitationVariable[] = [];
|
|
for (const commit of invitation.data.commits || []) {
|
|
for (const variable of commit.data?.variables || []) {
|
|
variables.push({
|
|
variableIdentifier: variable.variableIdentifier,
|
|
value: variable.value,
|
|
roleIdentifier: variable.roleIdentifier,
|
|
entityIdentifier: commit.entityIdentifier,
|
|
});
|
|
}
|
|
}
|
|
return variables;
|
|
}
|
|
|
|
/**
|
|
* Get the user's role from commits (the role they have accepted).
|
|
*
|
|
* @param invitation - The invitation to check
|
|
* @param userEntityId - The user's entity identifier
|
|
* @returns The role identifier if found, null otherwise
|
|
*/
|
|
export function getUserRole(
|
|
invitation: Invitation,
|
|
userEntityId: string | null,
|
|
): string | null {
|
|
if (!userEntityId) return null;
|
|
|
|
for (const commit of invitation.data.commits || []) {
|
|
if (commit.entityIdentifier === userEntityId) {
|
|
// Check inputs for role
|
|
for (const input of commit.data?.inputs || []) {
|
|
if (input.roleIdentifier) return input.roleIdentifier;
|
|
}
|
|
// Check outputs for role
|
|
for (const output of commit.data?.outputs || []) {
|
|
if (output.roleIdentifier) return output.roleIdentifier;
|
|
}
|
|
// Check variables for role
|
|
for (const variable of commit.data?.variables || []) {
|
|
if (variable.roleIdentifier) return variable.roleIdentifier;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Format an invitation for display in a list.
|
|
*
|
|
* @param invitation - The invitation to format
|
|
* @param template - Optional template for additional info (name)
|
|
* @returns Formatted item data for display
|
|
*/
|
|
export function formatInvitationListItem(
|
|
invitation: Invitation,
|
|
template?: XOTemplate | null,
|
|
): FormattedInvitationItem {
|
|
// Validate that we have the minimum required data
|
|
const invitationId = invitation?.data?.invitationIdentifier;
|
|
const actionId = invitation?.data?.actionIdentifier;
|
|
|
|
if (!invitationId || !actionId) {
|
|
return {
|
|
label: "",
|
|
status: "error",
|
|
statusColor: "error",
|
|
isValid: false,
|
|
};
|
|
}
|
|
|
|
const state = getInvitationState(invitation);
|
|
const templateName = template?.name ?? "Unknown";
|
|
const shortId = formatInvitationId(invitationId, 8);
|
|
|
|
return {
|
|
label: `[${state}] ${templateName}-${actionId} (${shortId})`,
|
|
status: state,
|
|
statusColor: getStateColorName(state),
|
|
isValid: true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Format an invitation ID for display (truncated).
|
|
*
|
|
* @param id - The full invitation ID
|
|
* @param maxLength - Maximum length for display
|
|
* @returns Truncated ID string
|
|
*/
|
|
export function formatInvitationId(id: string, maxLength: number = 16): string {
|
|
if (id.length <= maxLength) return id;
|
|
const half = Math.floor((maxLength - 3) / 2);
|
|
return `${id.slice(0, half)}...${id.slice(-half)}`;
|
|
}
|
|
|
|
/**
|
|
* Get all unique entity identifiers from an invitation's commits.
|
|
*
|
|
* @param invitation - The invitation to check
|
|
* @returns Array of unique entity identifiers
|
|
*/
|
|
export function getInvitationParticipants(invitation: Invitation): string[] {
|
|
const participants = new Set<string>();
|
|
for (const commit of invitation.data.commits || []) {
|
|
if (commit.entityIdentifier) {
|
|
participants.add(commit.entityIdentifier);
|
|
}
|
|
}
|
|
return Array.from(participants);
|
|
}
|
|
|
|
/**
|
|
* Check if a user is a participant in an invitation.
|
|
*
|
|
* @param invitation - The invitation to check
|
|
* @param userEntityId - The user's entity identifier
|
|
* @returns True if the user has made at least one commit
|
|
*/
|
|
export function isUserParticipant(
|
|
invitation: Invitation,
|
|
userEntityId: string | null,
|
|
): boolean {
|
|
if (!userEntityId) return false;
|
|
return getInvitationParticipants(invitation).includes(userEntityId);
|
|
}
|