Commit b6c2294a authored by dungtnguyen's avatar dungtnguyen

ai-agent

parent 11ce3237
...@@ -15,7 +15,7 @@ import {Colors} from 'react-native/Libraries/NewAppScreen'; ...@@ -15,7 +15,7 @@ import {Colors} from 'react-native/Libraries/NewAppScreen';
import {Provider} from 'react-redux'; import {Provider} from 'react-redux';
import store from './src/app/store'; import store from './src/app/store';
import NoConnection from './src/components/noconnection/NoConnection'; import NoConnection from './src/components/noconnection/NoConnection';
import Main from './src/Main'; import AppNavigation from './src/navigation/AppNavigation';
function App(): React.JSX.Element { function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark'; const isDarkMode = useColorScheme() === 'dark';
...@@ -79,7 +79,7 @@ function App(): React.JSX.Element { ...@@ -79,7 +79,7 @@ function App(): React.JSX.Element {
<Provider store={store}> <Provider store={store}>
<SafeAreaView style={backgroundStyle}> <SafeAreaView style={backgroundStyle}>
{!connectionStatus && <NoConnection isShow={!connectionStatus} />} {!connectionStatus && <NoConnection isShow={!connectionStatus} />}
<Main /> <AppNavigation />
<Toast /> <Toast />
</SafeAreaView> </SafeAreaView>
</Provider> </Provider>
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<!-- Include only if your app benefits from precise location access. --> <!-- Include only if your app benefits from precise location access. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application <application
android:name=".MainApplication" android:name=".MainApplication"
android:label="@string/app_name" android:label="@string/app_name"
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"@react-native-community/checkbox": "^0.5.17", "@react-native-community/checkbox": "^0.5.17",
"@react-native-community/datetimepicker": "^8.2.0", "@react-native-community/datetimepicker": "^8.2.0",
"@react-native-community/netinfo": "^11.4.1", "@react-native-community/netinfo": "^11.4.1",
"@react-native-community/voice": "^1.1.9",
"@react-navigation/bottom-tabs": "^6.6.1", "@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/drawer": "^6.7.2", "@react-navigation/drawer": "^6.7.2",
"@react-navigation/material-top-tabs": "^6.6.14", "@react-navigation/material-top-tabs": "^6.6.14",
...@@ -24,6 +25,7 @@ ...@@ -24,6 +25,7 @@
"formik": "^2.4.6", "formik": "^2.4.6",
"i18next": "^23.15.1", "i18next": "^23.15.1",
"ImCheckInModule": "file:ImCheckInModule-0.0.1.tgz", "ImCheckInModule": "file:ImCheckInModule-0.0.1.tgz",
"lottie-react-native": "^7.2.2",
"md5": "^2.3.0", "md5": "^2.3.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"react": "18.3.1", "react": "18.3.1",
...@@ -58,6 +60,7 @@ ...@@ -58,6 +60,7 @@
"react-native-swiper": "^1.6.0", "react-native-swiper": "^1.6.0",
"react-native-tab-view": "^3.5.2", "react-native-tab-view": "^3.5.2",
"react-native-toast-message": "^2.2.1", "react-native-toast-message": "^2.2.1",
"react-native-tts": "^4.1.1",
"react-native-virtualized-view": "^1.0.0", "react-native-virtualized-view": "^1.0.0",
"react-redux": "^9.1.2", "react-redux": "^9.1.2",
"redux": "^5.0.1", "redux": "^5.0.1",
...@@ -3354,6 +3357,18 @@ ...@@ -3354,6 +3357,18 @@
"react-native": ">=0.59" "react-native": ">=0.59"
} }
}, },
"node_modules/@react-native-community/voice": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@react-native-community/voice/-/voice-1.1.9.tgz",
"integrity": "sha512-6QC51lIxdtPyIWyJEG7qsZ0nyRQFV7wUREcnvlQD3+xyk3p5na9LELR+rkIGdxVbLb3WJ73rhgjrc/ywyUh10Q==",
"license": "MIT",
"dependencies": {
"invariant": "^2.2.4"
},
"peerDependencies": {
"react-native": ">=0.40.0"
}
},
"node_modules/@react-native/assets-registry": { "node_modules/@react-native/assets-registry": {
"version": "0.75.3", "version": "0.75.3",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.75.3.tgz", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.75.3.tgz",
...@@ -10759,6 +10774,26 @@ ...@@ -10759,6 +10774,26 @@
"loose-envify": "cli.js" "loose-envify": "cli.js"
} }
}, },
"node_modules/lottie-react-native": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-7.2.2.tgz",
"integrity": "sha512-pp3dnFVFZlfZzIL5qKGXju2d6RfnYhPbb8xQL9dYqvPbPy2EbnK2aFlv6jAZLYh0QjUGPEmRAgAAnsOOtT+H9Q==",
"license": "Apache-2.0",
"peerDependencies": {
"@lottiefiles/dotlottie-react": "^0.6.5",
"react": "*",
"react-native": ">=0.46",
"react-native-windows": ">=0.63.x"
},
"peerDependenciesMeta": {
"@lottiefiles/dotlottie-react": {
"optional": true
},
"react-native-windows": {
"optional": true
}
}
},
"node_modules/lower-case": { "node_modules/lower-case": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
...@@ -12940,6 +12975,12 @@ ...@@ -12940,6 +12975,12 @@
"react-native": "*" "react-native": "*"
} }
}, },
"node_modules/react-native-tts": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/react-native-tts/-/react-native-tts-4.1.1.tgz",
"integrity": "sha512-VL0TgCwkUWggbbFGIXAPKC3rM1baluAYtgOdgnaTm7UYsWf/y8n5VgmVB0J2Wa8qt1dldZ1cSsdQY9iz3evcAg==",
"license": "MIT"
},
"node_modules/react-native-vector-icons": { "node_modules/react-native-vector-icons": {
"version": "10.2.0", "version": "10.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.2.0.tgz", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.2.0.tgz",
......
import React, { useState, useEffect } from 'react'
import AsyncStorageKeys from './utils/AsyncStorageKeys'
import AppNavigation from './navigation/AppNavigation'
function Main() {
//TODO: Check whether user is admin or normal user
const [isLogged, setIsLogged] = useState(null)
return <AppNavigation isLogged={isLogged}></AppNavigation>
}
export default Main
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path d="M3 16h26M3 16l4-5M3 16l4 5" fill="none" stroke="#2430e7" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2px" class="stroke-000000"></path>
</svg>
\ No newline at end of file
<svg viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
enable-background="new 0 0 24 24"><path d="m21.5 11.1-17.9-9c-.9-.4-1.9.4-1.5 1.3l2.5 6.7L16 12 4.6 13.9l-2.5 6.7c-.3.9.6 1.7 1.5 1.2l17.9-9c.7-.3.7-1.3 0-1.7z" fill="#ffffff" class="fill-000000"></path></svg>
\ No newline at end of file
import ArrowLeftSVG from './ArrowLeftSVG.svg'; import ArrowLeftSVG from './ArrowLeftSVG.svg';
export {ArrowLeftSVG}; import ArrowLeftPrimarySVG from './ArrowLeftPrimarySVG.svg';
import SendMessageSVG from './SendMessageSVG.svg';
export {ArrowLeftSVG, ArrowLeftPrimarySVG, SendMessageSVG};
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -15,6 +15,7 @@ import {APP_NAVIGATE_SCREEN} from '../utils/constant'; ...@@ -15,6 +15,7 @@ import {APP_NAVIGATE_SCREEN} from '../utils/constant';
import BottomTabs from './BottomTabNavigation'; import BottomTabs from './BottomTabNavigation';
import RootNavigation from './RootNavigation'; import RootNavigation from './RootNavigation';
import HomeScreen from '../screens/home'; import HomeScreen from '../screens/home';
import ChatAssistantScreen from '../screens/chat_assistant';
const { const {
INTRO, INTRO,
...@@ -28,6 +29,7 @@ const { ...@@ -28,6 +29,7 @@ const {
CONFIRM_SHIFT_DETAIL, CONFIRM_SHIFT_DETAIL,
SALARY, SALARY,
HOME, HOME,
CHAT_ASSISTANT,
} = APP_NAVIGATE_SCREEN; } = APP_NAVIGATE_SCREEN;
const AppScreen = { const AppScreen = {
[SPLASH]: SplashContainer, [SPLASH]: SplashContainer,
...@@ -42,6 +44,7 @@ const AppScreen = { ...@@ -42,6 +44,7 @@ const AppScreen = {
[SALARY]: SalaryScreen, [SALARY]: SalaryScreen,
[INTRO]: IntroScreen, [INTRO]: IntroScreen,
[HOME]: HomeScreen, [HOME]: HomeScreen,
[CHAT_ASSISTANT]: ChatAssistantScreen,
}; };
const Stack = createNativeStackNavigator(); const Stack = createNativeStackNavigator();
......
import React, {useState} from 'react';
import ChatAssistantMainView from './template/ChatAssistantMainView';
const ChatAssistantContainer = () => {
const [messages, setMessages] = useState([
{
id: '1',
text: 'Hi, is this laptop still in stock? I wanna buy it',
user: 'user',
},
]);
const [inputText, setInputText] = useState('');
const sendMessage = () => {
if (inputText.trim()) {
const newMessage = {
id: String(messages.length + 1),
text: inputText,
user: 'user',
};
setMessages([...messages, newMessage]);
setInputText('');
// Phản hồi giả lập từ trợ lý (có thể thay bằng API thực tế)
setTimeout(() => {
const assistantReply = {
id: String(messages.length + 2),
text: 'Got it! I’ll process your request. Anything else I can help with? :)',
user: 'assistant',
};
setMessages(prevMessages => [...prevMessages, assistantReply]);
}, 1000); // Trễ 1 giây để mô phỏng phản hồi
}
};
const chatProps = {
messages,
inputText,
setInputText,
sendMessage,
};
return <ChatAssistantMainView {...chatProps} />;
};
export default ChatAssistantContainer;
import React from 'react';
import ChatAssistantContainer from './chatAssistantContainer';
export default function ChatAssistantScreen() {
return <ChatAssistantContainer />;
}
import {Dimensions, StyleSheet} from 'react-native';
import colors from '../../values/colors';
const windowHeight = Dimensions.get('screen').height;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
backgroundColor: colors.white,
elevation: 4, // Shadow cho Android
shadowColor: '#000', // Shadow cho iOS
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.2,
shadowRadius: 4,
},
backButton: {
padding: 5,
},
headerContent: {
flex: 1,
marginLeft: 10,
},
headerTitle: {
fontSize: 18,
fontWeight: 'bold',
color: colors.black1,
},
headerSubtitle: {
fontSize: 12,
color: colors.primary_blue,
},
messageList: {
flex: 1,
paddingHorizontal: 10,
},
messageContainer: {
maxWidth: '80%',
marginVertical: 5,
padding: 10,
borderRadius: 8,
},
userMessage: {
alignSelf: 'flex-end',
backgroundColor: '#007AFF', //
},
assistantMessage: {
alignSelf: 'flex-start',
backgroundColor: '#E5E5EA', // Màu xám nhạt cho tin nhắn trợ lý
},
messageText: {
fontSize: 16,
color: colors.black1,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
backgroundColor: colors.white,
borderTopWidth: 1,
borderTopColor: '#ddd',
},
input: {
flex: 1,
height: 40,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 20,
paddingHorizontal: 15,
backgroundColor: '#f9f9f9',
},
sendButton: {
marginLeft: 10,
backgroundColor: '#007AFF',
borderRadius: 20,
width: 40,
height: 40,
justifyContent: 'center',
alignItems: 'center',
},
btnAssistant: {
position: 'absolute',
bottom: 80,
left: 0,
zIndex: 100,
},
assistant: {
width: 100,
height: 100,
},
});
export default styles;
import LottieView from 'lottie-react-native';
import React from 'react';
import {
FlatList,
SafeAreaView,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import VirtualAssistant from '../../../mockup/assistant.json';
import styles from '../styles';
import {ArrowLeftPrimarySVG, SendMessageSVG} from '../../../assets/svg';
import AppText from '../../../components/AppText';
const ChatAssistantMainView = props => {
const {messages, inputText, setInputText, sendMessage} = props;
const renderMessage = ({item}) => (
<View
style={[
styles.messageContainer,
item.user === 'user' ? styles.userMessage : styles.assistantMessage,
]}>
<AppText style={styles.messageText}>{item.text}</AppText>
</View>
);
return (
<SafeAreaView style={styles.container}>
<TouchableOpacity style={styles.btnAssistant}>
<LottieView
source={VirtualAssistant} // File Lottie của bạn
style={styles.assistant}
loop={true}
autoPlay
duration={5000}
/>
</TouchableOpacity>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity style={styles.backButton}>
<ArrowLeftPrimarySVG width={20} height={20} />
</TouchableOpacity>
<View style={styles.headerContent}>
<AppText style={styles.headerTitle}>Tr Lý o Long Long</AppText>
<AppText style={styles.headerSubtitle}>Active now</AppText>
</View>
</View>
{/* Danh sách tin nhắn */}
<FlatList
data={messages}
renderItem={renderMessage}
keyExtractor={item => item.id}
style={styles.messageList}
// Đảo ngược danh sách để tin nhắn mới nhất ở dưới
/>
{/* Ô nhập tin nhắn */}
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
value={inputText}
onChangeText={setInputText}
placeholder="Type something..."
placeholderTextColor="#999"
/>
<TouchableOpacity onPress={sendMessage} style={styles.sendButton}>
<SendMessageSVG width={20} height={20} />
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
export default ChatAssistantMainView;
...@@ -30,6 +30,9 @@ import { ...@@ -30,6 +30,9 @@ import {
} from './homeSlice'; } from './homeSlice';
import styles from './style'; import styles from './style';
import HomeMainView from './template/HomeMainView'; import HomeMainView from './template/HomeMainView';
import RootNavigation from '../../navigation/RootNavigation';
import config from '../../config';
import {APP_NAVIGATE_SCREEN} from '../../utils/constant';
const HomeContainer = props => { const HomeContainer = props => {
const {userInfo, quotationList, birthdayListInMonth, randomQuotation} = props; const {userInfo, quotationList, birthdayListInMonth, randomQuotation} = props;
const dispatch = useDispatch(); const dispatch = useDispatch();
...@@ -398,6 +401,10 @@ const HomeContainer = props => { ...@@ -398,6 +401,10 @@ const HomeContainer = props => {
const downloadComponent = async () => { const downloadComponent = async () => {
setLoading(true); setLoading(true);
}; };
const navigateToChatAssistant = () => {
RootNavigation.navigate(APP_NAVIGATE_SCREEN.CHAT_ASSISTANT);
};
// useEffect // useEffect
useEffect(() => { useEffect(() => {
const backAction = () => { const backAction = () => {
...@@ -425,9 +432,6 @@ const HomeContainer = props => { ...@@ -425,9 +432,6 @@ const HomeContainer = props => {
birthdayListInMonth && formatDateList(); birthdayListInMonth && formatDateList();
}, [birthdayListInMonth]); }, [birthdayListInMonth]);
useEffect(() => {
RemoteComponent && console.log('RemoteComponent', RemoteComponent);
}, [RemoteComponent]);
//props //props
const homeProps = { const homeProps = {
userInfo, userInfo,
...@@ -460,6 +464,7 @@ const HomeContainer = props => { ...@@ -460,6 +464,7 @@ const HomeContainer = props => {
addMoreImgFromGallery, addMoreImgFromGallery,
onCalendarChangeMonth, onCalendarChangeMonth,
openCheckInPage, openCheckInPage,
navigateToChatAssistant,
}; };
return <HomeMainView {...homePropsProvider(homeProps)} />; return <HomeMainView {...homePropsProvider(homeProps)} />;
}; };
......
...@@ -24,6 +24,7 @@ export default function homePropsProvider(props) { ...@@ -24,6 +24,7 @@ export default function homePropsProvider(props) {
setShowModalInstall, setShowModalInstall,
toggleImCheckInModal, toggleImCheckInModal,
downloadComponent, downloadComponent,
navigateToChatAssistant,
} = props; } = props;
return { return {
userInfo, userInfo,
...@@ -37,6 +38,7 @@ export default function homePropsProvider(props) { ...@@ -37,6 +38,7 @@ export default function homePropsProvider(props) {
chooseSettingView, chooseSettingView,
openSettingView, openSettingView,
toggleImCheckInModal, toggleImCheckInModal,
navigateToChatAssistant,
calendarBirthdayProps: { calendarBirthdayProps: {
birthdayOfUser, birthdayOfUser,
bdUserCurrentMonth, bdUserCurrentMonth,
......
import {StyleSheet, Dimensions} from 'react-native'; import {StyleSheet, Dimensions} from 'react-native';
import colors from '../../values/colors'; import colors from '../../values/colors';
const windowWidth = Dimensions.get('window').width; const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('screen').height;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
...@@ -143,5 +143,15 @@ const styles = StyleSheet.create({ ...@@ -143,5 +143,15 @@ const styles = StyleSheet.create({
marginTop: -50, marginTop: -50,
backgroundColor: 'white', backgroundColor: 'white',
}, },
assistant: {
width: 100,
height: 100,
},
btnAssistant: {
position: 'absolute',
top: windowHeight / 2,
right: 0,
zIndex: 100,
},
}); });
export default styles; export default styles;
import React from 'react'; import React from 'react';
import { import {Image, SafeAreaView, ScrollView, TouchableOpacity} from 'react-native';
Image,
SafeAreaView,
ScrollView,
TouchableOpacity,
View,
} from 'react-native';
import styles from '../style'; import styles from '../style';
import LottieView from 'lottie-react-native';
import {IMAGES} from '../../../assets/images';
import VirtualAssistant from '../../../mockup/assistant.json';
import ImCheckInInstall from '../components/ImCheckInInstall';
import WishListComponent from '../components/WishListComponent'; import WishListComponent from '../components/WishListComponent';
import BirthdayModal from '../modals/BirthdayModals'; import BirthdayModal from '../modals/BirthdayModals';
import CalendarBirthday from './subViews/CalendarBirthday'; import CalendarBirthday from './subViews/CalendarBirthday';
import Quotation from './subViews/QuotationList'; import Quotation from './subViews/QuotationList';
import {Button} from 'react-native-paper';
import {IMAGES} from '../../../assets/images'; const HomeMainView = props => {
import ImCheckInInstall from '../components/ImCheckInInstall'; const {
import ImCheckInModule from 'ImCheckInModule'; userInfo,
const HomeMainView = ({ openView,
userInfo, openModalHappyBirthday,
openView, closeModalHappyBirthday,
openModalHappyBirthday, wishList,
closeModalHappyBirthday, openProfileComponent,
wishList, openCheckIn,
openProfileComponent, calendarBirthdayProps,
openCheckIn, randomQuotation,
calendarBirthdayProps, birthdayModalProps,
randomQuotation, imCheckInInstall,
birthdayModalProps, toggleImCheckInModal,
imCheckInInstall, navigateToChatAssistant,
toggleImCheckInModal, } = props;
}) => {
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<TouchableOpacity
style={styles.btnAssistant}
onPress={navigateToChatAssistant}>
<LottieView
source={VirtualAssistant} // File Lottie của bạn
style={styles.assistant}
loop={true}
autoPlay
duration={5000}
/>
</TouchableOpacity>
{!openCheckIn && ( {!openCheckIn && (
<ScrollView <ScrollView
nestedScrollEnabled={true} nestedScrollEnabled={true}
...@@ -59,6 +67,7 @@ const HomeMainView = ({ ...@@ -59,6 +67,7 @@ const HomeMainView = ({
)} )}
</ScrollView> </ScrollView>
)} )}
<BirthdayModal {...birthdayModalProps} /> <BirthdayModal {...birthdayModalProps} />
<ImCheckInInstall {...imCheckInInstall} /> <ImCheckInInstall {...imCheckInInstall} />
......
import React from 'react';
import VirtualAssistantContainer from './virtualAssistantContainer';
export default function VirtualAssistantScreen() {
return <VirtualAssistantContainer />;
}
import React, {useEffect, useRef} from 'react';
import {View, StyleSheet, Button} from 'react-native';
import LottieView from 'lottie-react-native';
import VirtualAssistant from '../../../mockup/assistant.json';
import {
Gesture,
GestureHandlerRootView,
TapGestureHandler,
} from 'react-native-gesture-handler';
const VirtualAssistantMainView = props => {
const {animationRef, handleInteraction} = props;
const onTap = () => {
alert('Bạn vừa chạm vào tôi!');
};
return (
<GestureHandlerRootView style={{flex: 1}}>
<View style={styles.container}>
<LottieView
ref={animationRef}
source={VirtualAssistant} // File Lottie của bạn
style={styles.assistant}
loop={true}
/>
</View>
<Button title="Nói chuyện với tôi" onPress={handleInteraction} />
</GestureHandlerRootView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
assistant: {
width: 200,
height: 200,
},
});
export default VirtualAssistantMainView;
import React, {useEffect, useRef} from 'react';
import Tts from 'react-native-tts';
import VirtualAssistantMainView from './template/virtualAssistantMainView';
const INTRODUCE_VOICE =
'Xin chào! Tôi là trợ lý ảo của bạn. Tôi ở đây để hỗ trợ bạn trong công việc, giải đáp thắc mắc và mang đến những giây phút thú vị. Hôm nay tôi có thể giúp gì cho bạn?';
const VirtualAssistantContainer = () => {
const animationRef = useRef(null);
// Hàm để trợ lý nói
const assistantSpeak = text => {
animationRef.current?.play(); // Phát hoạt hình khi nói
Tts.speak(text); // Chuyển văn bản thành giọng nói
};
// Hàm xử lý khi người dùng tương tác
const handleInteraction = () => {
assistantSpeak(INTRODUCE_VOICE);
};
useEffect(() => {
Tts.setDefaultLanguage('vi-VN'); // Ngôn ngữ tiếng Việt, thay bằng 'en-US' nếu muốn tiếng Anh
Tts.setDefaultRate(0.5); // Tốc độ nói (0.5 là bình thường)
Tts.addEventListener('tts-finish', () => {
animationRef.current?.reset(); // Dừng hoạt hình khi nói xong
});
animationRef.current?.play();
return () => {
Tts.stop(); // Dừng TTS khi component unmount
};
}, []);
const virtualProps = {animationRef, handleInteraction};
return <VirtualAssistantMainView {...virtualProps} />;
};
export default VirtualAssistantContainer;
...@@ -37,6 +37,7 @@ export const APP_NAVIGATE_SCREEN = { ...@@ -37,6 +37,7 @@ export const APP_NAVIGATE_SCREEN = {
FORGOT_PASS: 'Quên mật khẩu', FORGOT_PASS: 'Quên mật khẩu',
DAY_WAGE: 'Công nhật', DAY_WAGE: 'Công nhật',
Notification: 'Thông báo', Notification: 'Thông báo',
CHAT_ASSISTANT: 'CHAT ASSISTANT',
}; };
export const ADMIN_NAVIGATE_SCREEN = { export const ADMIN_NAVIGATE_SCREEN = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment