import { useState } from 'react'; import { Button, ScrollView, Text, View } from 'react-native'; import { Audio } from 'expo-av'; import * as Crypto from 'expo-crypto'; const WORKER = process.env.EXPO_PUBLIC_WORKER_URL; // e.g. https://toneperfect-proxy.you.workers.dev const APP_TOKEN = process.env.EXPO_PUBLIC_APP_TOKEN || ''; const SENTENCE = { text: '我想买书', language: 'zh-CN' }; export default function App() { const [recording, setRecording] = useState(null); const [busy, setBusy] = useState(false); const [result, setResult] = useState(null); async function start() { await Audio.requestPermissionsAsync(); await Audio.setAudioModeAsync({ allowsRecordingIOS: true, playsInSilentModeIOS: true }); const { recording: rec } = await Audio.Recording.createAsync(Audio.RecordingOptionsPresets.HIGH_QUALITY); setRecording(rec); } async function stop() { await recording.stopAndUnloadAsync(); const uri = recording.getURI(); setRecording(null); setBusy(true); const form = new FormData(); form.append('audio', { uri, name: 'recording.m4a', type: 'audio/m4a' }); form.append('text', SENTENCE.text); form.append('language', SENTENCE.language); try { const response = await fetch(`${WORKER}/assess`, { method: 'POST', body: form, headers: { 'Idempotency-Key': Crypto.randomUUID(), 'X-App-Token': APP_TOKEN }, }); setResult(await response.json()); } catch (e) { setResult({ error: { message: 'Network error: ' + e.message } }); } finally { setBusy(false); } } return ( Say: {SENTENCE.text}