When i put credits and make a payment the payment is succesfull but for some reason after the payment stripe is triggering all modals to open(from header), like it setting true all the modals , anyone know why ? ( The modal is from another compoenent name "Header" its working fine with everything else in my app .
Code below :
Merry Christmas :)
//React imports
import React, { useState } from 'react';
import { StyleSheet, View, Text, Pressable, Alert, Image , KeyboardAvoidingView , Dimensions , Platform} from 'react-native';
import { useNavigation , useFocusEffect } from '#react-navigation/native';
//Icon imports
import { AntDesign } from '#expo/vector-icons';
import { FontAwesome5 } from '#expo/vector-icons';
//API imports
import axios from 'axios';
import { StripeProvider, CardField, CardFieldInput, createToken, confirmPayment } from '#stripe/stripe-react-native';
import { Buffer } from "buffer";
//useConfirmPayment,useStripe
//My imports
import Header from './Header';
//.Env imports
import {URL} from '#env';
export default function Payment({route}) {
//Constructors for Card Details
const [card, setCard] = useState(CardFieldInput.Details | null);
//Use of navigate Hook
const navigation = useNavigation();
//My local variables
const bookItem = route.params.paramKey;
const bookID = bookItem.id;
const bookPrice = bookItem.Price;
const bookImage = bookItem.image;
//Payment function
const pay = async () => {
//First we create token for the payment
const resToken = await createToken({...card,type:'Card'})
//Then we create data for the payment , like amount and currency
const payData = {
amount : bookPrice * 100,
currency : "eur",
}
console.log(payData)
//If credit card fields are correct fil out ,then continue to further confirmations
if(Object.keys(resToken) == "token"){
//Request to check the success of the payment
await axios.post(URL+"/createPayment",
{...payData}).then((response)=>{
const { confirmPaymentIntent } = confirmPayment(response.data.paymentIntent,{paymentMethodType : 'Card'})
//If confirm is true then update the book table in Bought Column
if(Object.keys(response.data).length > 1){
Alert.alert("Success payment")
//Request to inform the table that this book is bought
axios.post( URL+"/bookBuy",
{
bookCheckID : bookID,
userBoughtItID : global.id
}).catch((error)=>{
console.log("Axios request failed to buy the book")
})
//End of second Axios request
}
else
{
console.log("Token Error ,maybe card credits is wrong")
Alert.alert("Error in payment,maybe card credits are wrong?")
}
}).catch((error)=>{
console.log("Axios request for payment failed")
})
//End of first Axios request
}
else{
Alert.alert("Card credentials are not Valid")
}
}
return (
//Stripe public key provider
<StripeProvider
publishableKey = "pk_test_51MAWjfI5vsjFWvGhrr5n2mAujexURegEgW4ujlSPpois9Em7FBzHrEkuv5zkeRjck8CeLBAcI761eVqgWQ3mL6EX00oSp0WeB6"
merchantIdentifier = "merchant.com.{{E-LIBRARY}}"
urlScheme = "your-url-scheme"
>
<KeyboardAvoidingView
style={{ height: Platform.OS ==='ios' ? Dimensions.get('window').height * 1 : Dimensions.get('window').height * 1}}
behavior={(Platform.OS === 'ios') ? 'position' : "position"}
enabled
keyboardVerticalOffset={Platform.select({ ios: -50, android: -50 })}
>
<View style = {styles.container}>
{/* Header */}
<Header />
{/* Container */}
<View style={styles.container}>
<View style = {styles.viewContainerForInfos}>
<Text style = {styles.bookText}>{bookItem.title}</Text>
<View style = {styles.imageView}>
<Image
style = {{width:'30%',height:'60%',borderRadius:22,position:'relative',resizeMode:'contain'}}
source = {{uri :'data:image/png;base64,' + Buffer.from(bookImage.data,'hex').toString('base64')}}
/>
<AntDesign name="checkcircleo" size={24} color="white" style = {{}} />
</View>
</View>
<View style = {styles.viewForCardinfo}>
<Text style = {styles.creditText}>Credit Card Informations</Text>
<FontAwesome5 name="id-card-alt" size={30} color="black" />
</View>
<View style = {{alignItems:'center',width:'100%',height:'100%'}}>
{/* Card component from Stripe */}
<CardField
postalCodeEnabled={true}
placeholder={{
number: '4242 4242 4242 4242',
}}
cardStyle={{
backgroundColor: '#FFFFFF',
textColor: '#000000',
borderWidth:2,
borderRadius:15
}}
style={{
width: '80%',
height: '8%',
marginVertical: '15%',
}}
onCardChange={(cardDetails) => {
setCard(cardDetails);
}}
/>
{/* Pressable in order for the client to pay */}
<Pressable style = {styles.pressable} onPress = {() => {pay()}}>
<Text style = {styles.pressableText}>Buy</Text>
</Pressable>
</View>
</View>
</View>
</KeyboardAvoidingView>
</StripeProvider>
)
}
Header
//React imports
import React, { useState } from 'react';
import { StyleSheet, Text, View, Pressable, Modal, Platform} from 'react-native';
import { useNavigation } from '#react-navigation/native';
//Icon Imports
import { SimpleLineIcons } from '#expo/vector-icons';
//Icons inside header modal
import { AntDesign } from '#expo/vector-icons';
import { Feather } from '#expo/vector-icons';
export default function Header() {
//Use of navigation Hook
const navigation = useNavigation();
//Modal Constructor
const [modalVisible,setModalVisible] = useState(false);
return (
<View style = {styles.Container}>
{/* Modal*/}
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}>
<View style = {styles.modalStyle}>
{/* Pressable and Text for each functionality */}
<Pressable style = {styles.pressable} onPress = { () => navigation.navigate('Home')}>
<AntDesign name="home" size={24} color="black" style = {{marginRight:'2%',marginLeft:'2%'}}/>
<Text style = {styles. textInsideModalPressables}>Home</Text>
</Pressable>
<Pressable style = {styles.pressable} onPress = { () => navigation.navigate('MyBooks')}>
<Feather name="book" size={24} color="black" style = {{marginRight:'2%',marginLeft:'2%'}}/>
<Text style = {styles. textInsideModalPressables}>My Collection</Text>
</Pressable>
<Pressable style = {styles.pressable} onPress = { () => navigation.navigate('Profile')}>
<AntDesign name="profile" size={24} color="black" style = {{marginRight:'2%',marginLeft:'2%'}}/>
<Text style = {styles. textInsideModalPressables}>Profile</Text>
</Pressable>
<Pressable style = {styles.pressable} onPress = { () => navigation.navigate('Settings')}>
<Feather name="settings" size={24} color="black" style = {{marginRight:'2%',marginLeft:'2%'}}/>
<Text style = {styles. textInsideModalPressables}>Settings</Text>
</Pressable>
<Pressable style = {styles.pressable} onPress = { () => navigation.navigate('LogInPage')}>
<AntDesign name="logout" size={24} color="black" style = {{marginRight:'2%',marginLeft:'2%'}}/>
<Text style = {styles. textInsideModalPressables}>Log Out</Text>
</Pressable>
<Pressable onPress={ () => {setModalVisible(!modalVisible)}} style = {styles.pressable}>
<AntDesign name="closecircleo" size={24} color="black" style = {{marginRight:'2%',marginLeft:'2%'}}/>
<Text style = {styles. textInsideModalPressables}>Close modal</Text>
</Pressable>
</View>
</Modal>
{/* Pressable Mechanism in order to open the Modal */}
<Pressable onPress={ () => {setModalVisible(!modalVisible)}} style = {styles.pressableMenu}>
<SimpleLineIcons style = {styles.iconMenu} name="menu" size={24} color="white" />
<Text style = {styles.menuText}>Menu</Text>
</Pressable>
<View style ={{width:'50%',alignItems:'center',justifyContent:'center'}}>
<Text style = {styles.nameText}> E-Library</Text>
</View>
</View>
);}
Related
I am very new to React Native and this is the reason why I need your help now. I try to create an app with a normal Sidemenu (Burgermenu) which should apear on ech page. And I have a Bottom tab bar which should appear on 3 pages. The problem is, that on those 3 pages the Sidemenu is not displayed. What am I doing wrong?
This is the first called Page:
import * as React from 'react';
import DrawerNavigator from './DrawerNavigator'
function App() {
return (
<DrawerNavigator />
);
}
export default App;
This is the Sidemenu (DrawerNavigator.js):
import * as React from 'react'
import { createDrawerNavigator, DrawerContentScrollView, DrawerItemList, DrawerItem } from '#react-navigation/drawer'
import ProjekteStackNavigator from './side_menu/Projekte'
import DashboardStackNavigator from './side_menu/Desktop'
...
import { Text, View, StyleSheet} from 'react-native';
//##################### ICONS Importieren #####################
import AppLoading from 'expo-app-loading';
import { useFonts } from 'expo-font';
import { createIconSetFromIcoMoon } from '#expo/vector-icons';
const Icon = createIconSetFromIcoMoon(
require('../../assets/icomoon/selection.json'),
'IcoMoon',
'zepra_icons.ttf'
);
//#############################################################
const Drawer = createDrawerNavigator()
function myLogout(){
navigation.navigate("LoginScreen");
}
const DrawerNavigator = () => {
const [fontsLoaded] = useFonts({ IcoMoon: require('../../assets/icomoon/zepra_icons.ttf') });
if (!fontsLoaded) {
return <AppLoading />;
}
return (
<Drawer.Navigator initialRouteName="Home" drawerContent={props => {
return (
<DrawerContentScrollView {...props} scrollIndicatorInsets={{ right: 1 }}>
<DrawerItemList {...props} />
<DrawerItem label="Logout" labelStyle={{marginLeft:-18, fontSize: 15,}}
icon={({ focused, color, size }) => <Icon2 style={{marginLeft:8}} color={focused ? '#1585b5' : '#6cbabf'} size={26} name={focused ? 'log-out-outline' : 'log-out-outline'} /> }
onPress={() => props.navigation.navigate("StartScreen") } />
</DrawerContentScrollView>
)
}}>
<Drawer.Screen name="Dashboard" component={DashboardStackNavigator}
options={{
drawerLabel: 'Dashboard',
drawerLabelStyle:{ marginLeft:-20, fontSize: 15},
drawerIcon: ({focused, size}) => (
<Icon name='HAUS_3' size={35} color={focused ? '#1585b5' : '#6cbabf'} />
)}}/>
<Drawer.Screen name="Projekte" component={ProjekteStackNavigator}
options={{
drawerLabel: 'Projekte',
drawerLabelStyle:{marginLeft:-20, fontSize: 15},
drawerIcon: ({focused, size}) => (
<Icon name='PROJEKTE_ALLE' size={35} color={focused ? '#1585b5' : '#6cbabf'} />
)}}/>
</Drawer.Navigator>
)
}
export default DrawerNavigator
This is the Projects Page which calls the Pages with the Bottom Tab:
import React, { useState, useEffect } from 'react';
import {Alert, View, Text, Modal, StyleSheet, Pressable, ListView, FlatList, TouchableOpacity } from 'react-native'
import { createStackNavigator } from '#react-navigation/stack'
import { theme } from '../../core/theme';
import clientConfig from '../../../client-config';
import Icon2 from 'react-native-vector-icons/Ionicons';
import ProjektOverview from '../ProjektOverview'
//##################### ICONS Importieren #####################
import AppLoading from 'expo-app-loading';
import { useFonts } from 'expo-font';
import { createIconSetFromIcoMoon } from '#expo/vector-icons';
const Icon = createIconSetFromIcoMoon(
require('../../../assets/icomoon/selection.json'),
'IcoMoon',
'zepra_icons.ttf'
);
//#############################################################
const Stack = createStackNavigator()
const ModalComponent = ({ modalVisible, setModalVisible, children }) => {
return (
<Modal
animationType="slide"
transparent={true}
style={styles.centeredView}
visible={modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
setModalVisible(!modalVisible);
}}>
{children}
</Modal>
);
};
export default function ObjectsScreen({ navigation }){
const [modalVisible, setModalVisible] = useState(false);
const [modal2Visible, setModal2Visible] = useState(false);
const [data, setData] = React.useState(null);
const [fontsLoaded] = useFonts({ IcoMoon: require('../../../assets/icomoon/zepra_icons.ttf') });
const onClickHandler =(data)=>()=> {
navigation.navigate('ProjektOverview', {data});
};
const comp_id = localStorage.getItem('anwender_unternehmen');
if ( localStorage.getItem( 'token' )!=null ) {
React.useEffect(() => {
async function getData() {
try {
const siteUrl = clientConfig.siteUrl + '/wp-json/zep/v1/projekt-liste/' + comp_id;
const response = await fetch(siteUrl, {
method: 'GET',
headers: {
Authorization: 'Bearer '+localStorage.getItem( 'token' )
}
})
if (response.status === 200) {
let json = await response.json();
const array = json.data;
const error = array.error_code;
if(error == 0){
const projekte = array.data_values[0];
setData(projekte);
}else{
setData(1201);
}
}else {
if(response.status === 503) {
setModalVisible(true);
}
if(response.status === 403) {
setModal2Visible(true);
}
throw new Error(`failed due to status code ${response.status}`);
}
} catch (err) {
console.log(err);
}
}
getData();
}, []);
if(data != 1201){
return (
<FlatList
data={data}
keyExtractor={(item, index) => String(index) + item.value}
renderItem={({item, index})=>(
<TouchableOpacity onPress={ onClickHandler({item}) } >
<View style={{ textAlign: 'left', borderColor:'#6cbabf', borderBottomWidth: 1, padding:10, margin:0, flexDirection: "row", alignItems: "center"}}>
<Text style={{textAlign: 'left', marginLeft:15, fontSize: 16, fontWeight: '500',}}>
{item.projekt_name} {item.projekt_nummer}</Text>
<View style={{flex:1, alignItems: "center", flexDirection: "row", justifyContent: "flex-end", marginRight:30}}>
<Icon name='PROJEKTE_BEARBEITEN' size={40} color={'#6cbabf'} />
<Icon2 name='eye-outline' size={10} color={'#6cbabf'} style={{padding:-100, margin:-17, bottom:-7.5}} />
</View>
</View>
</TouchableOpacity>
)}/>
); // return
}else{
return (
<View style={{borderColor:'#6cbabf',borderWidth: 1,borderRadius:5, padding:15,margin:12,}}>
<View style={{justiftyContent:"center", alignItems:"center"}}>
<Icon name='PROJEKTE_ALLE' size={100} color={'#6cbabf'} />
</View>
<Text style={{textAlign: 'left', fontSize: 16, fontWeight: '700', paddingBottm:15,}}>Sie haben noch keine Projekte erstellt.</Text>
<Text style={{fontSize: 12}}> </Text>
<Text style={{textAlign: 'center', fontSize: 13}}>Projekte können nur in der Desktopanwendung verwalten werden.</Text>
</View>
); // return
}
}else{
navigation.navigate('Dashboard');
}
return (
<>
<ModalComponent
modalVisible={modalVisible}
setModalVisible={(val) => {
setModalVisible(val);
}}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>Fehler 503</Text>
<Text style={styles.modalText}>Ein unbekannter Fehler ist aufgetreten!.</Text>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={() => setModalVisible(!modalVisible)}>
<Text style={styles.textStyle}>Ok</Text>
</Pressable>
</View>
</View>
</ModalComponent>
<ModalComponent
modalVisible={modal2Visible}
setModalVisible={(val) => {
setModal2Visible(val);
}}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>Fehler 403</Text>
<Text style={styles.modalText}>Bitte loggen Sie sich erneut ein!</Text>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={() => setModal2Visible(!modal2Visible)}>
<Text style={styles.textStyle}>Ok</Text>
</Pressable>
</View>
</View>
</ModalComponent>
</>
);
}
This is the Bottom Navigator:
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
//##################### ICONS Importieren #####################
import AppLoading from 'expo-app-loading';
import { useFonts } from 'expo-font';
import { createIconSetFromIcoMoon } from '#expo/vector-icons';
const Icon = createIconSetFromIcoMoon(
require('../../assets/icomoon/selection.json'),
'IcoMoon',
'zepra_icons.ttf'
);
//#############################################################
//##################### Seiten importieren #####################
import ProjectsScreen from './ProjectScreen';
import WaermeschutzScreen from './tabs/waermeschutz';
import BegehungenScreen from './tabs/begehungen';
//##############################################################
//##################### Menünamen für Buttontab ################
const projectsName = "Projekte";
const waermeschutzName = "Wärmeschutz";
const begehungenName = "Begehungen";
//##############################################################
//#################### BUTTON-TAB erstellen #####################
const Tab = createBottomTabNavigator();
function MainContainer() {
// Load the icon font before using it
const [fontsLoaded] = useFonts({ IcoMoon: require('../../assets/icomoon/zepra_icons.ttf') });
if (!fontsLoaded) {
return <AppLoading />;
}
return (
<Tab.Navigator
initialRouteName={projectsName}
screenOptions={({ route }) => ({
headerShown: false,
tabBarIcon: ({ focused, color, size }) => {
let iconName;
let rn = route.name;
if (rn === projectsName) {
iconName = focused ? 'PROJEKTE_ALLE' : 'PROJEKTE_ALLE';
} else if (rn === waermeschutzName) {
iconName = focused ? 'HAUS_3' : 'HAUS_3';
} else if (rn === begehungenName) {
iconName = focused ? 'NOTIZ_ERSTELLEN' : 'NOTIZ_ERSTELLEN';
}
// You can return any component that you like here!
return <Icon name={iconName} size={43} color={color} />;
},
'tabBarActiveTintColor': '#4283b1',
'tabBarInactiveTintColor': '#5db8bd',
'tabBarStyle':{ 'paddingTop':4, 'height':90 },
'tabBarLabelStyle':{ 'paddingTop':3, 'fontSize':13 }
})}>
<Tab.Screen name={projectsName} component={ProjectsScreen} />
<Tab.Screen name={waermeschutzName} component={WaermeschutzScreen} />
<Tab.Screen name={begehungenName} component={BegehungenScreen} />
</Tab.Navigator>
);
}
export default MainContainer;
And this is the page with the ProjectsScreen:
import React, { useState, useEffect } from 'react';
import {Alert, View, Text, Modal, StyleSheet, ImageBackground, ScrollView, FlatList , TouchableOpacity, Linking} from 'react-native';
import Background from '../components/Background_2';
import BackButton from '../components/BackButton';
import helpers from '../components/Data_funks';
import Icon2 from 'react-native-vector-icons/Ionicons';
import DrawerNavigator from './DrawerNavigator'
//##################### ICONS Importieren #####################
import AppLoading from 'expo-app-loading';
import { useFonts } from 'expo-font';
import { createIconSetFromIcoMoon } from '#expo/vector-icons';
const Icon = createIconSetFromIcoMoon( require('../../assets/icomoon/selection.json'), 'IcoMoon', 'zepra_icons.ttf');
const Bold = ({ children }) => <Text style={{ fontWeight: 'bold' }}>{children}</Text>
//#############################################################
//export default function ProjectsScreen({ route, navigation }) {
function ProjectsScreen({ route, navigation }) {
const data = route.params['data'];
const [projekte_data, setProjektdata] = useState([]);
const [bauherr, setBauherr] = useState([]);
const [bauleiter, setBauleiter] = useState([]);
const [projektleiter, setProjektleiter] = useState([]);
const [sachbearbeiter, setSachbearbeiter] = useState([]);
const [objekte, getObjekte] = useState([]);
// Projektdaten ########################################################################
useEffect(() => {
const init = async () => {
await helpers.get_projekt_data(data.item.projekt_id).then(result =>{
setProjektdata(result.p_data[0]);
setBauherr(result.bauherr[0]);
setBauleiter(result.bauleiter[0]);
setProjektleiter(result.projektleiter[0]);
setSachbearbeiter(result.sachbearbeiter[0]);
})
}
;
init();
},[]);
//###################################################################################
// Objektdaten ########################################################################
useEffect(() => {
const init2 = async () => {
await helpers.getObjekte(data.item.projekt_id).then(result2 =>{
getObjekte(result2[0]);
})
}
;
init2();
},[]);
//###################################################################################
const onClickHandler =(data)=>()=> {
navigation.navigate('ProjektOverview', {data});
};
console.log(objekte);
return(
<ScrollView scrollIndicatorInsets={{ right: 1 }} contentContainerStyle={{ flexGrow: 1 }}>
<Background>
<DrawerNavigator />
<View style={{paddingTop:50,}}>
<Icon name='KRAN' size={80} color={'#6cbabf'} style={{textAlign: 'center',}} />
</View>
<View style={{paddingTop:10,}}>
{ data.item.projekt_name !='Kein Titel'? <Text style={styles.HeadNameStyle}>{data.item.projekt_name}
<Text>{"\n"}</Text>
{data.item.projekt_nummer}</Text> : <Text style={styles.HeadNameStyle}>{data.item.projekt_nummer}</Text> }
</View>
<View style={styles.tabsStyle}>
<View style={styles.row}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${bauleiter['kontakte_mobil']}`) }>
<Icon2 style={styles.icon} name='call-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
<View style={styles.inputWrap}>
<Text style={styles.tabsHeader}><Bold>Bauleiter:</Bold> {bauleiter['kontakte_vorname']} {bauleiter['kontakte_vorname']}</Text>
</View>
<View style={styles.inputWrap2}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${bauleiter['kontakte_mail']}`) }>
<Icon2 style={styles.icon} name='md-mail-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
</View>
</View>
<View style={styles.row}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${bauherr['kontakte_mobil']}`) }>
<Icon2 style={styles.icon} name='call-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
<View style={styles.inputWrap}>
<Text style={styles.tabsHeader}><Bold>Bauherr:</Bold> {bauherr['kontakte_vorname']} {bauherr['kontakte_nachname']}</Text>
</View>
<View style={styles.inputWrap2}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${bauherr['kontakte_mail']}`) }>
<Icon2 style={styles.icon} name='md-mail-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
</View>
</View>
<View style={styles.row}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${projektleiter['anwender_mobil']}`) }>
<Icon2 style={styles.icon} name='call-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
<View style={styles.inputWrap}>
<Text style={styles.tabsHeader}><Bold>Projektleiter:</Bold> {projektleiter['anwender_vorname']} {projektleiter['anwender_nachname']}</Text>
</View>
<View style={styles.inputWrap2}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${projektleiter['anwender_mail']}`) }>
<Icon2 style={styles.icon} name='md-mail-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
</View>
</View>
<View style={styles.row}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${sachbearbeiter['anwender_mobil']}`) }>
<Icon2 style={styles.icon} name='call-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
<View style={styles.inputWrap}>
<Text style={styles.tabsHeader}><Bold>Sachbearbeiter:</Bold> {sachbearbeiter['anwender_vorname']} {sachbearbeiter['anwender_nachname']}</Text>
</View>
<View style={styles.inputWrap2}>
<TouchableOpacity onPress={() => Linking.openURL(`tel:${sachbearbeiter['anwender_mail']}`) }>
<Icon2 style={styles.icon} name='md-mail-outline' size={22} color={'#6cbabf'} />
</TouchableOpacity>
</View>
</View>
</View>
<View style={styles.objectHeader}>
{ objekte !=''? <Text style={styles.HeadNameStyle}>Objekte</Text>: null}
</View>
<FlatList style={styles.FlatList}
data={objekte}
scrollEnabled={false}
keyExtractor={(item, index) => String(index) + item.value}
renderItem={({item, index})=>(
<View style={styles.Container}>
<View style={styles.navBar}>
<Icon name='HAUS_3' size={110} color={'#6cbabf'} />
<View style={styles.centerContainer}>
<Text style={styles.centerText}>{item.objekt_name} </Text>
<Text style={styles.centerText}>{item.objekt_nummer} </Text>
</View>
</View>
<Text>{<Bold>Adresse:</Bold>} {item.objekt_strasse}</Text>
<Text>{' '} {item.objekt_plz}, {item.objekt_ort}</Text>
<Text>{' '}</Text>
<Text>{<Bold>Nutzung:</Bold>} {item.objekt_typ}, {<Bold>Vorhaben:</Bold>} {item.objekt_sanierungs_art}</Text>
</View>
)}/>
<Text>{''}</Text>
</Background>
</ScrollView>
)
}
export default ProjectsScreen;
Hi I am new to React Native.
I want to open a modal from another modal.
I have a modal showing event details and user can delete this event. When they press delete button, I want to add another modal saying 'are you sure ?' as in the picture
[![enter image description here][1]][1]
My code doesn't work. I used onModalHide with setTimeOut to close the first one and opend the second one. But did not work. What can be the error here ?
import React, {useRef, useState} from 'react';
import {View, ScrollView, Text, Pressable} from 'react-native';
import styles from '#/screens/dashboard/events/CreatedEventDetails.styles';
import Modal from 'react-native-modal';
import common from '#/config/common';
import {useDispatch, useSelector} from 'react-redux';
import FastImage from 'react-native-fast-image';
import images from '#/config/images';
import {Icon, LoadingIndicator} from '#/components';
import {
closeEventDetailsModal,
fetchEventsList,
handleDeleteEvent,
handleWithDrawEvent,
LOADING,
} from '#/store/events/events';
import {EVENT_STATUS} from '#/constants/eventStatus';
import {strings} from '#/localization';
import moment from 'moment';
const CreatedEventDetails = ({isVisible, closeModal}) => {
const createdEventDetails = useSelector(state => state?.events?.eventDetails);
const userInfo = useSelector(state => state.profile.user);
const dispatch = useDispatch();
const currentEventId = useSelector(state => state.events.currentEventId);
const isLoading =
useSelector(state => state?.events?.eventResponseStatus) === LOADING;
const capitilizedLetter = createdEventDetails?.name
?.toLowerCase()
.charAt(0)
.toUpperCase();
const restOfTheName = createdEventDetails?.name?.slice(1);
const deleteEvent = id => {
dispatch(handleDeleteEvent(id));
dispatch(closeEventDetailsModal());
};
const withDrawEvent = id => {
dispatch(handleWithDrawEvent(id));
dispatch(closeEventDetailsModal());
};
function getBgColor(condition) {
switch (condition) {
case EVENT_STATUS.STATUS_NEW:
return '#ef9d50';
case EVENT_STATUS.STATUS_ACCEPTED:
return '#a1dc6a';
case EVENT_STATUS.STATUS_TENTATIVE:
return 'blue';
case EVENT_STATUS.STATUS_REJECTED:
return 'red';
default:
return '#ef9d50';
}
}
// to change the modal action according to created events or accepted events, we have to check
// if the logged in user id is matching with event owner id. ( delete - withdraw action )
const isOwner = createdEventDetails?.owner?.id === userInfo?.id;
const createdEventDate = createdEventDetails?.event_date;
const formattedDate = moment(createdEventDate).format('DD MMM, ddd');
const fromTime = createdEventDetails?.from_time?.toString();
const formattedFromTime = moment(fromTime, 'hh:mm').format('LT');
const toTime = createdEventDetails?.to_time?.toString();
const formattedToTime = moment(toTime, 'hh:mm').format('LT');
// second modal state
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
// second modal function
const toggleModal = () => {
// when the second modal is open, I close the first one here.
dispatch(closeEventDetailsModal());
setIsDeleteModalVisible(!isDeleteModalVisible);
};
return (
<>
<View style={styles.modalContainer}>
<Modal
isVisible={isVisible}
propagateSwipe
onBackdropPress={() => this.setState(isVisible)}
onModalHide={() => {
setTimeout(() => {
setIsDeleteModalVisible(!isDeleteModalVisible);
}, 100);
}}
onModalWillHide={() => {
setTimeout(() => {
dispatch(fetchEventsList());
}, 500);
}}>
<View style={styles.content}>
{/* we have to check if event id is matching with currentEventId to prevent undefined problem
and also modal detail can have previous event detail. this will prevent the issue */}
{createdEventDetails.id !== currentEventId ? (
<LoadingIndicator visible />
) : (
<View>
<LoadingIndicator visible={isLoading} />
<View style={styles.closeBtnContainer}>
<Pressable style={styles.closeBtn} onPress={closeModal}>
<Text style={styles.closeText}>X</Text>
</Pressable>
</View>
<View>
<Text
style={
styles.contentTitle
}>{`${capitilizedLetter}${restOfTheName}`}</Text>
<Text style={styles.contenSubtTitle}>
{formattedDate}
{'\n'}
{formattedFromTime} - {formattedToTime}
</Text>
<Text style={styles.contentText}>
At {createdEventDetails?.location}
</Text>
<View style={styles.participantsContainer}>
<Text style={styles.contentTitle}>
{strings.eventDetails.participantsTitle} (
{createdEventDetails?.invites?.length})
</Text>
<View style={common.stickyButtonLine} />
<ScrollView>
{createdEventDetails?.invites?.map(invite => (
<View style={styles.checkboxContainer} key={invite.id}>
<View style={styles.imageContainer}>
{invite?.user?.thumb ? (
<FastImage
source={{uri: invite?.user?.thumb}}
style={styles.listItemUserImage}
resizeMode={FastImage.resizeMode.cover}
/>
) : (
<Icon
icon={images.ic_user}
style={styles.noUserIcon}
/>
)}
<Text style={styles.contentPersonName}>
{invite?.user?.name}
</Text>
</View>
<View
style={{
backgroundColor: getBgColor(invite?.status),
width: 25,
height: 25,
borderRadius: 25,
}}>
<Text>{''}</Text>
</View>
</View>
))}
</ScrollView>
</View>
<View>
<View>
<Text style={styles.contentTitle}>
{strings.eventDetails.hobbiesTitle} (
{createdEventDetails?.hobbies?.length})
</Text>
<View style={common.stickyButtonLine} />
<View style={styles.hobbiesContainer}>
{createdEventDetails?.hobbies?.map(hobby => (
<View style={styles.emojiContainer} key={hobby.id}>
<Text>{hobby.emoji}</Text>
</View>
))}
</View>
<View style={common.stickyButtonLine} />
</View>
<View style={[styles.buttons, styles.singleButtonAlign]}>
{isOwner ? (
<>
<Pressable
style={styles.decline}
onPress={toggleModal}
// onPress={() =>
// deleteEvent(createdEventDetails?.id)
// }
>
<Text style={styles.declineText}>
{strings.eventDetails.delete}
</Text>
</Pressable>
// second modal content here
{/* <Modal
isVisible={isDeleteModalVisible}
propagateSwipe>
<View style={styles.content}>
<Text>Hello!</Text>
<Pressable
title="Hide modal"
onPress={toggleModal}
/>
</View>
</Modal> */}
</>
) : (
<Pressable
style={styles.decline}
onPress={() =>
withDrawEvent(createdEventDetails?.id)
}>
<Text style={styles.declineText}>
{strings.eventDetails.withdraw}
</Text>
</Pressable>
)}
</View>
</View>
</View>
</View>
)}
</View>
</Modal>
</View>
// second modal content here
<View style={styles.modalContainer}>
<Modal isVisible={isDeleteModalVisible} propagateSwipe>
<View style={styles.content}>
<Text>Hello!</Text>
<Pressable title="Hide modal" onPress={toggleModal} />
</View>
</Modal>
</View>
</>
);
};;
export default CreatedEventDetails;
````
[1]: https://i.stack.imgur.com/BUMfa.png
Here are some way of debugging.
first check both the modal is showing up by passing harcoded true value for one by one.
if its working correctly, remove those timeouts and apicalls and
just check with useState only.
if all are fine then try to call
the api.
In my react native chat application i add recording audio functionality, when start recording function in called i put the number of recorded seconds in state and showing them to user. but after doing this stop function in calling but the recording is not stopping. here is my code -
import React from 'react';
import { View, TouchableOpacity, Text} from 'react-native';
import AudioRecorderPlayer from 'react-native-audio-recorder-player';
const [rectime, setrectime] = useState(0)
export default function ChatScreen({ navigation, user, route }) {
const audioRecorderPlayer = new AudioRecorderPlayer();
const dirs = RNFetchBlob.fs.dirs;
const path = Platform.select({
ios: 'hello.m4a',
android: `${dirs.CacheDir}/hello.mp3`,
});
const onStartRecord = async () => {
await audioRecorderPlayer.startRecorder(path);
audioRecorderPlayer.addRecordBackListener(e => {
console.log('Recording . . . ', e);
setrectime(e.currentPosition / 1000)
});
};
const onStopRecord = async () => {
const audio = await audioRecorderPlayer.stopRecorder().then(() => {
})
return (() => {
alert('stop')
audioRecorderPlayer.removeRecordBackListener()
setrectime(0)
})
sendmp3(path)
};
return(
<View style={[styles.footer]}>
<TouchableOpacity activeOpacity={0.5} onPress={() => onStartRecord()} style=
{styles.btnSendd}>
<Ionicons name={'mic-circle-outline'} size={24} color={'#000'} />
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.5} onPress={() => onStopRecord()} style=
{styles.btnSendd}>
<Ionicons name={'mic-circle-outline'} size={24} color={'#efc100'} />
</TouchableOpacity>
{
rectime > 0
?
<View style={styles.rectime}>
<Text style={{ fontSize: 15 }}>{rectime.toFixed(0)}</Text>
</View>
:
<>
<TouchableOpacity activeOpacity={0.5} onPress={handleVideoPicker} style=
{styles.btnSendd}>
<Ionicons name={'videocam-outline'} size={24} color={'black'} />
</TouchableOpacity>
<TouchableOpacity style={styles.btnSendd} activeOpacity={0.5} onPress=
{handlePhotoPicker}>
<Ionicons name={'camera-outline'} size={24} color={'black'} />
</TouchableOpacity>
</>
}
)
}
If I change any state or change the textinput value, if it changed then its scroll to top and leave the textinput. Or If I like one product, its autoamticlly scroll to top if the array is changed. Anyone can help me ?
import React, { useState, useMemo, useEffect, forwardRef, memo } from 'react';
import { StyleSheet, Text, TextInput, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { RecyclerListView, LayoutProvider, DataProvider } from 'recyclerlistview';
import { useSelector, useDispatch } from 'react-redux';
import { AntDesign } from '#expo/vector-icons';
import { addAmountOnCartItem } from '../../../redux/slice/product/shopping_cart';
import faker from 'faker';
import ButtonWithoutLoader from '../../ButtonWithoutLoader';
const { width, height } = Dimensions.get('window');
const Shopping_cart_list = ({ datas, onPressSetting }) => {
const dispatch = useDispatch();
const provider = useMemo(() => {
return new DataProvider(
(r1, r2) => {
return r1 !== r2;
},
index => {
return 'id:' + index;
}
)
}, []);
const dataProvider = useMemo(() => {
return provider.cloneWithRows(datas);
}, [datas, provider]);
const [update, updateRecycler] = useState({
update: false
});
const handleChange = (e, product_id) => {
dispatch(addAmountOnCartItem({ value: e, product_id }));
updateRecycler(prevState => {
return {
update: !prevState
}
});
};
const layoutProvider = new LayoutProvider((i) => {
return dataProvider.getDataForIndex(i).type;
}, (type, dim) => {
switch(type) {
case 'NORMAL':
dim.height = 250;
dim.width = width * 0.9;
break;
default:
dim.height = 0;
dim.width = 0;
break;
}
});
const RenderData = memo(({ product_id, product_name, price, product_image, amount, username }) => {
return (
<TouchableWithoutFeedback style={{height: 250, backgroundColor: '#fff', marginBottom: 16}}>
<View style={styles.header}>
<TouchableOpacity style={styles.profile_info}>
<Image source={{uri: faker.image.avatar()}} resizeMode="contain" style={styles.profile_image} />
<Text style={styles.username}>{ username }</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onPressSetting(product_id)}>
<AntDesign name="setting" size={24} color="#444" />
</TouchableOpacity>
</View>
<View style={styles.mainContainer}>
<Image source={{uri: product_image}} style={styles.image} />
<View>
<Text style={styles.text}>{product_name}</Text>
<Text style={styles.text}>Preis: {price}</Text>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text style={styles.text}>Anzahl: </Text>
<AntDesign name="minussquareo" style={{marginRight: 6}} size={24} color="black" />
<TextInput onBlur={() => handleChange(1, product_id)} value={ isNaN(amount) ? '' : amount.toString() } onChangeText={e => handleChange(e, product_id)} style={{height: 28, width: 28, borderRadius: 4, textAlign: 'center', backgroundColor: '#eee'}} />
<AntDesign name="plussquareo" style={{marginLeft: 6}} size={24} color="black" />
</View>
</View>
</View>
<View style={[styles.header, { marginTop: 4 }]}>
<ButtonWithoutLoader onPress={() => updateRecycler(prevState => !prevState)} title="Jetzt Kaufen!" width={width * 0.9} />
</View>
</TouchableWithoutFeedback>
)
});
const rowRenderer = (type, data) => {
const { product_id, product_name, price, product_image, amount, username } = data.item;
return <RenderData product_id={product_id} product_name={product_name} price={price} product_image={product_image} amount={amount} username={username} />
};
return (
<View style={{flex: 1, paddingBottom: 85}}>
<RecyclerListView
dataProvider={dataProvider}
layoutProvider={layoutProvider}
forceNonDeterministicRendering={true}
rowRenderer={rowRenderer}
style={{marginLeft: width * 0.05, marginRight: width * 0.05}}
extendedState={update}
scrollViewProps={{showsVerticalScrollIndicator: false}}
/>
</View>
)
};
Flatlist:
import React, { useState, useRef, memo, useMemo } from 'react';
import { StyleSheet, Animated, FlatList, Text, TextInput, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigation } from '#react-navigation/core';
import { AntDesign } from '#expo/vector-icons';
import { addAmountOnCartItem } from '../../../redux/slice/product/shopping_cart';
import faker from 'faker';
import ButtonWithoutLoader from '../../ButtonWithoutLoader';
const { width } = Dimensions.get('window');
const Shopping_cart = ({ datas, onPressSetting }) => {
const dispatch = useDispatch();
const navigation = useNavigation();
const [update, updateRecycler] = useState({
update: false
});
const handleChange = (e, product_id) => {
dispatch(addAmountOnCartItem({ value: e, product_id }));
updateRecycler(prevState => {
return {
update: !prevState
}
});
};
const RenderItem = memo(({ item }) => {
const { product_id, product_name, price, product_image, amount, username, size, colors, desc, selectedColor, selectedSize } = item.item;
return (
<TouchableWithoutFeedback style={{height: 300, backgroundColor: '#fff', marginBottom: 16}}>
<View style={styles.header}>
<TouchableOpacity style={styles.profile_info}>
<Image source={{uri: faker.image.avatar()}} resizeMode="contain" style={styles.profile_image} />
<Text style={styles.username}>{ username }</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onPressSetting(product_id)}>
<AntDesign name="setting" size={24} color="#444" />
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => navigation.navigate('ProductStack', {
product_id,
desc,
price,
product_image,
username,
user_profil_image: faker.image.avatar(),
size,
colors,
})} style={styles.mainContainer}>
<Image source={{uri: product_image}} style={styles.image} />
<View>
<Text style={styles.text}>{product_name}</Text>
<Text style={styles.text}>Preis: {price}</Text>
<Text style={styles.text}>Farbe: {selectedColor}</Text>
<Text style={styles.text}>Größe: {selectedSize}</Text>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text style={styles.text}>Anzahl: </Text>
<AntDesign name="minussquareo" style={{marginRight: 6}} size={24} color="black" />
<TextInput onBlur={() => handleChange(1, product_id)} value={ isNaN(amount) ? '' : amount.toString() } onChangeText={e => handleChange(e, product_id)} style={{height: 28, width: 28, borderRadius: 4, textAlign: 'center', backgroundColor: '#eee'}} />
<AntDesign name="plussquareo" style={{marginLeft: 6}} size={24} color="black" />
</View>
</View>
</TouchableOpacity>
<View style={[styles.header, { marginTop: 4 }]}>
<ButtonWithoutLoader onPress={() => updateRecycler(prevState => !prevState)} title="Jetzt Kaufen!" width={width * 0.9} />
</View>
</TouchableWithoutFeedback>
);
});
return (
<FlatList
data={datas}
keyExtractor={item => item.item.product_id + Math.random(100)}
renderItem={({ item }) => <RenderItem item={item}/>}
contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}
removeClippedSubviews={true}
initialNumToRender={2}
maxToRenderPerBatch={1}
extraData={update}
updateCellsBatchingPeriod={100}
/>
);
};
........................................................................................................................................................................................
If you see the docs of RecyclerView, you will see for extendedState prop:
In some cases the data passed at row level may not contain all the info that the item depends upon, you can keep all other info outside and pass it down via this prop. Changing this object will cause everything to re-render. Make sure you don't change it often to ensure performance. Re-renders are heavy.
Based on your code you are updating this prop in button click and text change handler, this will eventually re-render your screen as per the description. So, here you would need to revise your logic to avoid unnecessary re-renders.
I recently just solved this problem in my own project so I'd be glad to help.
The main issue is that you can't have ANY UI related state in your list items. None. You must instead move all of that information to be transferred through props.
I see your only instance of that is through your TextInput. This could be difficult. So my first question would then be: do you really need RecyclerListView? Unless your list is thousands of items long, and items have a complicated/image intensive UI, I'd suggest FlatList for this project.
If you do continue to need RecyclerListView, I'd suggest making it so after a textinput is finished ('finished' as in maybe after a 3 second timer or, when they press 'submit' or something like that), you change the dataProvider's data object. Change the item at the index you need, and then resubmit it with cloneWithRows()
I'm new to react-native. This component below should render comments posted by users, I would like to add an inputText component from react-native to allow users to post a comment, but don't know where I should place it within the code below.
import React, { useState, useEffect } from 'react';
import { useNavigation } from "#react-navigation/native";
import Icon from 'react-native-vector-icons/AntDesign';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Image,
ScrollView,
FlatList,
Button,
TextInput
} from 'react-native';
import parseDate from "../utils/parseDate";
import * as API from "../api/api"
export default function CommentList({ ride }) {
const [text, setText] = React.useState("");
const [commentsData, setComments] = useState([]);
const navigation = useNavigation();
useEffect(() => {
API.getCommentsByRideId(ride.ride_id).then((comments) => {
setComments(comments)
})
}, [ride.ride_id])
deleteComment = (comment_id) => {
// useEffect(() => {
API.deleteCommentsByCommentId(comment_id).then(() => {
const updatedComments = list.filter((item) => item.comment_id !== comment_id);
setComments(updatedComments)
})
// }, [comment_id])
}
//deletes on refresh only
addComment = (newComment, ride_id) => {
API.postCommentByRideId(newComment, ride_id).then(() => {
setComments(newComment)
})
}
return (
<FlatList
style={styles.root}
data={commentsData}
ItemSeparatorComponent={() => {
return (
<View style={styles.separator} />
)
}}
keyExtractor={(item) => {
return item.author;
}}
renderItem={(item) => {
const comment = item.item;
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => navigation.navigate("UserProfile", { username: item.author })}>
{/* <Image style={styles.image} source={{ uri: comment.avatar_url }} /> */}
</TouchableOpacity>
<View style={styles.content}>
<View style={styles.contentHeader}>
<Text style={styles.name}>{comment.author}</Text>
<Text style={styles.time}>
{parseDate(comment.created_at)}
{comment.votes}
</Text>
</View>
<Text rkType='primary3 mediumLine'>{comment.body}</Text>
{/* <Text style={styles.time}> Likes: {comment.votes}</Text> */}
<TouchableOpacity onPress={() => deleteComment(comment.comment_id)}>
<Icon name="delete" size={20} color="#e33057" />
</TouchableOpacity>
</View>
</View>
);
}} />
);
}
This is the inputText I would like to add to allow users to post a comment.
<TextInput
value={text}
placeholder="write..."
onChangeText={text => setText(text)}
onSubmitEditing={() => addcomment(text, ride.ride_id)}
/>
if you want to add it at fixed position in bottom of screen you may do this
<View style={{flex : 1}}>
<Flatlist
contentContainerStyle={{paddingBottom: 50}}
.../>
<View style={{position : 'absolute', bottom : 0, width : '100%', height : 50}}>
//you input here
</View>
</View>
or if you want to add it last item of flatlist you may use ListFooterComponent
<FlatList
ListFooterComponent={<Input/>}
.../>
</FlatList>