Add resolveCommitReferences method
This commit is contained in:
@@ -26,12 +26,10 @@ import type { XOInvitationCommit, XOInvitationVariableValue, XOTemplate } from '
|
||||
import {
|
||||
getInvitationState,
|
||||
getStateColorName,
|
||||
getInvitationInputs,
|
||||
getInvitationOutputs,
|
||||
getInvitationVariables,
|
||||
formatInvitationListItem,
|
||||
formatInvitationId,
|
||||
} from '../../../utils/invitation-utils.js';
|
||||
import type { ResolvedInvitationVariable } from '../../../utils/resolve-invitation-data.js';
|
||||
|
||||
import { InvitationImportFlow } from './invitation-import/InvitationImportFlow.js';
|
||||
import { compileCashAssemblyString } from '@xo-cash/engine';
|
||||
@@ -401,16 +399,11 @@ export function InvitationScreen(): React.ReactElement {
|
||||
setStatus('Analyzing invitation...');
|
||||
|
||||
let requiredAmount = 0n;
|
||||
const commits = selectedInvitation.data.commits || [];
|
||||
for (const commit of commits) {
|
||||
const variables = commit.data?.variables || [];
|
||||
for (const variable of variables) {
|
||||
if (variable.variableIdentifier?.toLowerCase().includes('satoshi')) {
|
||||
requiredAmount = BigInt(variable.value?.toString() || '0');
|
||||
break;
|
||||
}
|
||||
for (const variable of selectedInvitation.resolvedData.variables) {
|
||||
if (variable.variableIdentifier.toLowerCase().includes('satoshi')) {
|
||||
requiredAmount = BigInt(variable.value?.toString() || '0');
|
||||
break;
|
||||
}
|
||||
if (requiredAmount > 0n) break;
|
||||
}
|
||||
|
||||
const fee = 500n;
|
||||
@@ -595,14 +588,17 @@ export function InvitationScreen(): React.ReactElement {
|
||||
|
||||
const state = getInvitationState(selectedInvitation);
|
||||
const action = selectedTemplate?.actions?.[selectedInvitation.data.actionIdentifier];
|
||||
const inputs = getInvitationInputs(selectedInvitation);
|
||||
const outputs = getInvitationOutputs(selectedInvitation);
|
||||
const variables = getInvitationVariables(selectedInvitation);
|
||||
const { inputs, outputs, variables } = selectedInvitation.resolvedData;
|
||||
const userEntityId = ownInvitationContext.entityIdentifier;
|
||||
const userRole = ownInvitationContext.roleIdentifier;
|
||||
const roleInfoRaw = userRole && selectedTemplate?.roles?.[userRole];
|
||||
const roleInfo = roleInfoRaw && typeof roleInfoRaw === 'object' ? roleInfoRaw : null;
|
||||
|
||||
const variableValues = variables.reduce((acc, variable) => {
|
||||
acc[variable.variableIdentifier] = variable.value as XOInvitationVariableValue;
|
||||
return acc;
|
||||
}, {} as Record<string, XOInvitationVariableValue>);
|
||||
|
||||
const getFiatSuffix = (satoshis: bigint): string => {
|
||||
const fiatValue = formatSatoshisToFiat(satoshis);
|
||||
return fiatValue ? ` (~${fiatValue})` : '';
|
||||
@@ -625,11 +621,10 @@ export function InvitationScreen(): React.ReactElement {
|
||||
}
|
||||
};
|
||||
|
||||
const isSatoshisVariable = (variableIdentifier: string): boolean => {
|
||||
const templateVariable = selectedTemplate?.variables?.[variableIdentifier];
|
||||
const templateType = templateVariable?.type?.toLowerCase();
|
||||
const templateHint = templateVariable?.hint?.toLowerCase();
|
||||
const identifier = variableIdentifier.toLowerCase();
|
||||
const isSatoshisVariable = (variable: ResolvedInvitationVariable): boolean => {
|
||||
const templateHint = variable.hint?.toLowerCase();
|
||||
const templateType = variable.type?.toLowerCase();
|
||||
const identifier = variable.variableIdentifier.toLowerCase();
|
||||
|
||||
if (templateHint?.includes('satoshi')) {
|
||||
return true;
|
||||
@@ -641,6 +636,20 @@ export function InvitationScreen(): React.ReactElement {
|
||||
);
|
||||
};
|
||||
|
||||
const compileResolvedDescription = (description?: string): string | null => {
|
||||
if (!description) return null;
|
||||
|
||||
try {
|
||||
return compileCashAssemblyString({
|
||||
cashAssemblyText: description,
|
||||
variables: variableValues,
|
||||
evaluationDecodeMode: 'bigint',
|
||||
});
|
||||
} catch {
|
||||
return description;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{/* Type & Status */}
|
||||
@@ -693,28 +702,21 @@ export function InvitationScreen(): React.ReactElement {
|
||||
) : (
|
||||
inputs.map((input, idx) => {
|
||||
const isUserInput = input.entityIdentifier === userEntityId;
|
||||
const inputTemplate = selectedTemplate?.inputs?.[input.inputIdentifier ?? ''];
|
||||
const inputSatoshis = (
|
||||
'valueSatoshis' in input && input.valueSatoshis !== undefined
|
||||
)
|
||||
? parseNumberishToBigInt(input.valueSatoshis)
|
||||
: null;
|
||||
const inputDescription = compileResolvedDescription(input.description);
|
||||
return (
|
||||
<Text
|
||||
key={`input-${idx}`}
|
||||
color={isUserInput ? colors.success : colors.text}
|
||||
>
|
||||
{/* Indicator for whether this is the user's input */}
|
||||
{' '}{isUserInput ? '• ' : '○ '}
|
||||
|
||||
{/* TODO: Why doesnt this stuff work? It just cant resolve inputs? */}
|
||||
{/* Input name */}
|
||||
{inputTemplate?.name ?? input.inputIdentifier ?? `Input ${idx}`}
|
||||
|
||||
{/* Input role */}
|
||||
{input.name ?? input.inputIdentifier ?? `Input ${idx}`}
|
||||
{input.roleIdentifier && ` (${input.roleIdentifier})`}
|
||||
|
||||
{/* Input value */}
|
||||
{inputDescription && ` - ${inputDescription}`}
|
||||
{inputSatoshis !== null && ` ${formatSatoshis(inputSatoshis)}${getFiatSuffix(inputSatoshis)}`}
|
||||
</Text>
|
||||
);
|
||||
@@ -729,33 +731,18 @@ export function InvitationScreen(): React.ReactElement {
|
||||
) : (
|
||||
outputs.map((output, idx) => {
|
||||
const isUserOutput = output.entityIdentifier === userEntityId;
|
||||
const outputTemplate = selectedTemplate?.outputs?.[output.outputIdentifier ?? ''];
|
||||
const outputSatoshis = output.valueSatoshis !== undefined
|
||||
? parseNumberishToBigInt(output.valueSatoshis)
|
||||
: null;
|
||||
const outputDescription = compileResolvedDescription(output.description);
|
||||
return (
|
||||
<Text
|
||||
key={`output-${idx}`}
|
||||
color={isUserOutput ? colors.success : colors.text}
|
||||
>
|
||||
{/* Indicator for whether this is the user's output */}
|
||||
{' '}{isUserOutput ? '• ' : '○ '}
|
||||
|
||||
{/* Output name */}
|
||||
{outputTemplate?.name ?? output.outputIdentifier ?? `Output ${idx}`}
|
||||
|
||||
{/* Output description */}
|
||||
{outputTemplate?.description && ' - ' + compileCashAssemblyString({
|
||||
cashAssemblyText: outputTemplate?.description,
|
||||
variables: variables.reduce((acc, variable) => {
|
||||
acc[variable.variableIdentifier] = variable.value as XOInvitationVariableValue;
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, XOInvitationVariableValue>),
|
||||
evaluationDecodeMode: 'bigint'
|
||||
})}
|
||||
|
||||
{/* Output value */}
|
||||
{output.name ?? output.outputIdentifier ?? `Output ${idx}`}
|
||||
{outputDescription && ` - ${outputDescription}`}
|
||||
{outputSatoshis !== null && ` (${formatSatoshis(outputSatoshis)}${getFiatSuffix(outputSatoshis)})`}
|
||||
</Text>
|
||||
);
|
||||
@@ -772,11 +759,10 @@ export function InvitationScreen(): React.ReactElement {
|
||||
) : (
|
||||
variables.map((variable, idx) => {
|
||||
const isUserVariable = variable.entityIdentifier === userEntityId;
|
||||
const varTemplate = selectedTemplate?.variables?.[variable.variableIdentifier];
|
||||
const displayValue = typeof variable.value === 'bigint'
|
||||
? variable.value.toString()
|
||||
: String(variable.value);
|
||||
const parsedVariableSatoshis = isSatoshisVariable(variable.variableIdentifier)
|
||||
const parsedVariableSatoshis = isSatoshisVariable(variable)
|
||||
? parseNumberishToBigInt(variable.value)
|
||||
: null;
|
||||
return (
|
||||
@@ -785,11 +771,11 @@ export function InvitationScreen(): React.ReactElement {
|
||||
color={isUserVariable ? colors.success : colors.text}
|
||||
>
|
||||
{' '}{isUserVariable ? '• ' : '○ '}
|
||||
{varTemplate?.name ?? variable.variableIdentifier}: {displayValue}
|
||||
{variable.name ?? variable.variableIdentifier}: {displayValue}
|
||||
{parsedVariableSatoshis !== null &&
|
||||
` (${formatSatoshis(parsedVariableSatoshis)}${getFiatSuffix(parsedVariableSatoshis)})`}
|
||||
{varTemplate?.description && (
|
||||
<Text color={colors.textMuted} dimColor> - {varTemplate.description}</Text>
|
||||
{variable.description && (
|
||||
<Text color={colors.textMuted} dimColor> - {variable.description}</Text>
|
||||
)}
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -14,12 +14,29 @@ import { useLayeredInput } from '../../../../hooks/useInputLayer.js';
|
||||
import {
|
||||
getInvitationState,
|
||||
getStateColorName,
|
||||
getInvitationInputs,
|
||||
getInvitationOutputs,
|
||||
getInvitationVariables,
|
||||
} 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.
|
||||
*/
|
||||
@@ -51,16 +68,18 @@ export function PreviewInvitationStep({
|
||||
|
||||
const state = getInvitationState(invitation);
|
||||
const action = template?.actions?.[invitation.data.actionIdentifier];
|
||||
const inputs = getInvitationInputs(invitation);
|
||||
const outputs = getInvitationOutputs(invitation);
|
||||
const variables = getInvitationVariables(invitation);
|
||||
const { inputs, outputs, variables } = invitation.resolvedData;
|
||||
|
||||
// Collect role identifiers that appear across all commits
|
||||
// Collect role identifiers that appear across resolved invitation data
|
||||
const filledRoles = new Set<string>();
|
||||
for (const commit of invitation.data.commits ?? []) {
|
||||
for (const input of commit.data?.inputs ?? []) {
|
||||
if (input.roleIdentifier) filledRoles.add(input.roleIdentifier);
|
||||
}
|
||||
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 (
|
||||
@@ -143,11 +162,10 @@ export function PreviewInvitationStep({
|
||||
</Box>
|
||||
) : (
|
||||
inputs.map((input, idx) => {
|
||||
const inputTemplate = template?.inputs?.[input.inputIdentifier ?? ''];
|
||||
return (
|
||||
<Box key={`input-${idx}`}>
|
||||
<Text color={colors.text}>
|
||||
{' '}• {inputTemplate?.name ?? input.inputIdentifier ?? `Input ${idx}`}
|
||||
{' '}• {input.name ?? input.inputIdentifier ?? `Input ${idx}`}
|
||||
{input.roleIdentifier && ` (${input.roleIdentifier})`}
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -170,15 +188,17 @@ export function PreviewInvitationStep({
|
||||
</Box>
|
||||
) : (
|
||||
outputs.map((output, idx) => {
|
||||
const outputTemplate = template?.outputs?.[output.outputIdentifier ?? ''];
|
||||
const fiatValue = output.valueSatoshis !== undefined
|
||||
? formatSatoshisToFiat(output.valueSatoshis)
|
||||
: null;
|
||||
const outputSatoshis = output.valueSatoshis !== undefined
|
||||
? parseNumberishToBigInt(output.valueSatoshis)
|
||||
: null;
|
||||
return (
|
||||
<Box key={`output-${idx}`}>
|
||||
<Text color={colors.text}>
|
||||
{' '}• {outputTemplate?.name ?? output.outputIdentifier ?? `Output ${idx}`}
|
||||
{output.valueSatoshis !== undefined && ` (${formatSatoshis(output.valueSatoshis)})`}
|
||||
{' '}• {output.name ?? output.outputIdentifier ?? `Output ${idx}`}
|
||||
{outputSatoshis !== null && ` (${formatSatoshis(outputSatoshis)})`}
|
||||
{fiatValue && ` (~${fiatValue})`}
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -201,14 +221,13 @@ export function PreviewInvitationStep({
|
||||
</Box>
|
||||
) : (
|
||||
variables.map((variable, idx) => {
|
||||
const varTemplate = template?.variables?.[variable.variableIdentifier];
|
||||
const displayValue = typeof variable.value === 'bigint'
|
||||
? variable.value.toString()
|
||||
: String(variable.value);
|
||||
return (
|
||||
<Box key={`var-${idx}`}>
|
||||
<Text color={colors.text}>
|
||||
{' '}• {varTemplate?.name ?? variable.variableIdentifier}: {displayValue}
|
||||
{' '}• {variable.name ?? variable.variableIdentifier}: {displayValue}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user