Commit 06fd6461 authored by Malsha Rathnasiri's avatar Malsha Rathnasiri

tts and chat screen design

parent 21820ebf
...@@ -48,6 +48,13 @@ import { Platform } from 'react-native' ...@@ -48,6 +48,13 @@ import { Platform } from 'react-native'
// const endpoint = getEnvironment().endpoint; // const endpoint = getEnvironment().endpoint;
export const isLoggedIn = async () => {
const token = await AsyncStorage.getItem('access_token')
console.log({ token })
return !!token
}
export const login = async (username, password) => { export const login = async (username, password) => {
const endpoint = false ? 'register' : 'login'; const endpoint = false ? 'register' : 'login';
...@@ -69,13 +76,17 @@ export const login = async (username, password) => { ...@@ -69,13 +76,17 @@ export const login = async (username, password) => {
axios.defaults.headers.common.Authorization = `Token ${access}`; axios.defaults.headers.common.Authorization = `Token ${access}`;
if (Platform.OS = 'web') { if (Platform.OS = 'web') {
console.log('no storage') await AsyncStorage.setItem('user_id', user.id)
await AsyncStorage.setItem('username', user.username)
await AsyncStorage.setItem('user_email', user.email)
await AsyncStorage.setItem('access_token', access)
await AsyncStorage.setItem('refresh_token', refresh)
} else { } else {
await AsyncStorage.setItem('user_id', user.id) await AsyncStorage.setItem('user_id', user.id)
await AsyncStorage.setitem('username', user.username) await AsyncStorage.setItem('username', user.username)
// AsyncStorage.setItem('user_email', user.email) await AsyncStorage.setItem('user_email', user.email)
// AsyncStorage.setitem('access_token', access) await AsyncStorage.setitem('access_token', access)
// AsyncStorage.setItem('refresh_token', refresh) await AsyncStorage.setItem('refresh_token', refresh)
} }
console.log("login successful") console.log("login successful")
...@@ -90,6 +101,10 @@ export const login = async (username, password) => { ...@@ -90,6 +101,10 @@ export const login = async (username, password) => {
} }
export const logout = async () => {
await AsyncStorage.clear()
}
// export const AuthProvider = ({ children }) => { // export const AuthProvider = ({ children }) => {
// const [isLoggedIn, setIsLoggedIn] = useState(false); // const [isLoggedIn, setIsLoggedIn] = useState(false);
......
import { BACKEND_URL } from "./constants" import { BACKEND_URL } from "./constants"
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation } from "@react-navigation/native";
const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYxMzU2NDA5LCJpYXQiOjE2NjAxNDY4MDksImp0aSI6ImFhMTljMmY2ZDNkMzRiNDdhZmZmM2FjMzVjNzI4MWJhIiwidXNlcl9pZCI6MX0.IVzibo_Rf2xzoT1J5o1L3zwu3mco6ODcNPC-7imu3Lo"
const headers = new Headers({ authorization: `Bearer ${token}`, 'Content-Type': 'application/json' })
export const create = (resource, values) => { //"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYxMzU2NDA5LCJpYXQiOjE2NjAxNDY4MDksImp0aSI6ImFhMTljMmY2ZDNkMzRiNDdhZmZmM2FjMzVjNzI4MWJhIiwidXNlcl9pZCI6MX0.IVzibo_Rf2xzoT1J5o1L3zwu3mco6ODcNPC-7imu3Lo"
// const headers =
const getHeaders = async () => {
var token = await AsyncStorage.getItem('access_token')
return new Headers({ authorization: `Bearer ${token}`, 'Content-Type': 'application/json' })
}
export const create = async (resource, values) => {
const headers = await getHeaders()
try { try {
return fetch(`${BACKEND_URL}/${resource}/`, { method: 'POST', body: JSON.stringify(values), headers }) return fetch(`${BACKEND_URL}/${resource}/`, { method: 'POST', body: JSON.stringify(values), headers })
} }
...@@ -12,6 +23,7 @@ export const create = (resource, values) => { ...@@ -12,6 +23,7 @@ export const create = (resource, values) => {
} }
} }
export const getList = (resource) => { export const getList = async (resource) => {
const headers = await getHeaders()
return fetch(`${BACKEND_URL}/${resource}/`, { method: 'GET', headers: headers }) return fetch(`${BACKEND_URL}/${resource}/`, { method: 'GET', headers: headers })
} }
\ No newline at end of file
// export const BACKEND_URL = "http://192.168.8.103:8000" // export const BACKEND_URL = "http://192.168.8.103:8000"
export const BACKEND_ADDRESS = "0be4-123-231-126-225.in.ngrok.io" export const BACKEND_ADDRESS = "127.0.0.1:8000"
export const BACKEND_URL = `https:/${BACKEND_ADDRESS}` export const BACKEND_URL = `http://${BACKEND_ADDRESS}`
\ No newline at end of file \ No newline at end of file
...@@ -73,7 +73,7 @@ export const AudioRecorder = ({ setDetectedText }) => { ...@@ -73,7 +73,7 @@ export const AudioRecorder = ({ setDetectedText }) => {
return ( return (
<Button <Button
style={{ height: '100%', padding: 5 }} style={{ height: '100%', padding: 5 }}
title={recording ? 'Stop Recording' : 'Start Recording'} title={recording ? 'Stop' : 'Record'}
onPress={recording ? stopRecording : startRecording} onPress={recording ? stopRecording : startRecording}
/> />
......
import { Ionicons } from "@expo/vector-icons";
import React, { useState } from "react";
import { View } from "react-native";
import { styles } from "../util/styles";
import * as Speech from 'expo-speech';
export const PlayMessage = ({ message }) => {
const [isPlaying, setIsPlaying] = useState(false)
const [isTtsReady, setIsTtsReady] = useState(false)
// // Tts.setDucking(true);
// Tts.addEventListener('tts-start', (event) => console.log("start"));
// Tts.addEventListener('tts-finish', (event) => console.log("finish"));
// const synth = window.speechSynthesis;
// const speech = new SpeechSynthesisUtterance(message)
// speech.lang = 'en'
// speech.onend((e) => console.log('end'))
const onSpeechDone = (props) => {
setIsPlaying(false)
}
const onPlayPress = () => {
setIsPlaying(true)
// synth.speak(speech)
Speech.speak(message, { onDone: onSpeechDone });
// speech.onend((e) => console.log('end'))
}
const onStopPress = () => {
Speech.stop()
setIsPlaying(false)
}
if (!isPlaying) {
return (
<Ionicons name="play" size={15} style={styles.chatIcon} onPress={onPlayPress} />
)
}
else {
return (
<Ionicons name="stop" size={15} style={styles.chatIcon} onPress={onStopPress} />
)
}
}
\ No newline at end of file
...@@ -7,27 +7,31 @@ import { FontAwesome } from '@expo/vector-icons'; ...@@ -7,27 +7,31 @@ import { FontAwesome } from '@expo/vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer, DefaultTheme, DarkTheme } from '@react-navigation/native'; import { NavigationContainer, DefaultTheme, DarkTheme } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Toast } from 'native-base'; import { Center, Toast } from 'native-base';
import * as React from 'react'; import * as React from 'react';
import { ColorSchemeName, Pressable } from 'react-native'; import { ColorSchemeName, Pressable, View, Button, Text, Image } from 'react-native';
import { login } from '../api/Authentication'; import { isLoggedIn, login, logout } from '../api/Authentication';
import Colors from '../constants/Colors'; import Colors from '../constants/Colors';
import useColorScheme from '../hooks/useColorScheme'; import useColorScheme from '../hooks/useColorScheme';
import { LoginScreen } from '../screens/loginScreen'; import { LoginScreen } from '../screens/loginScreen';
import ModalScreen from '../screens/ModalScreen'; import ModalScreen from '../screens/ModalScreen';
import NotFoundScreen from '../screens/NotFoundScreen'; import NotFoundScreen from '../screens/NotFoundScreen';
import { SignupScreen } from '../screens/SignupScreen';
import ChatScreen from '../screens/TabOneScreen'; import ChatScreen from '../screens/TabOneScreen';
import TabTwoScreen from '../screens/TabTwoScreen'; import TabTwoScreen from '../screens/TabTwoScreen';
import { RootStackParamList, RootTabParamList, RootTabScreenProps } from '../types'; import { RootStackParamList, RootTabParamList, RootTabScreenProps } from '../types';
import { ERROR_TOAST_PROPS } from '../util/util'; import { ERROR_TOAST_PROPS } from '../util/util';
import LinkingConfiguration from './LinkingConfiguration'; import LinkingConfiguration from './LinkingConfiguration';
import Small_logo from '../assets/images/Logo_small.jpeg'
export default function Navigation({ colorScheme }) { export default function Navigation({ colorScheme }) {
return ( return (
<NavigationContainer <NavigationContainer
linking={LinkingConfiguration} linking={LinkingConfiguration}
theme={colorScheme === 'dark' || true ? DarkTheme : DefaultTheme}> theme={colorScheme === 'dark' && false ? DarkTheme : DefaultTheme}>
<RootNavigator /> <RootNavigator />
</NavigationContainer> </NavigationContainer>
); );
...@@ -40,8 +44,9 @@ export default function Navigation({ colorScheme }) { ...@@ -40,8 +44,9 @@ export default function Navigation({ colorScheme }) {
const Stack = createNativeStackNavigator(); const Stack = createNativeStackNavigator();
function RootNavigator() { function RootNavigator() {
const [isAuthenticated, setIsAutheticated] = React.useState(false)
const [isAuthenticated, setIsAutheticated] = React.useState(true) isLoggedIn().then((res) => setIsAutheticated(true))
// console.log({ isLogged })
const onLogin = async (username, password) => { const onLogin = async (username, password) => {
await login(username, password).then(() => setIsAutheticated(true)).catch(e => { await login(username, password).then(() => setIsAutheticated(true)).catch(e => {
...@@ -51,14 +56,16 @@ function RootNavigator() { ...@@ -51,14 +56,16 @@ function RootNavigator() {
} }
const onLogout = () => { const onLogout = () => {
logout()
setIsAutheticated(false) setIsAutheticated(false)
} }
return ( return (
<Stack.Navigator > <Stack.Navigator >
{isAuthenticated ? <Stack.Screen name="Root" options={{ headerShown: false }}>{(props) => <BottomTabNavigator {...props} onLogout={onLogout} />}</Stack.Screen> : {isAuthenticated ? <Stack.Screen name="Root" options={{ headerShown: false }}>{(props) => <BottomTabNavigator {...props} onLogout={onLogout} />}</Stack.Screen> :
<Stack.Screen name='Root'>{(props) => <LoginScreen {...props} onLogin={onLogin} />}</Stack.Screen>} <Stack.Screen name='Root' options={{ headerShown: false }}>{(props) => <LoginScreen {...props} onLogin={onLogin} />}</Stack.Screen>}
<Stack.Screen name='Signup' component={SignupScreen} options={{ headerShown: false, title: 'Sign up' }} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} /> <Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
<Stack.Group screenOptions={{ presentation: 'modal' }}> <Stack.Group screenOptions={{ presentation: 'modal' }}>
...@@ -81,14 +88,25 @@ function BottomTabNavigator({ onLogout }) { ...@@ -81,14 +88,25 @@ function BottomTabNavigator({ onLogout }) {
<BottomTab.Navigator <BottomTab.Navigator
initialRouteName="TabOne" initialRouteName="TabOne"
screenOptions={{ screenOptions={{
tabBarActiveTintColor: Colors[colorScheme].tint, tabBarActiveBackgroundColor: 'lightgray',
tabBarActiveTintColor: 'blue' //Colors[colorScheme].tint,
}}> }}>
<BottomTab.Screen
name="TabTwo"
component={TabTwoScreen}
options={{
title: 'Map',
tabBarIcon: ({ color }) => <TabBarIcon name="map" color={color} />,
}}
/>
<BottomTab.Screen <BottomTab.Screen
name="TabOne" name="TabOne"
component={ChatScreen} component={ChatScreen}
options={({ navigation }) => ({ options={({ navigation }) => ({
title: 'Chat', title: 'Chat',
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />, headerTitleAlign: 'center',
tabBarIcon: ({ color }) => <TabBarIcon name="comments" color={color} />,
headerLeft: () => (<Image source={Small_logo} style={{height: 40, width: 70, marginLeft: 20}}/>),
headerRight: () => ( headerRight: () => (
<Pressable <Pressable
// onPress={() => navigation.navigate('Modal')} // onPress={() => navigation.navigate('Modal')}
...@@ -99,21 +117,14 @@ function BottomTabNavigator({ onLogout }) { ...@@ -99,21 +117,14 @@ function BottomTabNavigator({ onLogout }) {
<FontAwesome <FontAwesome
name="info-circle" name="info-circle"
size={25} size={25}
color={Colors[colorScheme].text} // color={Colors[colorScheme].text}
style={{ marginRight: 15 }} style={{ marginRight: 15 }}
/> />
</Pressable> </Pressable>
), ),
})} })}
/> />
<BottomTab.Screen
name="TabTwo"
component={TabTwoScreen}
options={{
title: 'Tab Two',
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
}}
/>
</BottomTab.Navigator> </BottomTab.Navigator>
); );
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
"dependencies": { "dependencies": {
"@expo/vector-icons": "^12.0.0", "@expo/vector-icons": "^12.0.0",
"@react-native-async-storage/async-storage": "~1.15.0", "@react-native-async-storage/async-storage": "~1.15.0",
"@react-native-community/slider": "4.1.12",
"@react-navigation/bottom-tabs": "^6.0.5", "@react-navigation/bottom-tabs": "^6.0.5",
"@react-navigation/native": "^6.0.2", "@react-navigation/native": "^6.0.2",
"@react-navigation/native-stack": "^6.1.0", "@react-navigation/native-stack": "^6.1.0",
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
"expo-font": "~10.0.4", "expo-font": "~10.0.4",
"expo-linking": "~3.0.0", "expo-linking": "~3.0.0",
"expo-media-library": "~14.0.0", "expo-media-library": "~14.0.0",
"expo-speech": "~10.1.0",
"expo-splash-screen": "~0.14.0", "expo-splash-screen": "~0.14.0",
"expo-status-bar": "~1.2.0", "expo-status-bar": "~1.2.0",
"expo-web-browser": "~10.1.0", "expo-web-browser": "~10.1.0",
......
import React from "react";
import { View, Button, Text } from 'react-native'
import { styles } from "../util/styles";
export const SignupScreen = ({ navigation }) => {
return (
<View style={styles.container}><Text style={{
padding: 10,
textAlign: 'center',
// fontWeight: 'bold',
fontSize: 40
}}>Sign up</Text>
<View style={{ flexDirection: 'row', padding: 10, justifyContent: 'center' }}>
<Text style={{ textAlign: 'center' }}>Already have an account? </Text>
<Text style={{ fontWeight: 'bold', textAlign: 'center', }} onPress={() => navigation.replace('Root')}>Login</Text>
</View>
<SignupForm />
</View>
)
}
const SignupForm = () => {
return(
<View>
</View>
)
}
\ No newline at end of file
This diff is collapsed.
import React, { useState } from 'react' import React, { useState } from 'react'
import { StyleSheet, TextInput } from 'react-native'; import { StyleSheet, TextInput, Button, Image, Dimensions, View, Text } from 'react-native';
import EditScreenInfo from '../components/EditScreenInfo'; import EditScreenInfo from '../components/EditScreenInfo';
import { Text, View } from '../components/Themed'; // import { Text, View } from '../components/Themed';
import { TouchableOpacity } from 'react-native'; import { TouchableOpacity } from 'react-native';
import { Toast } from 'native-base'; import { Toast } from 'native-base';
import { ERROR_TOAST_PROPS } from '../util/util'; import { ERROR_TOAST_PROPS } from '../util/util';
import { screenWidth, styles } from '../util/styles';
import TTS_logo from '../assets/images/TTS_logo.jpeg'
const LoginForm = ({ onLogin }) => { const LoginForm = ({ onLogin, navigation }) => {
var passwordInput var passwordInput
...@@ -20,14 +22,16 @@ const LoginForm = ({ onLogin }) => { ...@@ -20,14 +22,16 @@ const LoginForm = ({ onLogin }) => {
defaultValue={username} defaultValue={username}
onChangeText={(e) => { onChangeText={(e) => {
console.log(e); console.log(e);
setUsername(e)}} setUsername(e)
}}
autoCapitalize="none" autoCapitalize="none"
onSubmitEditing={() => passwordInput.focus()} onSubmitEditing={() => passwordInput.focus()}
autoCorrect={false} autoCorrect={false}
keyboardType='email-address' keyboardType='email-address'
returnKeyType="next" returnKeyType="next"
placeholder='Email' placeholder='Email'
placeholderTextColor='rgba(225,225,225,0.7)' /> // placeholderTextColor='rgba(225,225,225,0.7)'
/>
<TextInput style={styles.input} <TextInput style={styles.input}
defaultValue={password} defaultValue={password}
...@@ -35,7 +39,7 @@ const LoginForm = ({ onLogin }) => { ...@@ -35,7 +39,7 @@ const LoginForm = ({ onLogin }) => {
returnKeyType="go" returnKeyType="go"
ref={(input) => passwordInput = input} ref={(input) => passwordInput = input}
placeholder='Password' placeholder='Password'
placeholderTextColor='rgba(225,225,225,0.7)' // placeholderTextColor='rgba(225,225,225,0.7)'
secureTextEntry /> secureTextEntry />
<TouchableOpacity style={styles.buttonContainer} <TouchableOpacity style={styles.buttonContainer}
...@@ -43,12 +47,12 @@ const LoginForm = ({ onLogin }) => { ...@@ -43,12 +47,12 @@ const LoginForm = ({ onLogin }) => {
// disabled={!username || !password} // disabled={!username || !password}
onPress={() => { onPress={() => {
console.log({ username, password }) console.log({ username, password })
if(!username || !password){ if (!username || !password) {
Toast.show({title: 'Please fill in all the fields!', ...ERROR_TOAST_PROPS}) Toast.show({ title: 'Please fill in all the fields!', ...ERROR_TOAST_PROPS })
}else{ } else {
onLogin(username, password) onLogin(username, password)
} }
}} }}
> >
<Text style={styles.buttonText}>LOGIN</Text> <Text style={styles.buttonText}>LOGIN</Text>
...@@ -58,11 +62,24 @@ const LoginForm = ({ onLogin }) => { ...@@ -58,11 +62,24 @@ const LoginForm = ({ onLogin }) => {
) )
} }
export const LoginScreen = ({ onLogin }) => { export const LoginScreen = ({ onLogin, navigation }) => {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<View style={{ alignContent: 'center', justifyContent: 'center' }}>
<Image source={TTS_logo} style={{ height: screenWidth - 30, width: screenWidth - 30, margin: 'auto' }} />
</View>
<Text style={{
padding: 10,
textAlign: 'center',
// fontWeight: 'bold',
fontSize: 40
}}>Login</Text>
<View style={{ flexDirection: 'row', padding: 10, justifyContent: 'center' }}>
<Text style={{ textAlign: 'center' }}>Don't have an account? </Text>
<Text style={{ fontWeight: 'bold', textAlign: 'center', }} onPress={() => navigation.replace('Signup')}>Sign up</Text>
</View>
<View style={styles.loginContainer}> <View style={styles.loginContainer}>
{/* r<Image resizeMode="contain" style={styles.logo} source={require('../../components/images/logo-dark-bg.png')} /> */} {/* r<Image resizeMode="contain" style={styles.logo} source={require('../../components/images/logo-dark-bg.png')} /> */}
</View> </View>
...@@ -73,44 +90,4 @@ export const LoginScreen = ({ onLogin }) => { ...@@ -73,44 +90,4 @@ export const LoginScreen = ({ onLogin }) => {
</View> </View>
); );
} }
\ No newline at end of file
// define your styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#2c3e50',
},
loginContainer: {
alignItems: 'center',
flexGrow: 1,
justifyContent: 'center'
},
logo: {
position: 'absolute',
width: 300,
height: 100
},
container: {
padding: 20
},
input: {
height: 40,
backgroundColor: 'rgba(225,225,225,0.2)',
marginBottom: 10,
padding: 10,
color: '#fff'
},
buttonContainer: {
backgroundColor: '#2980b6',
paddingVertical: 15
},
buttonText: {
color: '#fff',
textAlign: 'center',
fontWeight: '700'
}
})
\ No newline at end of file
import { StyleSheet, Dimensions } from "react-native"
export const screenWidth = Dimensions.get('window').width
export const styles = StyleSheet.create({
// container: {
// flex: 1,
// backgroundColor: '#2c3e50',
// },
loginContainer: {
alignItems: 'center',
flexGrow: 1,
justifyContent: 'center'
},
logo: {
position: 'absolute',
width: 300,
height: 100
},
container: {
padding: 20,
backgroundColor: 'white'
},
input: {
height: 40,
backgroundColor: 'rgba(225,225,225,0.2)',
marginBottom: 10,
padding: 10,
// color: '#fff',
borderRadius: 5,
},
buttonContainer: {
backgroundColor: '#2980b6',
paddingVertical: 15,
borderRadius: 5
},
buttonText: {
// color: '#fff',
textAlign: 'center',
fontWeight: '700'
},
chatIcon: {
paddingHorizontal: 4
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontSize: 20,
fontWeight: 'bold',
},
separator: {
marginVertical: 30,
height: 1,
width: '80%',
},
convoFooter: {
},
header: { textAlign: 'center', backgroundColor: 'gray', marginRight: 'auto', marginLeft: 'auto', padding: 3, borderRadius: 5 }
})
\ No newline at end of file
export const TOAST_PROPS = {placement : 'top'} export const TOAST_PROPS = {placement : 'top'}
export const ERROR_TOAST_PROPS = {...TOAST_PROPS, style:{backgroundColor: 'red'}} export const ERROR_TOAST_PROPS = {...TOAST_PROPS, style:{backgroundColor: '#B00020'}}
\ No newline at end of file \ No newline at end of file
...@@ -2159,6 +2159,11 @@ ...@@ -2159,6 +2159,11 @@
sudo-prompt "^9.0.0" sudo-prompt "^9.0.0"
wcwidth "^1.0.1" wcwidth "^1.0.1"
"@react-native-community/slider@4.1.12":
version "4.1.12"
resolved "https://registry.yarnpkg.com/@react-native-community/slider/-/slider-4.1.12.tgz#279ff7bbe487af92ad95ec758a029a54569e2a62"
integrity sha512-CiuLZ2orueBiWHYxfaJF57jQY6HY2Q3z5pdAE4MKH8EqIImr/jgDJrJ/UxOVZHK1Ng9P+XlGIKfVIcuWZ6guuA==
"@react-native/assets@1.0.0": "@react-native/assets@1.0.0":
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
...@@ -4221,6 +4226,11 @@ expo-modules-core@0.6.5: ...@@ -4221,6 +4226,11 @@ expo-modules-core@0.6.5:
compare-versions "^3.4.0" compare-versions "^3.4.0"
invariant "^2.2.4" invariant "^2.2.4"
expo-speech@~10.1.0:
version "10.1.1"
resolved "https://registry.yarnpkg.com/expo-speech/-/expo-speech-10.1.1.tgz#fc4a820fd1bd6203c388837ab3947e24242c3615"
integrity sha512-tovcfD3Qi5WvY91d+N+dMVDMz+GwW/c9ze/Ct3cZQLBJQubqlVtlDqb/cDD2Uwuwb+zjAH3N6NfAFSgCdhqwAQ==
expo-splash-screen@~0.14.0: expo-splash-screen@~0.14.0:
version "0.14.2" version "0.14.2"
resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.14.2.tgz#2598d6980e71ecd8b7467ca821fb9dbfb80f355b" resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.14.2.tgz#2598d6980e71ecd8b7467ca821fb9dbfb80f355b"
......
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