perform ugly sign in
continuous-integration/drone/push Build is passing Details

pull/8/head
David D'ALMEIDA 2 years ago
parent 0369630fc0
commit 897cbaa6a3

@ -19,7 +19,12 @@
"**/*"
],
"ios": {
"supportsTablet": true
"supportsTablet": true,
"infoPlist": {
"UIBackgroundModes": [
"audio"
]
}
},
"android": {
"adaptiveIcon": {

@ -40,8 +40,8 @@ const Card = ({ title, image, onSwipe } : CardProps) => {
},
onEnd : (event, context) => {
console.log(translateX.value);
console.log(translateX.value - translateY.value);
// console.log(translateY.value);
// translateX.value = withSpring(0);
// translateY.value = withSpring(snapPoint(translateY.value,velocityY, snapPoints ))
if (translateX.value > 160) {
@ -82,22 +82,26 @@ const Card = ({ title, image, onSwipe } : CardProps) => {
const opacCStyle = useAnimatedStyle(() => {
const opacityl = interpolate
( translateX.value,
[-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4],
[-SCREEN_WIDTH / 4, 0, SCREEN_WIDTH / 4],
[0.35, 0, 0.35]);
return {
opacity : opacityl,
};
});
const opacDStyle = useAnimatedStyle(() => {
const opacityl = interpolate
( translateY.value,
[-SCREEN_HEIGHT / 4, 0, SCREEN_HEIGHT / 2],
[0, 0, 1]);
[ 100, 300],
[ 0, 1]);
return {
backgroundColor : 'red',
opacity : opacityl,
};
});
const horizontalThreshold = SCREEN_WIDTH * 0.65;
const rotateStyle = useAnimatedStyle(() => {
@ -148,14 +152,14 @@ const horizontalThreshold = SCREEN_WIDTH * 0.65;
],
};
});
console.log('==========================================',SCREEN_WIDTH/4 , "===============================");
return (
<View>
<PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View style={[ styleCardsNew, styles.container]}>
<Animated.View style={[ styles.image,{backgroundColor: 'black',elevation: 100,
position: "absolute",
position: "absolute",borderWidth : 8, borderColor : '#DA1D1D',
zIndex: 1000,}, opacCStyle]}>
</Animated.View>
<Image source={{uri : image}} style={[styles.image]} />
@ -163,6 +167,7 @@ const horizontalThreshold = SCREEN_WIDTH * 0.65;
<Animated.View
style={[{
// transform: [{ rotate: "30deg" }],
elevation: 100,
position: "absolute",
zIndex: 1000,

@ -0,0 +1,76 @@
import { useState } from 'react';
import { View, Text, Image ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TextInput } from 'react-native'
import Animated, { interpolate, lessThan, multiply, useAnimatedStyle } from 'react-native-reanimated';
import HalfCirlce from './HalfCircle';
interface CircularProps {
background : string,
foreground : string,
progress : Animated.Value<number>,
radius : number;
}
const PI= Math.PI;
const FladInput = ({background, foreground, progress} : CircularProps) => {
const [focused, setFocused] = useState<boolean>(false);
const theta = multiply(progress,2*PI);
const rotateTop = theta;
const opacity = lessThan(theta, PI);
const rotateAnimation = useAnimatedStyle(() => {
const rotate = interpolate
( theta,
[PI, 2*PI],
[0,PI]);
return {
...StyleSheet.absoluteFillObject,
transform: [
{rotate: rotate},
{translateX: RADUIS/2},
{translateY: RADUIS/2}
],
};
});
const rotateAnimation2 = useAnimatedStyle(() => {
const rotate = interpolate
( theta,
[PI, 2*PI],
[0,PI]);
return {
...StyleSheet.absoluteFillObject,
transform: [
{rotate: theta},
{translateX: RADUIS/2},
{translateY: RADUIS/2}
],
};
});
return (
<>
<View style={{zIndex : 1}}>
<HalfCirlce backgroundColor={background}/>
<Animated.View style={{...StyleSheet.absoluteFillObject, transform : [{rotate : '180%'}], opacity}}>
<HalfCirlce backgroundColor={background}/>
</Animated.View>
</View>
<View style={{ transform : [{rotate : '180%'}]}}>
<HalfCirlce backgroundColor={background}/>
<Animated.View style={{}}>
<HalfCirlce backgroundColor={background}/>
</Animated.View> </View>
</>
);
};
const styles = StyleSheet.create({
input : {
justifyContent : 'center',
alignItems : 'center',
placeholder : "placeholde"
},
})
export default FladInput;

@ -79,7 +79,7 @@ const FladLoading = () => {
<View style={{flex : 1, justifyContent : 'center', alignItems :'center'}}>
<Animated.View style={[{backgroundColor : '#B40404', justifyContent : 'center', alignItems : 'center'}, breatheStyleSquare]}>
<Animated.Image source={require('../assets/icons/icon.png')} style={[{height: size, width : size, borderColor : '#fff',borderRadius : size/2, borderWidth : 3}]}/>
<Animated.Image source={require('../assets/icons/icon.png')} style={[{height: size, width : size, borderColor : '#fff',borderRadius : size/2}]}/>
</Animated.View>
{/* <Animated.View style={[ {backgroundColor : 'green'},breatheStyleSquare]}>
</Animated.View> */}

@ -0,0 +1,33 @@
import { useState } from 'react';
import { View, Text, Image, Animated ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TextInput } from 'react-native'
interface HalfCirlceProps {
backgroundColor : string;
}
const HalfCirlce = ({backgroundColor} : HalfCirlceProps) => {
const [focused, setFocused] = useState<boolean>(false);
return (
<View style={{
width : RADUIS* 2,
height : RADUIS* 2,
overflow : "hidden",
}}>
<View style={{backgroundColor : backgroundColor, width : RADUIS* 2, height : RADUIS * 2, borderRadius : RADUIS, }}>
</View>
</View>
);
};
const styles = StyleSheet.create({
input : {
justifyContent : 'center',
alignItems : 'center',
placeholder : "placeholde"
},
})
export default HalfCirlce;

@ -19,6 +19,8 @@
"buffer": "^6.0.3",
"expo": "~47.0.12",
"expo-auth-session": "~3.8.0",
"expo-av": "~13.0.3",
"expo-blur": "~12.0.1",
"expo-cli": "^6.3.0",
"expo-haptics": "~12.0.1",
"expo-linear-gradient": "~12.0.1",
@ -43,8 +45,7 @@
"react-navigation-shared-element": "^3.1.3",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
"rive-react-native": "^3.0.41",
"expo-blur": "~12.0.1"
"rive-react-native": "^3.0.41"
},
"devDependencies": {
"@babel/core": "^7.12.9",

@ -5,6 +5,13 @@ export interface Credentials {
email : string,
password : string
}
export interface CredentialsRegister {
email : string,
password : string,
name : string,
idFlad : string,
idSpotify : string
}
// export const setLoggedInState = loggedInState => (
// {
// type: types.SET_LOGGED_IN_STATE,

@ -4,15 +4,17 @@ import axios from "axios";
import { json } from "express";
import { useEffect } from "react";
import { API_URL } from "../../fladConfig";
import { Credentials, restoreToken, setLoginState } from "../actions/userActions";
import { Credentials, CredentialsRegister, restoreToken, setLoginState } from "../actions/userActions";
import * as SecureStore from 'expo-secure-store';
const key = 'userToken';
export const registerUser = ( resgisterCredential : any) => {
export const registerUser = ( resgisterCredential : CredentialsRegister) => {
//@ts-ignore
return async dispatch => {
try {
console.log(resgisterCredential);
const config = {
headers: {
'Content-Type': 'application/json',
@ -23,13 +25,32 @@ export const registerUser = ( resgisterCredential : any) => {
resgisterCredential,
config
)
if (resp.data.msg === 'success') { // response success checking logic could differ
await SecureStore.setItemAsync(key, resp.data.token);
if (resp.data.token) {
console.log(resp.data.token);
const token = resp.data.token;
// await SecureStore.setItemAsync(key, token);
const headers = {
'Authorization': 'Bearer ' + token};
const user = await axios.get(
"https://flad-api-production.up.railway.app/api/users",
{headers}
)
dispatch(setLoginState(resp.data.user) ); // our action is called here
// console.log(user.data);
// dispatch(setLoginState(user.data) ); // our action is called here
} else {
console.log('Login Failed', 'Username or Password is incorrect');
}
// if (resp.data.msg === 'success') { // response success checking logic could differ
// await SecureStore.setItemAsync(key, resp.data.token);
// dispatch(setLoginState(resp.data.user) ); // our action is called here
// } else {
// console.log('Login Failed', 'Username or Password is incorrect');
// }
} catch (error) {
console.log('Error---------', error);
}

@ -7,6 +7,11 @@ import * as AuthSession from 'expo-auth-session';
import axios from 'axios';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { registerUser } from '../redux/thunk/authThunk';
import { useDispatch } from 'react-redux';
import { CredentialsRegister } from '../redux/actions/userActions';
import { Buffer } from 'buffer';
// @ts-ignore
const DismissKeyboard = ({ children }) => (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
@ -32,10 +37,16 @@ await SecureStore.setItemAsync(key, value);
export default function InscriptionPage() {
const [rememberMe, setRememberMe] = useState(false);
const navigation = useNavigation();
const [spotifyToken, setSpotifyToken] = useState('');
const [spotifyID, setSpotifyIds] = useState('')
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const toggleRememberMe = () => {
setRememberMe(!rememberMe);
}
//spotify auth
const [request, response, promptAsync] = useAuthRequest(
{
@ -60,6 +71,68 @@ export default function InscriptionPage() {
}
}, [response]);
const dispatch = useDispatch();
const submitForm = () => {
const credentials: CredentialsRegister = {
email: email,
password: password,
idSpotify : spotifyToken,
name : username,
idFlad : "3030"
};
//@ts-ignore
dispatch(registerUser(credentials))
}
const scopesArr = ['user-read-private','user-read-email','user-read-playback-state','user-read-currently-playing','user-read-recently-played','playlist-modify-public','ugc-image-upload','user-modify-playback-state'];
const scopes = scopesArr.join(' ');
//work so use this for my implementation
const getAuthorizationCode = async () => {
try {
const redirectUrl = "https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d"; //this will be something like https://auth.expo.io/@your-username/your-app-slug
const result = await AuthSession.startAsync({
authUrl:
'https://accounts.spotify.com/authorize' +
'?response_type=code' +
'&client_id=' +
"1f1e34e4b6ba48b388469dba80202b10" +
(scopes ? '&scope=' + encodeURIComponent(scopes) : '') +
'&redirect_uri=' +
encodeURIComponent(redirectUrl),
})
console.log(result);
return result.params.code;
} catch (err) {
console.error(err)
}
}
const getTokens = async () => {
try {
const authorizationCode = await getAuthorizationCode() //we wrote this function above
console.log(authorizationCode, "shhhhhhhhhhhhhheeeeeeeeeeeeeeeetttttttttttt");
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
Authorization: 'Basic ' + (Buffer.from('1f1e34e4b6ba48b388469dba80202b10' + ':' + '779371c6d4994a68b8dd6e84b0873c82').toString('base64')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `grant_type=authorization_code&code=${authorizationCode}&redirect_uri=https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d`,
});
const responseJson = await response.json();
console.log(responseJson, "okkkkkkkkkkkkkkk") ;
// destructure the response and rename the properties to be in camelCase to satisfy my linter ;)
const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
} = responseJson;
await setSpotifyToken(accessToken);
console.log(spotifyToken);
} catch (err) {
console.error(err);
}
}
return (
<DismissKeyboard>
<View style={styles.container}>
@ -70,24 +143,31 @@ export default function InscriptionPage() {
<Image source={require("../assets/icons/Logo_White_Flad.png")} style={styles.imageLogo}/>
<Text style={styles.text}>S'INSCRIRE</Text>
<View>
<TextInput style={[styles.input, styles.shadow]}/>
<TextInput style={[styles.input, styles.shadow]} placeholder="Username"
value={username}
onChangeText={setUsername}/>
<Image source={require('../assets/icons/icons/User.png')} style={styles.iconUser} />
</View>
<View>
<TextInput style={[styles.input, styles.shadow]}/>
<TextInput style={[styles.input, styles.shadow]} placeholder="Email"
value={email}
onChangeText={setEmail}/>
<Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} />
</View>
<View>
<TextInput style={[styles.input, styles.shadow]}/>
<TextInput style={[styles.input, styles.shadow]} placeholder="Password"
value={password} secureTextEntry={true}
onChangeText={setPassword}/>
<Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} />
</View>
<TouchableOpacity onPress={() => {
promptAsync();
<TouchableOpacity onPress={async() => {
await getTokens();
}} style={[styles.buttonSpotify, styles.shadow]}>
<Text style={styles.textIntoButton}>Lier compte</Text>
<Image source={require("../assets/icons/icons/Spotify.png")} style={{width: 30, height: 30}}/>
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.shadow]} onPress={() => console.log("Oui")}>
<TouchableOpacity style={[styles.button, styles.shadow]} onPress={() => submitForm()}>
<Image source={require("../assets/icons/icons/next.png")} style={styles.buttonImage}/>
</TouchableOpacity>
<View style={styles.connectionText}>

@ -1,8 +1,14 @@
import { SharedElement } from "react-navigation-shared-element";
import { NavigationProp, RouteProp } from "@react-navigation/native";
import { View,Text,Image,StyleSheet, Dimensions, useWindowDimensions } from "react-native";
import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, withSpring, withTiming } from "react-native-reanimated";
import { View,Text,Image,StyleSheet, Dimensions, useWindowDimensions, Button, TouchableOpacity } from "react-native";
import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, useDerivedValue, useSharedValue, Value, withSpring, withTiming } from "react-native-reanimated";
import { BlurView } from 'expo-blur';
import qs from "qs";
import axios from "axios";
import { Buffer } from 'buffer';
import { Audio } from 'expo-av';
import { useEffect, useState } from "react";
import { State, TapGestureHandler } from "react-native-gesture-handler";
interface SpotProps {
spot: { name: string, sourceUrl: string, index : number };
@ -13,10 +19,100 @@ const halfPi = Math.PI/2;
//@ts-ignore
const SpotDetailsPage = ({ route }) => {
const {width, height} = useWindowDimensions();
console.log("====================================");
console.log(route);
const spot : { name: string, sourceUrl: string, index : number } = route.params.spot;
const [currentspot, setCurrentspot] = useState(spot);
const [sound, setSound] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const loader = useSharedValue(0);
useEffect(() => {
loader.value = isPlaying ? 1 : 0
}, [isPlaying,loader ]);
const transition = useDerivedValue(()=>{
return withTiming(loader.value, {duration : 1000})
}
)
// const styleAniamatedButton = useAnimatedStyle(() => {
// const verticalAxis =interpolate(
// transition.value,
// [0,1],
// [circumference, 0]
// )
// return {
// top : withSpring( verticalAxis),
// left : withSpring(horizontalAxis),
// };
// })
const trackPreviewUrl = 'https://p.scdn.co/mp3-preview/08ef3b9d6dbd6bab233f5e9ca564091902767f71?cid=774b29d4f13844c495f206cafdad9c86';
const playTrackPreview = async () => {
console.log("===============================================================================================================");
console.log('get in Sound');
const { sound } = await Audio.Sound.createAsync({uri :trackPreviewUrl});
//@ts-ignore
setSound(sound);
console.log('Playing Sound');
await sound.playAsync();
setIsPlaying(true);
// const soundObject = new Audio.Sound();
// try {
// await soundObject.loadAsync({ uri: trackPreviewUrl });
// await soundObject.playAsync();
// setIsPlaying(true);
// } catch (error) {
// console.log('Error loading sound:', error);
// }
};
const handlePlaySound = async () => {
if (sound === null) {
const { sound: newSound } = await Audio.Sound.createAsync(
{ uri: trackPreviewUrl },
{ shouldPlay: true }
);
setSound(newSound);
} else {
//@ts-ignore
await sound.playAsync();
}
};
const handleStopSound = async () => {
if (sound !== null) {
//@ts-ignore
await sound.stopAsync();
}
else{
setIsPlaying(true);
}
};
useEffect(() => {
return sound ? () => {
console.log('Unloading Sound');
//@ts-ignore
sound.unloadAsync();
}
: undefined;
}, [sound]);
// useEffect(() => {
// if(isPlaying){
// }
// })
console.log(spot);
const sensor = useAnimatedSensor(SensorType.ROTATION);
const styleAniamatedImage = useAnimatedStyle(() => {
@ -24,12 +120,12 @@ const SpotDetailsPage = ({ route }) => {
const verticalAxis =interpolate(
pitch,
[-halfPi,halfPi],
[-30, 30]
[-45, 45]
)
const horizontalAxis =interpolate(
roll,
[-halfPi*2,halfPi*2],
[-30, 30]
[-45, 45]
)
return {
top : withSpring( verticalAxis),
@ -37,12 +133,90 @@ const SpotDetailsPage = ({ route }) => {
};
})
const CLIENT_ID = "1f1e34e4b6ba48b388469dba80202b10";
const CLIENT_SECRET = "779371c6d4994a68b8dd6e84b0873c82";
const spotify = "BQA2IAFZ-7ta4-_4_Uqdcdrqi_peE6Hlf1jwxFqjXTbwes0z8xgVGx0rE3zv4cQlusd1ILJhRwkxzPsL1YakzSvCxaTI1P7kOzBrrMqlkDgk4vlFvzLjScB0hBLULbpZyn3ylgx4RyZBEWfmc24wZPQOsrJU58AYCveA52UxYVSIc_Frr7LZyRmwjzGB68MPZeBD"
var authOptions = {
method: 'GET',
url: 'https://api.spotify.com/v1/me/player/currently-playing',
headers: {
'Authorization': 'Bearer ' + spotify,
'Content-Type' : 'application/json',
'market' : 'FR',
},
json: true
};
var id = '0cFS3AMF9Lhj3CNoFvwjvY'
const getCurrentTrack = async () => {
try {
var GetTrackOptions = {
method: 'GET',
url: 'https://api.spotify.com/v1/tracks/'+id,
headers: {
'Authorization': 'Bearer ' + spotify,
'Content-Type' : 'application/json',
'market' : 'FR',
},
json: true
};
const resp = await axios(GetTrackOptions)
console.log("============");
console.log(resp.data.href);
console.log("================================"+resp.data.album.images[0].url+ "================================");
var tmp = currentspot;
tmp.sourceUrl = resp.data.album.images[0].url;
setCurrentspot(tmp);
// await axios(authOptions).then(async (response) =>{
// console.log(response.data.item.preview_url);
// const id = response.data.item.id;
// var GetTrackOptions = {
// method: 'GET',
// url: 'https://api.spotify.com/v1/tracks/'+id,
// headers: {
// 'Authorization': 'Bearer ' + spotify,
// 'Content-Type' : 'application/json',
// 'market' : 'FR',
// },
// json: true
// };
// console.log("============");
// const music = await axios(GetTrackOptions);
// console.log("================================"+music.data+ "================================");
// currentspot.sourceUrl = music.data.images[0];
// setCurrentspot(currentspot);
// })
// const response = await fetch('https://api.spotify.com/v1/me', {
// method: 'GET',
// headers: {
// Authorization: 'Bearer ' + spotify,
// 'Content-Type': 'application/json',
// },
// });
// response.json()
// destructure the response and rename the properties to be in camelCase to satisfy my linter ;)
} catch (err) {
console.error(err);
}
}
const animationState = new Value(State.UNDETERMINED);
return (
<View style={{ flex: 1, justifyContent : 'flex-start', alignItems : 'center' }}>
{/* <SharedElement id={spot.name} style={{ flex: 1 }}> */}
<View style={{borderWidth : 1, borderColor : 'red'}}>
<Animated.Image
source={{
uri:spot.sourceUrl ,
uri:currentspot.sourceUrl ,
}}
style={[
{
@ -52,8 +226,45 @@ const SpotDetailsPage = ({ route }) => {
borderRadius : 24,
resizeMode: 'stretch',
},styleAniamatedImage
]}
/>
<Button title="Current Track"
onPress={() => {
getCurrentTrack()
// promptAsync();
}}
/>
</View>
{/* Button */}
{/* <TapGestureHandler {...gestureHandler}> */}
<Animated.View>
<TouchableOpacity style={{
backgroundColor: '#1DB954',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 24,
}} onPressIn={handlePlaySound}
onPressOut={handleStopSound}
onLongPress={handlePlaySound}
delayLongPress={1000}>
<Text style={ {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',}}>
{isPlaying ? 'Playing...' : 'Play'}
</Text>
</TouchableOpacity>
</Animated.View>
{/* </TapGestureHandler> */}
{/* Button */}
{/* <View style={detailRadicalStyle.container}>
<Text style={detailRadicalStyle.radicalText}>{props.character}</Text>

@ -110,6 +110,7 @@ const scopes = scopesArr.join(' ');
encodeURIComponent(redirectUrl),
})
console.log(result);
return result.params.code;
} catch (err) {
console.error(err)
}
@ -117,6 +118,7 @@ const scopes = scopesArr.join(' ');
const getTokens = async () => {
try {
const authorizationCode = await getAuthorizationCode() //we wrote this function above
console.log(authorizationCode, "shhhhhhhhhhhhhheeeeeeeeeeeeeeeetttttttttttt");
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
@ -126,6 +128,7 @@ const scopes = scopesArr.join(' ');
body: `grant_type=authorization_code&code=${authorizationCode}&redirect_uri=https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d`,
});
const responseJson = await response.json();
console.log(responseJson.access_token, "okkkkkkkkkkkkkkk") ;
// destructure the response and rename the properties to be in camelCase to satisfy my linter ;)
const {
access_token: accessToken,
@ -133,7 +136,6 @@ const scopes = scopesArr.join(' ');
expires_in: expiresIn,
} = responseJson;
console.log(responseJson);
} catch (err) {
console.error(err);
}

@ -1,4 +1,4 @@
import { View, Text, Image ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TouchableOpacity } from 'react-native'
import { View, Text, Image ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TouchableOpacity, SafeAreaView } from 'react-native'
import React, { useCallback, useEffect, useRef, useState, useTransition } from 'react'
import { LinearGradient } from 'expo-linear-gradient';
import * as Haptics from 'expo-haptics';
@ -191,6 +191,7 @@ export default function Spot() {
uri:currentCard.sourceUrl ,
}}
></ImageBackground>
<View style={{flex : 1.65}}>
<LinearGradient colors={['rgba(2, 2, 2, 0.58) 0%','rgba(0, 0, 0, 0) 90.56%']}style={styles.gradient}>
<Text
@ -250,7 +251,8 @@ export default function Spot() {
</View>
</View>
</>)
</>
)
: (<View style={{justifyContent : 'center', alignItems : 'center', flex : 1}}>
<View style={{flex:7}}>
@ -299,5 +301,9 @@ export default function Spot() {
right : 0,
height : 209,
},
mainSafeArea: {
flex: 1,
}
})

Loading…
Cancel
Save