translation-terrace/components/ConversationThread.tsx

149 lines
4.5 KiB
TypeScript

import React, { useState, useEffect } from "react";
import { ScrollView, StyleSheet, Text, TouchableHighlight, View } from "react-native";
import { useNavigation, Route } from "@react-navigation/native";
import { Conversation, Message } from "@/app/lib/conversation";
import MessageBubble from "@/components/ui/MessageBubble";
import { CachedTranslator, LanguageServer, language_matrix_entry } from "@/app/i18n/api";
const lasOptions = {
sampleRate: 32000, // default is 44100 but 32000 is adequate for accurate voice recognition
channels: 1, // 1 or 2, default 1
bitsPerSample: 16, // 8 or 16, default 16
audioSource: 6, // android only (see below)
bufferSize: 4096, // default is 2048
};
// LiveAudioStream.init(lasOptions as any);
const ConversationThread = ({ route } : {route?: Route<"Conversation", {conversation : Conversation}>}) => {
const navigation = useNavigation();
if (!route) {
return (<View><Text>Missing Params!</Text></View>)
}
/* 2. Get the param */
const { conversation } = route?.params;
const [messages, setMessages] = useState<Message[]>([]);
const [guestSpeak, setGuestSpeak] = useState<string | undefined>();
const [guestSpeakLoaded, setGuestSpeakLoaded] = useState<boolean>(false);
const [cachedTranslator, setCachedTranslator] = useState<
undefined | CachedTranslator
>();
const [languageLabels, setLanguageLabels] = useState<undefined | {
hostNative: {
host: string,
guest: string,
},
guestNative: {
host: string,
guest: string,
}
}>()
useEffect(() => {
(async () => {
const languageServer = await LanguageServer.getDefault();
const languages = await languageServer.fetchLanguages();
const cc = new CachedTranslator(
"en",
conversation.guest.language,
languageServer,
)
setCachedTranslator(cc);
setGuestSpeak(await cc.translate("Speak"));
const hostLang1 = languages[conversation.host.language].name;
const guestLang1 = languages[conversation.host.language].name;
const hostLang2 = await cc.translate(languages[conversation.host.language].name);
const guestLang2 = await cc.translate(languages[conversation.host.language].name);
setLanguageLabels({
hostNative: {
host: hostLang1,
guest: guestLang1,
},
guestNative: {
host: hostLang2,
guest: guestLang2,
}
})
})();
const updateMessages = (c: Conversation) => {
setMessages([...c]);
};
conversation.onAddMessage = updateMessages;
conversation.onTranslationDone = updateMessages;
return () => {
conversation.onAddMessage = undefined;
conversation.onTranslationDone = undefined;
};
}, [conversation, guestSpeak]);
const renderMessages = () =>
messages.map((message, index) => (
<MessageBubble key={index} message={message} />
));
return cachedTranslator ? (
<View style={{ flex: 1, flexDirection: "column" }}>
{languageLabels && (<View style={styles.languageLabels}>
<Text style={styles.nativeHostLabel}>{ languageLabels.hostNative.host } / { languageLabels.hostNative.guest }</Text>
<Text style={styles.nativeGuestLabel}>{ languageLabels.guestNative.host } / { languageLabels.guestNative.guest }</Text>
</View>)
}
<ScrollView
style={{
borderColor: "black",
borderWidth: 1,
borderStyle: "solid",
height: "90%",
}}
>
{renderMessages()}
</ScrollView>
<View style={{ alignSelf: "center", flexDirection: "row" }}>
<TouchableHighlight
style={{ backgroundColor: "blue", padding: 3, borderRadius: 5 }}
>
<Text style={{ color: "white", fontSize: 30 }}>Speak</Text>
</TouchableHighlight>
<TouchableHighlight
style={{ backgroundColor: "gray", padding: 3, borderRadius: 5 }}
onPress={navigation.goBack}
>
<Text style={{ color: "white", fontSize: 30 }}>Go Back</Text>
</TouchableHighlight>
<TouchableHighlight
style={{ backgroundColor: "blue", padding: 3, borderRadius: 5 }}
>
<Text style={{ color: "white", fontSize: 30 }}>
{guestSpeak ? guestSpeak : "Speak"}
</Text>
</TouchableHighlight>
</View>
</View>
) : (
<View>
<Text>Loading...</Text>
</View>
);
};
const styles = StyleSheet.create({
languageLabels: {
},
nativeHostLabel: {
},
nativeGuestLabel: {
},
})
export default ConversationThread;