import React, { useEffect, useMemo, useState } from 'react'; import { Text, View } from 'react-native'; import { CreditCard } from 'lucide-react-native'; import { conversationStyles } from '../../features/conversation/conversationStyles'; import type { SuperagentToolRendererProps } from '../../types'; import { formatMethods, normalizeProviders, parsePspToolErrorMessage, selectPaymentProviderTitle, showsPspForm, } from '../interactiveWidgetUtils'; import { ApprovalCard } from '../primitives/ApprovalCard'; import { toolWidgetStyles } from '../primitives/toolWidgetStyles'; import { getWidgetStatus, parseToolArgs } from '../toolWidgetUtils'; import { useApprovalSubmit } from '../useApprovalSubmit'; import { FallbackActionCard, SelectableOptionRow, StatusRowWithError } from './interactiveWidgetParts'; /** * select_payment_provider — native port of the web SelectPaymentProvider * radio picker. Both actions resolve the call as approved: Continue submits * {processor_type}, Skip submits {skipped: true} (the backend treats skip as * a successful resolution, not a rejection). The form renders for waiting AND * error (the backend keeps this tool submittable on error for in-place retry * — web showForm parity, with the failure reason shown above the picker), * gated on isLastAssistantMessage so a stale card can't resume an old turn * out of order. */ export function SelectPaymentProviderWidget({ isLastAssistantMessage, submitToolCallInput, toolCall }: SuperagentToolRendererProps) { const args = useMemo(() => parseToolArgs(toolCall), [toolCall]); const providers = useMemo(() => normalizeProviders(args), [args]); const status = getWidgetStatus(toolCall.status); const { canAct, error, isSubmitting, submit } = useApprovalSubmit(toolCall, submitToolCallInput); const [selected, setSelected] = useState(providers[0]?.key ?? null); // The card can mount before the prepare hook's trusted provider list arrives // (streaming) — re-seed the selection whenever the list changes and the // current pick is no longer in it (web useSelectPaymentProvider parity). const providerKeys = providers.map((provider) => provider.key).join(' '); useEffect(() => { setSelected((current) => (providers.some((provider) => provider.key === current) ? current : (providers[0]?.key ?? null))); // providers is recomputed per render; key off the stable joined-keys string. // eslint-disable-next-line react-hooks/exhaustive-deps }, [providerKeys]); const resultError = status === 'error' ? parsePspToolErrorMessage(toolCall.results) : null; if (showsPspForm(status, isLastAssistantMessage)) { if (providers.length === 0) { return ( submit(true, { skipped: true }) : undefined} /> ); } return ( { if (selected) void submit(true, { processor_type: selected }); } : undefined} onReject={canAct ? () => submit(true, { skipped: true }) : undefined} rejectLabel="Skip for now" title="Choose a payment provider" > Choose a payment provider to activate payments. {resultError ? {resultError} : null} {providers.map((provider) => ( setSelected(provider.key)} selected={selected === provider.key} subtitle={provider.methods.length > 0 ? formatMethods(provider.methods) : null} /> ))} ); } return ( ); }