I can increase and decrease item quantity from the app in sync with the graphql without any issue. the problem here is when i add a new item it appears in the graphql but it doesn't show up on the app unless i press save ctrl + s, and the same happens when i delete item it gets deleted on the graphql but it disappears from the app only when i press save or ctrl + s.
this is the product details screen and how i add items to cart, and it works fine without any issue and items get added to graphql when i call onAddToCart function
import React, {useState, useEffect} from "react";
import { View, Text, Image, StyleSheet, FlatList, TouchableOpacity, Alert} from 'react-native';
import Icon from 'react-native-vector-icons/AntDesign';
import Colors from "../../constants/Colors";
import { Auth , Hub} from "aws-amplify";
import { API, graphqlOperation } from 'aws-amplify';
import {getProduct } from '../graphql/queries';
import Card from "../../component/Card";
import {createCartItems} from '../graphql/mutations';
const [getProducts, setGetProducts] = useState([])
const [user, setUser] = useState(undefined)
const prodId = route.params;
const [quantity, setQuantity] = useState(1);
useEffect(()=>{
fetchProducts();
},[])
const fetchProducts = async ()=>{
const productsData = await API.graphql(graphqlOperation(getProduct, {id: prodId.productId}))
setGetProducts(productsData.data.getProduct)const checkUser = async ()=>{
try {
const authUser = await Auth.currentAuthenticatedUser({bypassCache: true})
// console.log(authUser)
setUser(authUser)
} catch (error) {
setUser(null)
}
}
useEffect(()=>{
checkUser();
},[])
useEffect(() => {
const listener = data => {
if (data.payload.event === 'signIn' || data.payload.event === 'signOut') {
checkUser();
}
};
Hub.listen('auth', listener);
return () => Hub.remove('auth', listener);
}, []);
const onAddToCart = async ()=>{
const userData = await Auth.currentAuthenticatedUser();
const newCartItem = {
userId: userData.attributes.sub,
productId: getProducts.id,
productPrice: getProducts.price,
productTitle: getProducts.title,
productImage: getProducts.image,
quantity: quantity,
}
API.graphql(graphqlOperation(createCartItems, {input: newCartItem}))
if(user === undefined || !user){
Alert.alert(
'خطأ تسجيل الدخول',
'لابد من تسجيل الدخول للأستمرار',
[
{
text : 'سجل الدخول',
onPress: ()=> navigation.navigate('SignIn')
},
{
text: 'اغلاق'
}
]
)
}else{
navigation.navigate('CartItem')
}
}
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
setQuantity(1);
});
return unsubscribe;
}, [navigation]);
return(
<View style={styles.container}>
<View style={styles.headerImageView}>
<Card image={{uri: getProducts.image}} promotion={getProducts.promotionRate} />
</View>
<View style={styles.descriptionPriceView}>
<Text style={styles.price}>{getProducts.price} ج.م </Text>
<Text style={styles.descriptionText}>{getProducts.description}</Text>
</View>
<View style={styles.quantity}>
<Text style={styles.quantityText}>الكمية</Text>
<Text style={styles.quantityText}>{quantity}</Text>
</View>
<View style={styles.share}>
<TouchableOpacity>
<Image style={{width: 30, height: 30, margin: 10}} source={{uri: 'https://firebasestorage.googleapis.com/v0/b/test-5c03d.appspot.com/o/NakoA%2FLogos%2Ffacebook.png?alt=media&token=6ba4cb45-4f42-42f5-b5c7-2063eeffd1a8'}}/>
</TouchableOpacity>
<TouchableOpacity>
<Image style={{width: 30, height: 30, margin: 10}} source={{uri: 'https://firebasestorage.googleapis.com/v0/b/test-5c03d.appspot.com/o/NakoA%2FLogos%2Finstagram.png?alt=media&token=f06f3b53-f026-43f0-a855-8ace6ed7b1b8'}}/>
</TouchableOpacity>
<TouchableOpacity>
<Icon name="sharealt" size={30} color="black" />
</TouchableOpacity>
</View>
<View style={styles.bottomAddToCartView}>
<View>
<TouchableOpacity onPress={onAddToCart}>
<Text style={styles.bottomAddToCartText}>أضف للسلة</Text>
</TouchableOpacity>
</View>
<View style={styles.addQuantity}>
<TouchableOpacity onPress={()=>{
setQuantity(Math.max(quantity - 1, 1))
}}>
<Text style={styles.minProd}>-</Text>
</TouchableOpacity>
<Text style={styles.numProd}>{quantity}</Text>
<TouchableOpacity onPress={()=>{
setQuantity(Math.min(quantity + 1, 100))
}}>
<Text style={styles.plusProd}>+</Text>
</TouchableOpacity>
</View>
</View>
</View>
)};
this is the CartItem screen
import React, {useEffect, useState} from "react";
import { View, Text, Image, StyleSheet, FlatList } from 'react-native';
import { Auth } from "aws-amplify";
import Colors from "../../constants/Colors";
import { DataStore} from "aws-amplify";
import ItemQuantity from '../../component/ItemQuantity';
import {CartItems} from '../models'
const CartItem = ()=>{
const [cartItem, setCartItem] = useState([]);
const [user, setUser] = useState('');
useEffect(()=>{
fetchCartItem();
userData();
},[])
const fetchCartItem = async ()=>{
try {
const cartitemsData = await DataStore.query(
CartItems, c=>c.userId("eq", user).quantity("gt", 0)
)
setCartItem(cartitemsData)
} catch (error) {
console.log(error)}}
return(
<View style={styles.screen}>
<FlatList data={cartItem} renderItem={({item})=>{
return(
<View style={styles.container}>
<View style={styles.cardView}>
<View style={styles.imageView}>
<Image style={styles.image} source={{uri: item.productImage}} />
</View>
<View style={styles.detailsView}>
<Text style={styles.titl}>{item.productTitle}</Text>
<Text style={styles.price}>{item.productPrice} ج.م </Text>
</View>
</View>
<ItemQuantity quantityItem={item.quantity}
updatedItemId={item.id}
userId={item.userId}
productID={item.productID}
productPrice={item.productPrice}
productTitle={item.productTitle}
productImage={item.productImage}
/>
</View>)}}/>
</View>)};
this is itemQuantity componenet
import React, {useState, useEffect} from "react";
import {View, Text, TouchableOpacity, StyleSheet} from 'react-native'
import { AntDesign } from '#expo/vector-icons';
import { DataStore } from "aws-amplify";
import { CartItems } from "../src/models";
const ItemQuantity = ({quantityItem, updatedItemId})=>{
const [quantity, setQuantity] = useState(quantityItem);
const onMinusPressed = ()=>{
setQuantity(Math.max(1, quantity - 1))
};
useEffect(()=>{fetchUpdatedCartItemPlus();},[quantity])
const fetchUpdatedCartItemPlus= async()=>{
const original = await DataStore.query(CartItems, updatedItemId)
await DataStore.save(CartItems.copyOf(original, updated=>{
updated.quantity = quantity
}))};
return(
<View style={styles.quantity}>
<TouchableOpacity onPress={onMinusPressed}>
<AntDesign name={'minuscircleo'} size={24} color="black" />
</TouchableOpacity>
<TouchableOpacity onPress={cartitemDelete}>
<AntDesign name={'delete'} size={24} color="black" />
</TouchableOpacity>
<Text style={styles.itemQuantity}>{quantity}</Text>
<TouchableOpacity onPress={onPlusPressed}>
<AntDesign name="pluscircleo" size={24} color="black" />
</TouchableOpacity>
</View>
)};
Related
I am trying to achieve two column layout if i click something inside tab (for example list of Chats) chat should show in right, now its appears inside left column
can you point me to correct way?
if someone needs this is how I make this
/* eslint-disable react-native/no-inline-styles */
import * as React from 'react';
import {Button, Text, View} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
function ContactScreen({route}) {
const {contactId} = route.params;
return (
<View style={{flexDirection: 'row', flex: 1}}>
<Tab.Navigator
initialRouteName="Contacts"
style={{flex: 1}}>
<Tab.Screen name="Chats" component={ChatsTab} />
<Tab.Screen name="Contacts" component={ContactsTab} />
</Tab.Navigator>
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Contact {contactId}</Text>
</View>
</View>
);
}
function ChatScreen({route}) {
const {chatId} = route.params;
return (
<View style={{flexDirection: 'row', flex: 1}}>
<Tab.Navigator
initialRouteName="Chats"
style={{flex: 1}}>
<Tab.Screen name="Chats" component={ChatsTab} />
<Tab.Screen name="Contacts" component={ContactsTab} />
</Tab.Navigator>
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Chat {chatId}</Text>
</View>
</View>
);
}
function ChatsTab({navigation}) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>chats tab</Text>
<Button
title="open chat 1"
onPress={() =>
navigation.navigate('Chat', {
screen: 'Chats',
chatId: 1,
})
}
/>
<Button
title="open chat 2"
onPress={() =>
navigation.navigate('Chat', {
screen: 'Chats',
chatId: 2,
})
}
/>
<Button
title="open chat 5"
onPress={() =>
navigation.navigate('Chat', {
screen: 'Chats',
chatId: 5,
})
}
/>
</View>
);
}
function ContactsTab({navigation}) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>contacts tab</Text>
<Button
title="open contact 4"
onPress={() =>
navigation.navigate('Contact', {
screen: 'Contacts',
contactId: 4,
})
}
/>
<Button
title="open contact 3"
onPress={() =>
navigation.navigate('Contact', {
screen: 'Contacts',
contactId: 3,
})
}
/>
</View>
);
}
const HomeStack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<HomeStack.Navigator screenOptions={{headerShown: false}}>
<HomeStack.Screen
initialParams={{chatId: 0}}
name="Chat"
options={{animationEnabled: false}}
component={ChatScreen}
/>
<HomeStack.Screen
initialParams={{contactId: 0}}
options={{animationEnabled: false}}
name="Contact"
component={ContactScreen}
/>
</HomeStack.Navigator>
</NavigationContainer>
);
}
I am using expo ^40.0.0.
I am trying to get react-native-gesture-handler components to work in a <Modal> from react-native in Android. I followed docs here - https://docs.swmansion.com/react-native-gesture-handler/docs/#for-library-authors
It stays to wrap in <GestureHandlerRootView>, I did this but it doesn't fix the issue.
I created a snack demo of issue - https://snack.expo.io/#noitidart/frisky-carrot
Here you see a "Tapped here count: 0". Please try tapping it, you will see it doesn't increment the counter.
You will see a "RN BUTTON" this works but it is not from react-native-gesture-handler.
Here is my code from the snack:
import * as React from 'react';
import { Text, View, Modal, Button as RNButton } from 'react-native';
import {
BaseButton,
GestureHandlerRootView
} from 'react-native-gesture-handler';
export default function App() {
return (
<Modal animationType="slide" transparent={false}>
<Example />
</Modal>
);
}
const Example = function () {
const [cnt, setCnt] = React.useState(0);
return (
<View style={{ justifyContent: 'center', flex: 1 }}>
<RNButton title="rn button" onPress={() => alert('hi')} />
<GestureHandlerRootView>
<BaseButton
onPress={() => {
// alert('pressed');
setCnt(cnt + 1);
}}
>
<View style={{ backgroundColor: 'steelblue', height: 100 }}>
<Text>Tapped here count: {cnt}</Text>
</View>
</BaseButton>
</GestureHandlerRootView>
</View>
);
};
I am using react native and AWS for my backend. I am trying to add a feature that allows users to be able to change their password after they have logged into the app. I believe I should be able to do this using the Auth.changePassword function (AWS doc). For some reason, I am getting an error that the username or password is incorrect.
When testing, the user returns a CognitoUser, the oldPassword is definitely correct, and the newPassword throws errors if it does not meet the character settings. I am lost as to what the issue could be. Should I also be using completeChangePassword in conjunction?
import React from 'react';
import {Auth} from '#aws-amplify/auth';
export default class ChangePassword extends React.Component {
constructor(props) {
super(props);
this.state = {
oldPassword: '',
newPassword: '',
};
}
handleChangePassword = async () => {
const { oldPassword, newPassword } = this.state;
await Auth.currentAuthenticatedUser()
.then(user => {
return Auth.changePassword(user, oldPassword, newPassword)
})
.then(data => console.log(data))
.then(user => this.props.navigation.navigate('Home'))
.catch(err => console.log(err))
}
render() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.text_header}>Change Password</Text>
</View>
<Animatable.View
animation='bounceInUp'
style={styles.footer}
>
<View style={styles.footer}>
<Text style={styles.text_footer}>Old Password</Text>
<View style={styles.action}>
<Feather
name='lock'
color='#05375a'
size={20}
/>
<TextInput
placeholder='Enter old password'
style={styles.textInput}
autoCapitalize='none'
onChangeText={(val) => this.setState({ oldPassword: val })}
/>
</View>
<Text style={styles.text_footer}>New Password</Text>
<View style={styles.action}>
<Feather
name='lock'
color='#05375a'
size={20}
/>
<TextInput
placeholder='Enter new password'
style={styles.textInput}
autoCapitalize='none'
onChangeText={(val) => this.setState({ newPassword: val })}
/>
</View>
<View style={styles.button}>
<TouchableOpacity
style={styles.signIn}
onPress={this.handleChangePassword}
>
<LinearGradient
colors={['#55B142', '#155843']}
style={styles.signIn}
>
<Text style={[ styles.textSign, { color: '#fff' }]}>Submit</Text>
</LinearGradient>
</TouchableOpacity>
</View>
<View style={{ alignItems: 'center'}}>
<View style={{flexDirection: 'row', alignItems: 'center', paddingTop: 5 }}>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('EditProfile')}
style={[styles.signUp, {
//borderColor: '#155843',
//borderWidth: 1,
marginTop: 15,
}]}
>
<Text style={[styles.textSign, {
color: '#155843'
}]}>Go Back</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Animatable.View>
</View>
);
}}
Turns out the problem was that the account that was logged in wasn't verified which is why it would reject the email address.
I am working in demo Application for learning React native And I need to render clickable FlatList item.
Here is Render and itemClick function:
render() {
return (
<View style={styles.container}>
<View>
<FlatListData list={this.state.itemList} />
</View>
</View>
);
}
itemClick(item){
console.log('click on item', item);
}
I have already declare FlatListData functional component for that :
const FlatListData = ({list}) => {
return (
<FlatList
data={list}
keyExtractor={(item, index) => index.toString()}
renderItem={itemList}
/>
);
};
In FlatList component, RenderItem attribute have again custom component for single item i.e :
const itemList = ({item, index}) => {
return (
<TouchableOpacity onPress={this.itemClick(item)} style={styles.catalogContainer}>
<Image
source={{uri: item.img}}
style={styles.imageStyle}
/>
</TouchableOpacity>
);
};
But this code is not working. It will give me error like : Undefined is not an object (eveluting _this.itemClick)
Here in which way and where Should I write click function for item ?
Any one have a answer that can solve my problem ?
NOTE :
All code is written in one js file. My Render function is inside my class component. and other two declarative component are out of that class component.
I need that item inside that click function. But I don't know how to pass parameter inside function.
There are a couple of things you can do to fix your problem.
You need to move your itemClick function inside itemList as an arrow function.
const itemList = ({ item, index }) => {
itemClick = item => {
console.log("click on item", item);
};
return (
<TouchableOpacity
// onPress={this.itemClick(item)}
onPress={() => this.itemClick(item)}
style={styles.catalogContainer}
>
<Image source={{ uri: item.img }} style={styles.imageStyle} />
</TouchableOpacity>
);
};
Pass your itemClick function as props to child component
render() {
return (
<View style={styles.container}>
<FlatListData list={this.state.itemList} onItemClick={this.itemClick} />
</View>
);
}
itemClick = (item) => {
console.log('click on item', item);
}
Now you can call onItemClick prop inside itemList
const FlatListData = ({ list, onItemClick }) => {
itemList = ({ item }) => (
<TouchableOpacity
onPress={() => onItemClick(item)}
style={styles.catalogContainer}
>
<Image source={{ uri: item.img }} style={styles.imageStyle} />
</TouchableOpacity>
);
return (
<FlatList
data={list}
keyExtractor={(item, index) => index.toString()}
renderItem={this.itemList}
/>
);
};
Hope this helps you. Feel free for doubts.
In my react-native app I'm returning data from an api, everything is working except for my layout it got messed up, i have an albums list and i need to display each 2 albums next to each other, but all of them are getting displayed under each other, here is my code:
Album detail:
const AlbumDetails= (props) => {
return(
<Album>
<Image source={{ uri: props.album.thumbnail_image }}/>
<Text>{props.album.title}</Text>
</Album>
);
};
export default AlbumDetails;
Album:
const Album= (props) => {
return(
<View style={{flex: 1}}>
<View style={{display: "flex", flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<View style={styles.albumContainer}>
<View>{props.children}</View>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
albumContainer: {
backgroundColor: 'red',
width: '50%',
height: 180,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 20,
flexDirection: 'column'
},
});
export default Album;
I suggest to use FlatList component with numColumns param equal 2. Check Expo Snack or code below.
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<FlatList
style={{ margin: 5 }}
data={data}
numColumns={2}
keyExtractor={(item, index) => item.id}
renderItem={dataItem => (
<AlbumDetails
album={{
title: dataItem.item.title,
thumbnail_image: dataItem.item.image,
}}
/>
)}
/>
</View>
);
}
}