()
| 41 | } |
| 42 | |
| 43 | export default function App() { |
| 44 | const [transport, setTransport] = useState<TransportKey>('fetch-http') |
| 45 | const [input, setInput] = useState('Hello from React Native') |
| 46 | const connection = useConnection(transport) |
| 47 | |
| 48 | const { messages, sendMessage, isLoading, error, stop } = useChat({ |
| 49 | id: `rn-smoke-${transport}`, |
| 50 | connection, |
| 51 | }) |
| 52 | |
| 53 | async function send() { |
| 54 | const next = input.trim() |
| 55 | if (!next || isLoading) return |
| 56 | setInput('') |
| 57 | await sendMessage(next) |
| 58 | } |
| 59 | |
| 60 | return ( |
| 61 | <SafeAreaView style={styles.screen}> |
| 62 | <View style={styles.header}> |
| 63 | <Text style={styles.title}>TanStack AI RN Smoke</Text> |
| 64 | <Text style={styles.subtitle}>{chatUrl}</Text> |
| 65 | </View> |
| 66 | |
| 67 | <View style={styles.transportRow}> |
| 68 | {Object.entries(transportLabels).map(([key, label]) => ( |
| 69 | <Button |
| 70 | key={key} |
| 71 | title={label} |
| 72 | color={transport === key ? '#0f766e' : '#475569'} |
| 73 | onPress={() => setTransport(key as TransportKey)} |
| 74 | /> |
| 75 | ))} |
| 76 | </View> |
| 77 | |
| 78 | <ScrollView style={styles.messages}> |
| 79 | {messages.map((message) => ( |
| 80 | <View key={message.id} style={styles.message}> |
| 81 | <Text style={styles.role}>{message.role}</Text> |
| 82 | {message.parts.map((part, index) => |
| 83 | part.type === 'text' ? ( |
| 84 | <Text key={index} style={styles.part}> |
| 85 | {part.content} |
| 86 | </Text> |
| 87 | ) : null, |
| 88 | )} |
| 89 | </View> |
| 90 | ))} |
| 91 | {error ? <Text style={styles.error}>{error.message}</Text> : null} |
| 92 | </ScrollView> |
| 93 | |
| 94 | <View style={styles.composer}> |
| 95 | <TextInput |
| 96 | accessibilityLabel="Message" |
| 97 | style={styles.input} |
| 98 | value={input} |
| 99 | onChangeText={setInput} |
| 100 | editable={!isLoading} |
nothing calls this directly
no test coverage detected