react native and parse, currentuser is null after login and cmd+R - parse-platform

I'm having a problem with React Native and Parse JS SDK.
And I'm using ParseReact
I have built a login, sign up and a main view, Sign up and log in works well but after i logged in -> directed to main view and when I refresh the app (CMD+R) in my simulator, it brings me back to the login view again, i should be brought to Main view.
As you can see I have set a state for initialComponent:
this.state = {
InitialComponent : ((!currentUser) ? LoginView : MainView)
};
This allows my navigator to check for currentUser is null then load LoginView as initial component, else set Main View(user logged in)
'use strict';
var React = require('react-native');
var MainView = require('./MainView');
var LoginView = require('./LoginView');
var Parse = require('parse').Parse;
var ParseReact = require('parse-react');
Parse.initialize("mykey", "mykey");
var {
AppRegistry,
StyleSheet,
Text,
View,
TextInput,
TouchableHighlight,
Navigator,
Component
} = React;
class MyApp extends Component {
constructor(props) {
super(props);
var currentUser = Parse.User.current();
console.log('Current User:' + currentUser);
this.state = {
InitialComponent : ((!currentUser) ? LoginView : MainView)
};
}
render() {
return (
<Navigator
initialRoute={{
name : 'StatusList',
component: this.state.InitialComponent
}}
configureScene = {() =>{
return Navigator.SceneConfigs.FloatFromRight;
}}
renderScene={(route, navigator) =>{
if(route.component) {
return React.createElement(route.component, {navigator});
}
}}/>
);
}
}
AppRegistry.registerComponent('MyApp', function() { return MyApp });
In my Xcode console, i kept getting current user is null after each refresh even though i have previously logged in. On my parse app, I can see new session has been created.
In my LoginView.
'use strict';
var React = require('react-native');
var SignUp = require('./SignUp');
var MainView = require('./MainView');
var {
AppRegistry,
StyleSheet,
Text,
View,
TextInput,
TouchableHighlight,
Navigator,
AlertIOS,
Component
} = React;
var styles = StyleSheet.create({
container : {
flex: 1,
padding: 15,
marginTop: 30,
backgroundColor: '#0179D5',
},
text: {
color: '#000000',
fontSize: 30,
margin: 100
},
headingText: {
color: '#fff',
fontSize: 40,
fontWeight: '100',
alignSelf: 'center',
marginBottom: 20,
letterSpacing: 3
},
textBox: {
color: 'white',
backgroundColor: '#4BB0FC',
borderRadius: 5,
borderColor: 'transparent',
padding:10,
height:40,
borderWidth: 1,
marginBottom: 15,
},
greenBtn: {
height: 36,
padding: 10,
borderRadius: 5,
backgroundColor: '#2EA927',
justifyContent: 'center'
},
signUpButton: {
marginTop: 10,
height: 36,
padding: 10,
borderRadius: 5,
backgroundColor: '#FF5500',
justifyContent: 'center'
},
btnText: {
color : '#fff',
fontSize: 15,
alignSelf: 'center'
},
buttonText: {
fontSize: 18,
color: 'white',
alignSelf: 'center'
},
loginForm : {
flex:1,
marginTop:100
}
});
class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: ''
};
}
checkLogin() {
var success = true;
var state = this.state;
for(var key in state){
if(state[key].length <= 0){
success = false;
}
}
if(success) {
this._doLogin();
} else {
//show alert
AlertIOS.alert('Error','Please complete all fields',
[{text: 'Okay', onPress: () => console.log('')}]
);
}
}
goMainView() {
this.props.navigator.push({
title: "Home",
component: MainView
});
}
goSignUp() {
this.props.navigator.push({
title: "Sign Up",
component: SignUp
});
}
_doLogin() {
var parent = this;
Parse.User.logIn(this.state.username, this.state.password, {
success: function(user) {
parent.goMainView();
},
error: function(user, error) {
AlertIOS.alert('Login Error', error.message,
[{text: 'Okay', onPress: () => console.log('')}]
);
}
});
}
onUsernameChanged(event) {
this.setState({ username : event.nativeEvent.text });
}
onPasswordChanged(event) {
this.setState({ password : event.nativeEvent.text });
}
render() {
return(
<View style={styles.container}>
<View style={styles.loginForm}>
<Text style={styles.headingText}>
MyStatus
</Text>
<TextInput style={styles.textBox}
placeholder='Username'
onChange={this.onUsernameChanged.bind(this)}
placeholderTextColor='#fff'
autoCorrect={false}
>
</TextInput>
<TextInput style={styles.textBox}
placeholder='Password'
onChange={this.onPasswordChanged.bind(this)}
placeholderTextColor='#fff'
password={true}
>
</TextInput>
<TouchableHighlight style={styles.greenBtn}
underlayColor='#33B02C'
onPress={this.checkLogin.bind(this)}>
<Text style={styles.btnText}>Login</Text>
</TouchableHighlight>
<TouchableHighlight style={styles.signUpButton}
underlayColor='#D54700'
onPress={this.goSignUp.bind(this)}>
<Text style={styles.btnText}>Sign Up</Text>
</TouchableHighlight>
</View>
</View>
);
}
}
module.exports = LoginView;
Am I doing it the wrong way? Kindly advice. Or is it something wrong with parse localstorage/session?

Because React Native only provides asynchronous storage, we're forced to make Parse.User.current() an asynchronous call that returns a Promise. Since you're already using Parse+React, it's really easy to handle this. Your component that changes based upon whether the user is logged in or not (MyApp) should subscribe to ParseReact.currentUser. This stays synchronized with the current user, and allows your component to automatically update the moment the user logs in or out. For a demo of this, look at the AnyBudget demo in the GitHub repo:

In react-native...
import Parse from 'parse/react-native';
Parse.User.currentAsync()
.then((currentUser)=>{
if (currentUser) {
Alert.alert('', JSON.stringify(currentUser))
}
});

At first in my case I use ParseReact.Mutation that work perfectly.
After that I got a requirement that some field need to be unique, so I use Parse.Cloud to solve problem.
// For normally situaltion is work fine.
ParseReact.Mutation.Set(this.data.myObject, {
normal_field: this.state.value
}).dispatch();
// But if I use Parse.Cloud. Is there any solution to sync data to ParseReact.currentUser?
Parse.Cloud.run('setUniqueField', {
userID: this.data.user.id.objectId,
uniqueField: this.state.value,
}, {
success: function (response) {
// It doesn't work!
// ParseReact.currentUser.update();
this.props.navigator.pop();
}.bind(this),
error: function (error) {
console.log('[Parse Cloud Error]: ', error);
}
});

#Andrew's solution (alone) didn't work for me. In case anyone else is stuck, here's what was happening: subscribing to Parse.User.current() didn't work, and this.data.user was always null.
To fix this, I had to change:
var Parse = require('parse').Parse;
var ParseReact = require('parse-react');
to
var Parse = require('parse/react-native').Parse;
var ParseReact = require('parse-react/react-native');
then everything started working beautifully. It was loading the browser version of the parse modules until I forced it to load the react-native versions.

Related

React Native Camera is able to capture images but is NOT recording video

I am trying to use react-native-camera package to create an app to take pictures and record videos.
I took the example code and converted it to functional component, since its necessary for my app. I am able to take pictures and store in local cache, but function for recording video is not working, and not showing any output when I console.log it. Do I need to implement stop recording also? I am new to react native and and I didn't find reference for functional implementation anywhere properly. I am confused regarding this. Below is the code, with two buttons, one for picture and one for video
...
import { RNCamera } from "react-native-camera";
const App = () => {
let [flash, setFlash] = useState("off");
let [zoom, setZoom] = useState(0);
let [autoFocus, setAutoFocus] = useState("on");
let [depth, setDepth] = useState(0);
let [type, setType] = useState("back");
let [permission, setPermission] = useState("undetermined");
let [isRecording, setIsRecording] = useState("false");
let [recordingOptions, setRecordingOptions] = useState({
mute: false,
maxDuration: 10,
quality: RNCamera.Constants.VideoQuality["360p"],
});
let cameraRef = useRef(null);
useEffect(() => {
Permissions.check("photo").then((response) => {
// Response is one of: 'authorized', 'denied', 'restricted', or 'undetermined'
setPermission(response);
});
}, []);
const toggleFlash = () => {
setFlash(flashModeOrder[flash]);
};
const zoomOut = () => {
setZoom(zoom - 0.1 < 0 ? 0 : zoom - 0.1);
};
const zoomIn = () => {
setZoom(zoom + 0.1 > 1 ? 1 : zoom + 0.1);
};
const takePicture = async () => {
if (cameraRef) {
const options = { quality: 0.5, base64: true };
const data = await cameraRef.current.takePictureAsync(options);
console.log(data.uri);
}
};
const takeVideo = async () => {
if (cameraRef && !isRecording) {
try {
console.log(recordingOptions);
const recordoptions = {
mute: false,
maxDuration: 10,
quality: RNCamera.Constants.VideoQuality["360p"],
};
const promise = cameraRef.current.recordAsync(recordOptions);
if (promise) {
setIsRecording(true);
const data = await promise;
console.log("takeVideo", data.uri);
}
} catch (e) {
console.error(e);
}
}
};
return (
<View style={styles.container}>
<RNCamera
ref={cameraRef}
style={styles.preview}
type={type}
flashMode={flash}
/>
<View style={{ flex: 0, flexDirection: "row", justifyContent: "center" }}>
<TouchableOpacity onPress={takePicture} style={styles.capture}>
<Text style={{ fontSize: 14 }}> TAKE PICTURE </Text>
</TouchableOpacity>
</View>
<View style={{ flex: 0, flexDirection: "row", justifyContent: "center" }}>
<TouchableOpacity onPress={takeVideo} style={styles.capture}>
<Text style={{ fontSize: 14 }}> TAKE VIDEO </Text>
</TouchableOpacity>
</View>
</View>
);
};
export default App;
Refactor these lines
const promise = cameraRef.current.recordAsync(recordOptions);
if (promise) {
setIsRecording(true);
const data = await promise;
console.log("takeVideo", data.uri);
}
To
setIsRecording(true);
const data = await cameraRef.current.recordAsync(recordOptions);
console.log(data);

react native navigation - componentDidMount() fired twice

I am new to React Native. I am trying to build an app which has a Splash screen that would later navigate to Login screen if a user has not been authenticated or the Main screen if the user is authenticated. This is done using this.props.navigation.navigate()
The problem is that the Splash component would be mounted twice. I checked this by printing inside componentDidMount() of Splash. Because of this, the Login/Main screen enters twice, which looks very unpleasant. Is there any way to fix this?
Also, I want to add some delay when the screen changes from Splash to Login or Main using setTimeout(). Anyway to go about doing this?
Here's my code:
index.js
import React from 'react';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
import reduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import { StyleSheet } from 'react-native';
import LoginScreen from './src/components/Login/LoginScreen';
import Splash from './src/components/Login/Splash';
import Navigation from './src/components/Navigation/Navigation';
import { Font } from 'expo';
import {
createStackNavigator
} from 'react-navigation';
const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = createStoreWithMiddleware(reducers);
const persistor = persistStore(store);
export default class App extends React.Component {
constructor(props){
super(props);
this.state = {
fontLoaded: false,
currentScreen: 'Splash',
};
setTimeout(() => this.setState({currentScreen: 'Login'}), 2000);
}
async componentDidMount() {
await Font.loadAsync({
'Quicksand': require('./assets/fonts/Quicksand-Regular.ttf'),
'Quicksand-Medium': require('./assets/fonts/Quicksand-Medium.ttf'),
'Quicksand-Bold': require('./assets/fonts/Quicksand-Bold.ttf'),
});
this.setState({ fontLoaded: true });
}
render() {
const MainNavigator = createStackNavigator({
Splash: { screen: Splash },
Main: { screen: Navigation },
Login: { screen: LoginScreen },
})
if (this.state.fontLoaded)
return (
<Provider store={store}>
<MainNavigator></MainNavigator>
</Provider>
)
else return null;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Splash.js
import React from 'react';
import { StyleSheet, Text, View, ImageBackground, Image, Button } from 'react-native';
import bgImage from '../../../assets/images/login-background2.png';
import logo from '../../../assets/images/app-logo.png';
import { connect } from 'react-redux';
import { checkAuth } from '../../actions/auth.actions';
class Splash extends React.Component {
static navigationOptions ={
header: null
}
constructor(props){
super(props);
this.state = {
stillLoading: true,
}
}
componentDidMount() {
this.props.checkAuth();
}
render() {
if (this.props.authState.isLoginPending)
return (
<ImageBackground source={bgImage} style={styles.backgroundContainer}>
<View style={styles.logoContainer}>
<Image source={logo} style={styles.logo}></Image>
<Text style={styles.logoText}> Welcome to HealthScout</Text>
</View>
</ImageBackground>
);
else if (this.props.authState.isLoginSuccess){
setTimeout(() => this.props.navigation.navigate('Main'));
return null;
}
else{
setTimeout(() => this.props.navigation.navigate('Login'));
return null;
}
}
}
const mapStateToProps = state => {
return {
authState: state.authState
}
}
const mapDispatchToProps = dispatch => {
return {
checkAuth: () => dispatch(checkAuth()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Splash);
const styles = StyleSheet.create({
backgroundContainer: {
flex: 1,
alignItems: 'center',
width: null,
height: null,
justifyContent: 'center',
},
logoContainer: {
alignItems: 'center',
},
logo: {
width: 110,
height: 149,
},
logoText: {
color: '#fff',
fontSize: 40,
fontFamily: 'Quicksand',
opacity: 0.7,
marginTop: 20,
marginBottom: 10,
textAlign: 'center',
},
});
Solution
Take out the createStackNavigator from render.
It is better way wrapping screens above App class.
const MainNavigator = createStackNavigator({
Splash: { screen: Splash },
Main: { screen: Navigation },
Login: { screen: LoginScreen },
})
export default class App extends React.Component {
...
Why?
render is run repeatedly depends on various conditions as changing state, props and so on.
And your code looks making multiple components with createStackNavigation in render. Take out :)
p.s If you want to wait loading fonts before show home screen, just change to home screen from splash screen after loaded fonts. Thus, the better way is loading fonts in SplashScreen and do what you want.

React Native Lottie View Animation Play/Pause Issue

I'm using React Native Lottie Wrapper to show animation on screen.
I need a functionality to play/pause/resume animation.
Here is my a part of my code:
...
constructor(props) {
super(props);
this.state = {
progress: new Animated.Value(0)
};
}
static navigationOptions = {
title: "Details",
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerTruncatedBackTitle: 'List'
};
componentDidMount() {
this.animation.play();
}
playLottie() {
console.log('play');
}
pauseLottie() {
console.log('pause');
}
render() {
return (
<View>
<Animation
ref={animation => { this.animation = animation; }}
source={require('../../../../assets/anim/balloons.json')}
style={{height: 300, width: '100%'}}
loop={false}
progress={this.state.progress}
/>
<Text>Course with id: {this.props.navigation.state.params.courseId}</Text>
<Button
onPress={this.playLottie}
title="Play Lottie"
color="#841584"
accessibilityLabel="Play video"
/>
<Button
onPress={this.pauseLottie}
title="Pause Lottie"
color="#841584"
accessibilityLabel="Pause video"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
...
The animation is playing well but I can't pause it and resume it.
Does anyone have a solution for this problem?
P.S. I have tried to use this.animation in pauseLottie() method but it said that is undefined.
Thank you in advance!
You can pause and play Lottie animation by changing the speed prop, where speed={0} puts LottieView component in pause and speed={1} plays it at normal speed.
Here is an example:
playAnimation = () => {
this.setState({speed: 1})
}
pauseAnimation = () => {
this.setState({speed: 0})
}
<LottieView
source={this.state.sourceAnimation}
speed={this.state.speed} />
You have to set the state from the play/pause functions. In order to access the state of the Component, you have to bind the function to the component class:
First option in your constructor:
constructor(props) {
super(props);
this.playLottie.bind(this);
this.pauseLottie.bind(this);
}
or second option when declaring inside class use the es6 function syntax:
playLottie = () => {
...
}
pauseLottie = () => {
...
}
Inside those function call setState and add the value you want to set it to. In your case I would:
playLottie = () => {
this.setState({ progress: true })
}
pauseLottie = () => {
this.setState({ progress: false })
}
It is important you bind those two functions to your class component, because you will not be able to access component props. Thats why it is throwing you an error setState is not a function
Your render looks good ;)
for me it didn't work well: we have to add setValue(0), then we need to improve pause/restart to maintain the playing speed and change easing function to avoid slow re-start. Let's also add looping:
constructor(props) {
super(props);
this.playLottie.bind(this);
this.pauseLottie.bind(this);
this.state = {
progress: new Animated.Value(0),
pausedProgress: 0
};
}
playLottie = () => {
Animated.timing(this.state.progress, {
toValue: 1,
duration: (10000 * (1 - this.state.pausedProgress)),
easing: Easing.linear,
}).start((value) => {
if (value.finished) this.restartAnimation();
});
}
restartAnimation = () => {
this.state.progress.setValue(0);
this.setState({ pausedProgress: 0 });
this.playAnimation();
}
pauseLottie = () => {
this.state.progress.stopAnimation(this.realProgress);
}
realProgress = (value) => {
console.log(value);
this.setState({ pausedProgress: value });
};
...
(Now) For me, it's working fine! Play and pause option work as expected.
If you use an Lottie animation that contains a loop you can control it all with the LottieView api built in. (if you are using a file that has the animation)
import Lottie from 'lottie-react-native'
const ref = useRef<AnimatedLottieView>()
const pause = () => {
ref.current.pause()
}
const resume = () => {
ref.current.resume()
}
const reset = () => {
ref.current.reset()
}
<Lottie
ref={ref}
source={source}
resizeMode={resizeMode}
loop={true}
duration={duration}
autoPlay={true}
onAnimationFinish={onFinish}
/>

react native navigation custom animated transition

I'm using react native v0.49 and I'm trying to implement custom transition when navigate to other page.
what I'm trying to do is to make transition only for one scene from scene 2 to scene3. but not for all the app.
this example I found it's for all web so I want to make just for one screen and for all the app because if I do that way it will effect for all the app and it's not what I'm looking for. any idea?
class SceneOne extends Component {
render() {
return (
<View>
<Text>{'Scene One'}</Text>
</View>
)
}
}
class SceneTwo extends Component {
render() {
return (
<View>
<Text>{'Scene Two'}</Text>
</View>
)
}
}
let AppScenes = {
SceneOne: {
screen: SceneOne
},
SceneTwo: {
screen: SceneTwo
},
SceneThree: {
screen: SceneTwo
},
}
let MyTransition = (index, position) => {
const inputRange = [index - 1, index, index + 1];
const opacity = position.interpolate({
inputRange,
outputRange: [.8, 1, 1],
});
const scaleY = position.interpolate({
inputRange,
outputRange: ([0.8, 1, 1]),
});
return {
opacity,
transform: [
{scaleY}
]
};
};
let TransitionConfiguration = () => {
return {
// Define scene interpolation, eq. custom transition
screenInterpolator: (sceneProps) => {
const {position, scene} = sceneProps;
const {index} = scene;
return MyTransition(index, position);
}
}
};
class App extends Component {
return (
<View>
<AppNavigator />
</View>
)
}
Here's an example of how we do it, you can add your own transitions to make it your own. Our goal was simply to expose the baked-in transition configurations to have more control over the animations. Our transition configuration: https://gist.github.com/jasongaare/db0c928673aec0fba7b4c8d1c456efb6
Then, in your StackNavigator, add that config like so:
StackNavigator(
{
LoginScreen: { screen: LoginScreen },
HomeScreen: { screen: HomeScreen },
},
{
stateName: 'MainStack',
initialRouteName: 'HomeScreen',
initialRouteParams: { transition: 'fade' },
transitionConfig: TransitionConfig,
}
);
Finally, when you navigate, just add your params when you navigate:
this.props.navigation.navigate('HomeScreen', { transition: 'vertical' })

React Native Animating List of texts

I have an array of texts that I want to flash on a blank screen, one after the other with animations. Something like:
state = {
meditations: ["Take a deep breath", "embrace this feeling", "breath
deeply", ...]
}
I want to show only one string at a time, and animate their opacity. So a string fades in and fades out, then the next string, and so on.
I am new to react native and quite confused about how to go about this. Please, how may I approach this, I have read the docs but still not clear how to.
Below is what I have tried, I modified this from the docs but it shows everything at once. I'm still trying to see how I can make them animate one after the other, showing only one at a time. Thanks for your help in advance.
import React from 'react';
import { Animated, Text, View } from 'react-native';
class FadeInView extends React.Component {
state = {
fadeAnim: new Animated.Value(0), // Initial value for opacity: 0
}
renderMeditations() {
let { fadeAnim } = this.state;
return this.props.meditations.map((meditation, index) => {
Animated.timing( // Animate over time
this.state.fadeAnim, // The animated value to drive
{
toValue: 2, // Animate to opacity: 1 (opaque)
duration: 10000, // Make it take a while
}
).start(() => {
this.setState({ fadeAnim: new Animated.Value(0) })
}); // Starts the animation
return (
<Animated.Text // Special animatable View
key={index}
style={{
...this.props.style,
opacity: fadeAnim, // Bind opacity to animated value
}}
>
{meditation}
</Animated.Text>
)
})
}
render() {
return (
<View style={{flex: 1}}>
{this.renderMeditations()}
</View>
);
}
}
export default class App extends React.Component {
state = {
meditations: ["Take a deep breath", "Calm down", "Relax", "Tell yourself all will be fine"]
}
render() {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<FadeInView meditations={this.state.meditations} style={{fontSize: 28, textAlign: 'center', margin: 10}} />
</View>
)
}
}
After much toil with this, I was able to solve it with react-native-animatable like so:
import React from "react";
import {
View,
Text,
Animated
} from "react-native";
import * as Animatable from 'react-native-animatable';
class VideoScreen extends React.Component {
state = {
meditations: ["Take a deep breath", "embrace this feeling", "breath
deeply"],
index: 0
};
render() {
const { meditations, index } = this.state;
return (
<View style={{flex: 1}}>
<Animatable.Text
key={index}
animation={'fadeIn'}
iterationCount={2}
direction="alternate"
duration={2000}
onAnimationEnd={() => {
if (this.state.index < this.state.meditations.length - 1) {
this.setState({ index: this.state.index + 1});
}
}}
style={{
position: "absolute",
left: 0, right: 0,
bottom: 40
}}>
{meditations[index]}
</Animatable.Text>
</View>
);
}
}
export default VideoScreen;
The map function executes all at once so basically you are rendering/returning all 3 items at the same time. I understand that your issue is that the animation is working tho.
If what you want is to show one text, then the other and so on I suggest iterating the index of your text array instead of using the map function.
Something like:
Execute Animation
Increase Index
Index = 0 if you are at the end of the array.
In a loop. Check setInterval, it might help you.
For the function components:-
we can use the above-metioned solutions. I am writing a function hopefully it will help you display a looping text with the animation
We will use this package for the animation https://github.com/oblador/react-native-animatable.
import {StyleSheet} from 'react-native';
import React, {useState} from 'react';
import * as Animatable from 'react-native-animatable';
const UserMessage = () => {
const [index, setIndex] = useState(0);
const meditations = [
'Take a deep breath',
'embrace this feeling',
'breath deeply',
];
return (
<Animatable.Text
key={index}
animation={'fadeIn'}
iterationCount={2}
direction="alternate"
duration={2000}
onAnimationEnd={() => {
if (index < meditations.length - 1) {
setIndex(index + 1);
} else {
setIndex(0);
}
}}
style={styles.messageStyle}>
{meditations[index]}
</Animatable.Text>
);
};
export default UserMessage;
const styles = StyleSheet.create({
messageStyle: {
textAlign: 'center',
fontSize: 18,
fontWeight: '500',
width: '80%',
color: '#1C1C1C',
marginBottom: 20,
minHeight: 50,
alignSelf: 'center',
},
});

Resources