/** * PreviewInvitationStep — displays the current state of a fetched invitation. * * Shows which roles, inputs, outputs, and variables have already been filled * so the user can understand what they're joining before proceeding. * Press Enter to continue, Esc to cancel. */ import React from 'react'; import { Box, Text } from 'ink'; import { colors, formatSatoshis } from '../../../../theme.js'; import { useSatoshisConversion } from '../../../../hooks/useSatoshisConversion.js'; import { useLayeredInput } from '../../../../hooks/useInputLayer.js'; import { getInvitationState, getStateColorName, } from '../../../../../utils/invitation-utils.js'; import type { PreviewStepProps } from '../types.js'; /** * Map a semantic color name to an actual theme color value. */ function parseNumberishToBigInt(value: unknown): bigint | null { if (typeof value === 'bigint') { return value; } const asString = String(value).trim(); if (!/^[-]?\d+$/.test(asString)) { return null; } try { return BigInt(asString); } catch { return null; } } /** * Map a semantic color name to an actual theme color value. */ function stateColor(state: string): string { const name = getStateColorName(state); switch (name) { case 'info': return colors.info as string; case 'warning': return colors.warning as string; case 'success': return colors.success as string; case 'error': return colors.error as string; case 'muted': default: return colors.textMuted as string; } } export function PreviewInvitationStep({ invitation, template, onComplete, onCancel, isActive, }: PreviewStepProps): React.ReactElement { const { formatSatoshisToFiat } = useSatoshisConversion(); useLayeredInput('import-flow', (_input, key) => { if (key.return) onComplete(); if (key.escape) onCancel(); }, { isActive }); const state = getInvitationState(invitation); const action = template?.actions?.[invitation.data.actionIdentifier]; const { inputs, outputs, variables } = invitation.resolvedData; // Collect role identifiers that appear across resolved invitation data const filledRoles = new Set(); for (const input of inputs) { if (input.roleIdentifier) filledRoles.add(input.roleIdentifier); } for (const output of outputs) { if (output.roleIdentifier) filledRoles.add(output.roleIdentifier); } for (const variable of variables) { if (variable.roleIdentifier) filledRoles.add(variable.roleIdentifier); } return ( {/* Template info */} Template: {template?.name ?? invitation.data.templateIdentifier} {template?.description && ( {template.description} )} {/* Action info */} Action: {action?.name ?? invitation.data.actionIdentifier} {action?.description && ( {action.description} )} {/* Status */} Status: {state} {/* Roles already filled */} Roles Filled ({filledRoles.size}): {filledRoles.size === 0 ? ( None yet ) : ( Array.from(filledRoles).map(role => { const roleInfoRaw = template?.roles?.[role]; const roleInfo = roleInfoRaw && typeof roleInfoRaw === 'object' ? roleInfoRaw : null; return ( • {roleInfo?.name ?? role} ); }) )} {/* Inputs */} Inputs ({inputs.length}): {inputs.length === 0 ? ( None yet ) : ( inputs.map((input, idx) => { return ( {' '}• {input.name ?? input.inputIdentifier ?? `Input ${idx}`} {input.roleIdentifier && ` (${input.roleIdentifier})`} ); }) )} {/* Outputs */} Outputs ({outputs.length}): {outputs.length === 0 ? ( None yet ) : ( outputs.map((output, idx) => { const fiatValue = output.valueSatoshis !== undefined ? formatSatoshisToFiat(output.valueSatoshis) : null; const outputSatoshis = output.valueSatoshis !== undefined ? parseNumberishToBigInt(output.valueSatoshis) : null; return ( {' '}• {output.name ?? output.outputIdentifier ?? `Output ${idx}`} {outputSatoshis !== null && ` (${formatSatoshis(outputSatoshis)})`} {fiatValue && ` (~${fiatValue})`} ); }) )} {/* Variables */} Variables ({variables.length}): {variables.length === 0 ? ( None set ) : ( variables.map((variable, idx) => { const displayValue = typeof variable.value === 'bigint' ? variable.value.toString() : String(variable.value); return ( {' '}• {variable.name ?? variable.variableIdentifier}: {displayValue} ); }) )} {/* Navigation hint */} Enter: Continue • Esc: Cancel ); }