I have an App in which there is a View with two CardViews that each direct me to different screens.
Now I have added another CardView, and there are already three.
The problem is that neither in iOS nor in Android the third of the CardView is shown in full, only part of it is shown as I show in the screenshot.
I have tried ScrollView since it is a list of few items and Scroll works, but it does not stop and when I release my finger from the screen, the screen returns to its initial position.
Also the scroll does the entire screen, including the Header, which should not be the case
I have also tried using FlatList, but I get errors
I have played with the View styles, but I do not understand the problem that is offered so that the Scroll does not work within the View.
I show the code of the file, with the third of the CardView added.
How can I correct this and make the screen Scroll to be able to access all the CardViews on the screen?
import React, { Component } from "react";
import _ from "lodash"
import ToolBar from "../../../component/ToolBar";
import styleApp from "../../../../res/style/style";
import AppText from "../../../component/Text";
import { strings } from "../../../config/i18n/i18n";
import CardView from "../../../component/CardView";
import { sizeWidth } from "../../../util/Size";
import NavigationActions from "../../../router/NavigationActions"
import {
AsyncStorage,
Image,
StyleSheet,
View,
FlatList,
ScrollView,
List,
SectionList
} from "react-native"
export default class PaintScreen extends Component {
state = {
isLoading: true,
studiedAlphabet: [],
studiedNumber: []
};
componentDidMount() {
AsyncStorage.getAllKeys((error, keys) => {
AsyncStorage.multiGet(keys, (error, stores) => {
const alphabets = [];
const numbers = [];
stores.map((result, i, store) => {
// get at each store's key/value so you can work with it
if (store[i] != null) {
//data provide is [key: value] - value [[]]
if (store[i][0] == "learned_alphabets") {
alphabets.push(JSON.parse(store[i][1]));
}
if (store[i][0] == "learned_numbers") {
numbers.push(JSON.parse(store[i][1]));
}
}
if (i == keys.length - 1) {
this.setState({
isLoading: false,
studiedAlphabet: alphabets[0],
studiedNumber: numbers[0]
});
}
});
});
});
}
render() {
if (!this.state.isLoading) {
return (
<View style={styles.containerMio}>
<Image
style={styles.backgroundImage}
source={require("../../../../res/images_paint/background/backgroundA_2x.png")}
/>
<Image
style={styles.newPaintImage}
source={require("../../../../res/images_paint/mainScreen/new_paint_ball_2x.png")}
/>
{this.renderToolbar()}
<View
style={{
width: "100%",
alignItems: "center",
position: "absolute",
top: sizeWidth(20)
}}
>
{this.renderAnimal()}
{this.renderObject()}
{this.renderAnimal()}
</View>
</View>
)
} else {
return null;
}
}
renderToolbar = () => {
return (
<ToolBar
center={
<AppText style={styleApp.ToolBarText}>
{strings("paint.title")}
</AppText>
}
/>
);
};
renderAnimal = () => {
return (
<CardView
image={require("../../../../res/images_paint/newPaintingScreen/animals_2x.png")}
styleImg={styles.styleAnimal}
title={strings("paint.animals")}
backgroundColor={"#fff989"}
colorButton={"#FE6230"}
onPress={() =>
NavigationActions.navigate("PaintAnimals", {
studiedNumber: this.state.studiedNumber,
studiedAlphabet: this.state.studiedAlphabet
})
}
/>
);
}
renderObject = () => {
const { studiedAlphabet } = this.state;
return (
<CardView
image={require("../../../../res/images_paint/newPaintingScreen/object_2x.png")}
styleImg={styles.styleObject}
title={strings("paint.objects")}
backgroundColor={"#B29FFF"}
colorButton={"#FE6230"}
onPress={() =>
NavigationActions.navigate("PaintObjects", {
studiedNumber: this.state.studiedNumber,
studiedAlphabet: this.state.studiedAlphabet
})
}
/>
);
}
renderAnimal = () => {
return (
<CardView
image={require("../../../../res/images_paint/newPaintingScreen/animals_2x.png")}
styleImg={styles.styleAnimal}
title={strings("paint.animals")}
backgroundColor={"#fff989"}
colorButton={"#FE6230"}
onPress={() =>
NavigationActions.navigate("PaintAnimals", {
studiedNumber: this.state.studiedNumber,
studiedAlphabet: this.state.studiedAlphabet
})
}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column'
},
containerMio: {
flex: 1,
flexDirection: "column"
},
backgroundImage: {
resizeMode: "stretch",
position: "absolute",
width: "100%",
height: "100%"
},
newPaintImage: {
position: "absolute",
width: "100%",
height: "100%"
},
styleAnimal: {
resizeMode: "center",
width: sizeWidth(70),
height: sizeWidth(50),
marginLeft: sizeWidth(-5),
},
styleObject: {
resizeMode: "center",
width: sizeWidth(70),
height: sizeWidth(50),
marginLeft: sizeWidth(-5),
}
})
Related
In my React Native applikation I render a <FlatList> with Images. I pass the direct imageurl as source into the <Image> Component.
<FlatList
data={this.state.images}
keyExtractor={item => item.imageToken}
renderItem={({ item }) => (
<Image key={item.imageToken} style={{ marginRight: 2, marginTop: 2, width: '50%', opacity: 1 }} source={{ uri: item.imageUrl }} alt="Alternate Text" size="xl" /> )} />
This means that the images are loaded in a different order because they are also different sizes. I would like to show a placeholder during loading.
The listAll() function resets isLoading to false before all images are displayed. Is there a 'trigger' when an image is fully visible in the view? I can't just build a single state for each image - I guess.
There will be many hundreds of pictures!
I think it's important to know that I extract the url from the google firestore images and store they as an array in state beforehand. See function getDownloadURL
Fullcode
import React, { Component } from 'react'
import { StyleSheet, SafeAreaView, ActivityIndicator } from 'react-native'
import { Image, FlatList, Center, Box } from "native-base"
import EventGalleryHeader from '../components/EventGalleryHeader.js'
import { getStorage, ref, getDownloadURL, list, listAll } from "firebase/storage"
import { LongPressGestureHandler, State } from 'react-native-gesture-handler'
export default class EventScreen extends Component {
constructor(props) {
super(props);
this.storage = getStorage()
this.pathToImages = '/eventimages/'
this.eventImageSource = this.props.route.params.eventData.key
this.imagesRef = this.pathToImages + this.eventImageSource
this.state = {
isLoading: true,
images: [],
event: {
adress: this.props.route.params.eventData.adress,
hosts: this.props.route.params.eventData.hosts,
description: this.props.route.params.eventData.description,
eventtitle: this.props.route.params.eventData.eventtitle,
invitecode: this.props.route.params.eventData.invitecode,
key: this.props.route.params.eventData.key,
timestamp: this.props.route.params.eventData.timestamp,
}
}
}
componentDidMount() {
this.getEventImageData()
}
componentWillUnmount() {
}
getEventImageData() {
const images = []
const event = {
adress: this.props.route.params.eventData.adress,
description: this.props.route.params.eventData.description,
eventtitle: this.props.route.params.eventData.eventtitle,
key: this.props.route.params.eventData.key,
timestamp: this.props.route.params.eventData.timestamp,
}
listAll(ref(this.storage, this.imagesRef))
.then((res) => {
res.items.forEach((itemRef) => {
getDownloadURL(itemRef)
.then((url) => {
const indexOfToken = url.indexOf("&token=")
const token = url.slice(indexOfToken + 7)
images.push({
"imageUrl": url,
"imageToken": token
});
this.setState({
images,
event,
isLoading: false,
});
// console.log(this.state.images)
})
.catch((error) => {
switch (error.code) {
case 'storage/object-not-found':
break;
case 'storage/unauthorized':
break;
case 'storage/canceled':
break;
case 'storage/unknown':
break;
}
});
});
}).catch((error) => {
});
}
onLongPress = (event) => {
if (event.nativeEvent.state === State.ACTIVE) {
alert("I've been pressed for 800 milliseconds");
}
};
render() {
if (this.state.isLoading) {
return (<Center style={styles.container} _dark={{ bg: "blueGray.900" }} _light={{ bg: "blueGray.50" }}>
<ActivityIndicator size="large" color="#22d3ee" />
</Center>
)
} else {
return (
<SafeAreaView style={styles.container} >
<FlatList _dark={{ bg: "blueGray.900" }} _light={{ bg: "blueGray.50" }}
style={styles.list}
numColumns={2}
ListHeaderComponent={<EventGalleryHeader data={this.state.event} />}
data={this.state.images}
keyExtractor={item => item.imageToken}
renderItem={({ item }) => (
<LongPressGestureHandler
onHandlerStateChange={this.onLongPress}
minDurationMs={800}
>
<Image key={item.imageToken} style={{ marginRight: 2, marginTop: 2, width: '50%', opacity: 1 }} source={{ uri: item.imageUrl }} alt="Alternate Text" size="xl" />
</LongPressGestureHandler>
)}
/>
</SafeAreaView>
);
}
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
maxHeight: 450,
width: '100%',
height: 200,
overflow: 'hidden',
},
list: {
alignSelf: 'center',
},
gallery: {
flex: 1,
width: '100%',
flexDirection: 'row',
}
})
And again it shows how important it is to read the documentation properly beforehand and to look there first if you have any questions.
You can achieve the behavior I mentioned above with the following parameters.
loadingIndicatorSource link
Similarly to source, this property represents the resource used to render the loading indicator for the image, displayed until image is ready to be displayed, typically after when it got downloaded from network.
onLoad link
Invoked when load completes successfully.
onLoadEnd link
Invoked when load either succeeds or fails.
onLoadStart link
Invoked on load start.
Example: onLoadStart={() => this.setState({loading: true})}
I'm really sorry to bother you, but I try to learn React-Native. I'm trying to make a weather app and I have this error :
Text strings must be rendered within a <Text> component.
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4137:8 in <anonymous>
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4134:2 in createTextInstance
- ... 9 more stack frames from framework internal
Here's are my two files of code:
weather-card.js
import React, {Component} from "react";
import {Animated, View, Text, PanResponder, Image} from "react-native";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp
} from "react-native-responsive-screen";
const CARD_INITIAL_POSITION_Y = hp("80%");
const CARD_INITIAL_POSITION_X = wp("5%");
const TRESHOLD_TO_TOP = hp("75%");
const TRESHOLD_TO_BOTTOM = hp("70%");
const CARD_OPEN_POSITION = hp("45%");
const MAX_DRAG_ZONE_WHEN_OPEN = hp("65%");
const ICON_URL = "http://openweathermap.org/img/w/";
class WeatherCard extends Component {
state = { panResponder : undefined, isOpen: false};
componentDidMount() {
this.position = new Animated.ValueXY();
this.position.setValue({x:CARD_INITIAL_POSITION_X, y: CARD_INITIAL_POSITION_Y});
const panResponder = PanResponder.create({
onStartShouldSetPanResponder:() => true,
onPanResponderMove:(e,gesture) => {
if(!(this.state.isOpen && gesture.y0 > MAX_DRAG_ZONE_WHEN_OPEN)) {
this.position.setValue({
x:CARD_INITIAL_POSITION_X,
y: gesture.moveY
});
}
},
onPanResponderRelease:(e, gesture) => {
if (!this.state.isOpen){
if(gesture.moveY <= TRESHOLD_TO_TOP) {
this.setOpenPosition(() => this.setState({isOpen: true}));
} else {
this.resetPosition();
}
} else {
if(gesture.moveY <= TRESHOLD_TO_BOTTOM){
this.setOpenPosition()
} else {
if(gesture.y0 < MAX_DRAG_ZONE_WHEN_OPEN){
this.resetPosition(() => this.setState({isOpen: false}))
}
}
}
}
});
this.setState({panResponder})
}
setOpenPosition = (done) => {
Animated.spring(this.position, {
toValue: {x: CARD_INITIAL_POSITION_X, y : CARD_OPEN_POSITION}
}).start(() => done && done());
};
resetPosition = (done) => {
Animated.spring(this.position, {
toValue: {x: CARD_INITIAL_POSITION_X, y : CARD_INITIAL_POSITION_Y}
}).start(() => done && done())
};
getCardStyle() {
return {
width: wp("90%"),
height: hp("110%"),
borderRadius: 10,
zIndex: 2,
backgroundColor: "white",
elevation: 1,
shadowColor: "black",
shadowOpacity: 0.2,
shadowOffset: { height: 2, width: 2 },
position: "absolute",
left: CARD_INITIAL_POSITION_X,
padding: hp("2%"),
...this.position.getLayout()
};
}
renderHeader(){
return (
<View
style={{
justifyContent: "center",
alignItems: "center"
}}
>
<Text style={{ fontSize: 30, marginBottom: hp("1%") }}>
{this.props.currentWeather.name}
</Text>
<View style={{ flexDirection: "row" }}>
<Text style={{ marginTop: hp("1%"), fontSize: 35 }}>
{this.props.currentWeather.main.temp}
</Text>
<Image
style={{ height: 60, width: 60 }}
source={{
uri: `${ICON_URL}${this.props.currentWeather.weather[0].icon}.png`
}}
/>
</View>
</View>
)
}
render() {
return this.state.panResponder ? <Animated.View {...this.state.panResponder.panHandlers} style={this.getCardStyle()}> {this.renderHeader()} </Animated.View> : <View />;
}
}
export default WeatherCard;
And search-screen.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MapView from 'react-native-maps';
import {SearchBar} from "react-native-elements";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp
} from "react-native-responsive-screen";
import {connect} from "react-redux";
import {getCurrentWeatherByCity} from "../actions/index";
import WeatherCard from "../components/weather-card";
const DEFAULT_COORD = {
lat: 48.859268,
lng: 2.347060
};
class SearchScreen extends React.Component {
state={search: ""};
updateSearch = search => {
this.setState({search})
};
submitSearch = () => {
this.props.getCurrentWeatherByCity(this.state.search);
console.log(this.state.search)
};
render() {
console.log(this.props.currentWeather);
return (
<View style={styles.container}>
<MapView style={{flex : 1}}
region={{latitude : this.props.currentWeather ? this.props.currentWeather.coord.lat : DEFAULT_COORD.lat, longitude : this.props.currentWeather ? this.props.currentWeather.coord.lon : DEFAULT_COORD.lng, latitudeDelta: 0.2000, longitudeDelta: 0.1000}}
scrollEnabled={false}
liteMode={true} />
{this.props.currentWeather && <WeatherCard currentWeather={this.props.currentWeather} />}
<SearchBar
lightTheme
onChangeText={this.updateSearch}
value={this.state.search}
onSubmitEditing={this.submitSearch}
containerStyle={{
position: "absolute",
bottom: hp("50%"),
left: wp("5%"),
width: wp("90%")
}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
});
const mapStateToProps = (store) => {
return {
currentWeather : store.weather.currentWeather
}
};
const mapDispatchToProps = {
getCurrentWeatherByCity
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchScreen)
I was asking myself if the problem could be :
{this.props.currentWeather && <WeatherCard currentWeather={this.props.currentWeather} />}
Because it's not inside a Text component...
If you can help, it could be really great :D
If you need other resources, do not hesitate!
Thanks in advance :)
It might be because of this try this:
change
{this.props.currentWeather.name}
to
{this.props.currentWeather.name != undefined ? this.props.currentWeather.name : null}
Similarly do the above thing for {this.props.currentWeather.main.temp}
Hope this helps!
I would like to post my answer if someone has the same problem. The error says that a text string is "outside" a component. However it wasn't the case. It was indentation's problem. So don't forget to pay attention about that :D Expo can be mean !
I want to animate this image carousel in reactNative, but have no idea how to start. Read the documentation about animations but still really stuck, have no idea how to incorporate it in. I tried it this way but keep getting a big fat error. Help!
import React from 'react';
import {StyleSheet, View, ScrollView, Dimensions, Image, Animated} from 'react-native'
const DEVICE_WIDTH = Dimensions.get('window').width;
class BackgroundCarousel extends React.Component {
scrollRef = React.createRef();
constructor(props) {
super(props);
this.state = {
selectedIndex: 0,
opacity: new Animated.Value(0)
};
}
componentDidMount = () => {
Animated.timing(this.state.opacity , {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
setInterval(() => {
this.setState(
prev => ({ selectedIndex: prev.selectedIndex ===
this.props.images.length - 1 ? 0 : prev.selectedIndex +1 }),
() => {
this.scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * this.state.selectedIndex
});
}
);
}, 6000);
};
componentWillUnmount() {
clearInterval(this.setState);
}
render() {
const {images} = this.props
const {selectedIndex} = this.state
return (
<Animated.Image
onLoad={this.onLoad}
{...this.props}
style={[
{
opacity: this.state.opacity,
},
this.props.style,
]}
/>
<View style= {{height: "100%", width: "100%"}}>
{this.props.children}
<ScrollView
horizontal
pagingEnabled
scrollEnabled={false}
ref={this.scrollRef}
>
{images.map(image => (
<Image
key={image}
source={image}
style={styles.backgroundImage}
/>
))}
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create ({
backgroundImage: {
height: '100%',
width: DEVICE_WIDTH,
}
});
export default BackgroundCarousel;
Any help would be appreciated. Don't know where I'm going wrong. Basically trying to add a fade effect when my background carousel changes from image to image.
I have fixed your code and removed all errors, copy-paste it in https://snack.expo.io/ and give it some time to load.
Note: I have removed this.props.images for website demo, please change in your real project.
Working fade carousal: https://snack.expo.io/#rajrohityadav/fade-carosal
But I have not implemented this using React Animation.
import React from 'react';
import {StyleSheet, View, ScrollView, Dimensions, Image, Animated} from 'react-native'
const DEVICE_WIDTH = Dimensions.get('window').width;
export default class BackgroundCarousel extends React.Component {
scrollRef = React.createRef();
constructor(props) {
super(props);
this.state = {
selectedIndex: 0,
opacity: new Animated.Value(0)
};
}
componentDidMount = () => {
Animated.timing(this.state.opacity , {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
setInterval(() => {
this.setState(
prev => ({ selectedIndex: prev.selectedIndex ===
3 - 1 ? 0 : prev.selectedIndex +1 }),
() => {
this.scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * this.state.selectedIndex
});
}
);
}, 6000);
};
componentWillUnmount() {
clearInterval(this.setState);
}
render() {
const images =[
'https://image.shutterstock.com/image-vector/dragon-scream-vector-illustration-tshirt-260nw-1410107855.jpg','https://image.shutterstock.com/image-vector/dragon-head-vector-illustration-mascot-600w-1201914655.jpg',
'https://i.pinimg.com/474x/b7/1a/bb/b71abb6dd7678bbd14a1f56be5291747--dragon-illustration-samurai-tattoo.jpg']//this.props
const {selectedIndex} = this.state
return (
<>
<Animated.Image
onLoad={this.onLoad}
{...this.props}
style={[
{
opacity: this.state.opacity,
},
this.props.style,
]}
/>
<View style= {{height: "100%", width: "100%"}}>
{this.props.children}
<ScrollView
horizontal
pagingEnabled
scrollEnabled={false}
ref={this.scrollRef}
>
{images.map(image => (
<Image
key={image}
source={image}
style={styles.backgroundImage}
/>
))}
</ScrollView>
</View>
</>
)
}
}
const styles = StyleSheet.create ({
backgroundImage: {
height: '100%',
width: DEVICE_WIDTH,
}
});
You can also use a simple & optimized library react-native-fadecarousel and use it like this:
import React from 'react'
import { View, StyleSheet, Image } from 'react-native';
import FadeCarousel from 'react-native-fadecarousel';
const FadeCarouselScreen = () => {
const images = [
'https://image.shutterstock.com/image-vector/dragon-scream-vector-illustration-tshirt-260nw-1410107855.jpg',
'https://image.shutterstock.com/image-vector/dragon-head-vector-illustration-mascot-600w-1201914655.jpg',
'https://i.pinimg.com/474x/b7/1a/bb/b71abb6dd7678bbd14a1f56be5291747--dragon-illustration-samurai-tattoo.jpg'
];
return <FadeCarousel
loop
fadeAnimationDuration={1000}
autoPlay={{enable: true , delay: 1000 }}>
{
images.map((image, index) => {
return <View key={`slide ${index}`} style={styles.slideItem}>
<Image style={styles.image} resizeMethod="resize" resizeMode="cover" source={{ uri: image }}/>
</View>
})
}
</FadeCarousel>
}
const styles = StyleSheet.create({
image: {
width: "100%",
height: 300
},
slideItem: {
width: "100%",
height: 300,
justifyContent: "center",
alignContent: "center"
}
})
export default FadeCarouselScreen
I have a class function that filters my props then uses it to render a deckswiper. The problem is that the function doesn't complete by the time the deckswiper renders so it renders a blank deckswiper. Is there a way that I can either make it rerender when the deck is complete or make the function asynchronous? Or should I be filtering this data elsewhere? When I first refresh the page the deckswiper is blank, then if I click my button to add more data it seems to work.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Container, View, DeckSwiper, Text, Spinner, Button, Icon } from 'native-base';
import { findDogs, addDog, blacklistDog } from '../actions';
import SwipeDogItem from './SwipeDogItem';
class SwipeDogSelect extends Component {
componentWillMount() {
this.props.findDogs();
}
shouldComponentUpdate(nextProps) {
if(nextProps.blacklist !== this.props.blacklist) {
return false;
}
return true;
}
dogBreedString(breed) {
if (Array.isArray(breed)) {
let breedString = '';
for (let i = 0; i < breed.length; i++) {
breedString += `${breed[i].$t}, `;
}
return breedString.slice(0, -2);
}
return breed.$t;
}
filterDogs() {
const { dogs, gender, selectedBreeds, blacklist, size } = this.props;
return dogs.filter((pet) => {
return blacklist.indexOf(pet.id.$t) === -1 &&
(selectedBreeds > 248 || Object.values(pet.breeds.breed).filter(val => !selectedBreeds.includes(val)).length < 1) &&
(gender === 'either' || pet.gender.$t === gender) &&
(size === 'any' || pet.size.$t === size);
});
}
renderDeckSwiper() {
console.log(this.props.dogs);
if (this.props.findingDogs || typeof this.props.dogs === 'string') {
return (<Spinner color='black' />);
} else if (this.props.dogs === undefined) {
return (
<Text>No dogs found.</Text>
);
}
return (
<DeckSwiper
ref={mr => (this._deckSwiper = mr)}
dataSource={this.filterDogs()}
renderItem={dog => {
return (
<SwipeDogItem
dog={dog}
breed={this.dogBreedString(dog.breeds.breed)}
/>
);
}}
renderEmpty={() => {
return (<Text>No dogs found. Try less filters or refreshing.</Text>);
}}
onSwipeRight={(dog) => { this.props.addDog(dog); }}
onSwipeLeft={(dog) => { this.props.blacklistDog(dog.id.$t); }}
loop={false}
/>
);
}
render() {
return (
<Container>
<View>
{this.renderDeckSwiper()}
</View>
<View
style={styles.buttonViewStyles}
>
<Button
style={styles.buttonsStyles}
rounded
large
onPress={() => {
this.props.blacklistDog(this._deckSwiper._root.state.selectedItem.id.$t);
this._deckSwiper._root.swipeLeft();
}}
>
<Icon style={styles.buttonIconStyles} name="close" fontSize='40' color='red' />
</Button>
<Button
warning
rounded
style={styles.buttonsStyles}
large
onPress={() => this.props.findDogs()}
>
<Icon style={styles.buttonIconStyles} name='refresh' />
</Button>
<Button
rounded
style={styles.buttonsStyles}
large
danger
color='red'
onPress={() => {
this.props.addDog(this._deckSwiper._root.state.selectedItem);
this._deckSwiper._root.swipeLeft();
console.log(this._deckSwiper._root);
}
}
>
<Icon style={styles.buttonIconStyles} color='red' name="heart" active />
</Button>
</View>
</Container>
);
}
}
const styles = {
buttonsStyles: {
borderWidth: 1,
borderColor: 'rgba(0,0,0,0.2)',
alignItems: 'center',
justifyContent: 'center',
width: 75,
height: 75,
borderRadius: 100,
marginTop: 100,
},
buttonViewStyles: {
flexDirection: "row",
flex: 1,
position: "absolute",
bottom: 15,
left: 15,
right: 15,
justifyContent: "space-between",
padding: 15
},
buttonIconStyles: {
fontSize: 45,
}
};
const mapStateToProps = state => {
const { selectedBreeds, gender, size } = state.settings;
const { dogs, findingDogs } = state.findDogsReducer;
const { blacklist } = state.dogs;
return {
dogs,
findingDogs,
blacklist,
selectedBreeds,
gender,
size
};
};
export default connect(mapStateToProps, { findDogs, addDog, blacklistDog })(SwipeDogSelect);
I ended up switching to another swiper because native base's swiper was causing the issue.
I am facing 2 issues related to length of my selected photos:
When selecting photos, it lets me to select 5 photos without any issue (it fits my length restriction), however it doesn't save the chosen photos, when I go to the next screen. In another scenario, when I am at the same screen where I choose photos and I choose 6 photos, it selects the 6 photo but the warning popup will appear and say that its currently supports 5, then when I go to next screen its saves the selected photos unlike previously.
If I deselect photos and then try to select another photos (still in my length limit) popup jumps with selection limit and doesn't let me choose photos, when I go to the next screen it saves the changes from previous selection and not from current one.
import React from 'react';
import {
View,
ScrollView,
Image,
Dimensions,
TextInput,
Text,
StatusBar,
TouchableHighlight,
Linking,
Keyboard,
CameraRoll,
KeyboardAvoidingView
} from 'react-native';
import {connect} from 'react-redux';
import {ActionCreators} from '../redux/actions';
import {bindActionCreators} from 'redux';
import Colors from '../constants/Colors';
import api from '../api';
import {
getEmailAddress,
showError,
renderMessageBar,
registerMessageBar,
unregisterMessageBar
} from '../utils/ComponentUtils';
import {
regularHeader,
mainHeader
} from '../utils/Headers';
import {NavigationActions} from 'react-navigation';
import {SelectionLimitDialog} from '../utils/Dialogs';
import {ifIphoneX, isIphoneX} from 'react-native-iphone-x-helper';
import {SafeAreaView} from 'react-navigation';
// specific component imports.
import {List, ListItem} from 'react-native-elements'
import {Button} from 'react-native-elements'
import Loader from '../components/Loader';
import LoaderError from '../components/LoaderError';
import SelectedPhoto from '../components/SelectedPhoto';
class MultiSafeeScreen extends React.Component
{
static navigationOptions = ({navigation}) => {
const {params} = navigation.state;
const isIncome = params ? params.isIncome : false;
const notificationAction = params ? params.notificationAction : () => {};
const isShowBack = params ? params.isShowBack : false;
return mainHeader({
isShowBack: isShowBack,
backAction: () => navigation.goBack(),
notificationAction: () => notificationAction(),
income: isIncome
});
};
constructor(props) {
super(props);
this.selectedPhotos = [];
this.state = {
photos: null,
loader: {
loading: 0,
message: "Loading photos..."
},
selectedPhotos: [],
supportLength: 5
};
this.props.navigation.setParams({notificationAction: this.onNotification, isIncome: this.props.isNewNotifications});
}
UNSAFE_componentWillReceiveProps(newProps) {
if (newProps.isNewNotifications !== this.props.isNewNotifications) {
this.props.navigation.setParams({notificationAction: this.onNotification, isIncome: newProps.isNewNotifications});
}
}
componentDidMount() {
registerMessageBar(this.refs.alert);
let options = {
first: 30,
assetType: 'Photos',
}
CameraRoll.getPhotos(options)
.then(r => {
this.setState({
photos: r.edges,
loader: {
loading: 1,
message: "Loading photos..."
},
});
})
.catch((err) => {
//Error Loading Images
});
StatusBar.setHidden(false);
}
componentWillUnmount() {
unregisterMessageBar();
this.props.setSelectedPhotos(0);
}
onNotification = () => {
this.props.setNewNotifications(false);
this.props.navigation.navigate("Notifications");
}
closeKeyboard = () => {
Keyboard.dismiss();
}
onSelectPhoto = (photo, index) => {
let photos = new Set([...this.selectedPhotos]);
let len = photos.size + 1 ;
console.log('photos')
if (len > this.state.supportLength) {
this.limitDialog.open();
this.setState({selectedPhotos: this.selectedPhotos});
this.props.setSelectedPhotos(len);
}
else {
photos.add(photo);
this.selectedPhotos = Array.from(photos);
}
}
onDeselectPhoto = (photo, index) => {
let photos = new Set([...this.state.selectedPhotos]);
let len = photos.size - 1;
photos.delete(photo);
this.setState({selectedPhotos: Array.from(photos)});
this.props.setSelectedPhotos(len);
}
onNext = () => {
this.props.navigation.navigate("MultiSafeeCreate", {
isShowBack: true,
selected: [...this.state.selectedPhotos]
});
}
renderLoader() {
let {width, height} = Dimensions.get('window');
let photoWidth = width/3;
if (this.state.loader.loading === 0) {
return <Loader style={{justifyContent: 'center', alignItems: 'center'}} message={this.state.loader.message}/>
}
else if (this.state.loader.loading === 2) {
return <LoaderError style={{justifyContent: 'center', alignItems: 'center'}} message={this.state.loader.message}/>
}
// if photos are null do nothing, else if empty show onbording
// if has photos show photos.
if (this.state.photos === null) {
return <Loader style={{justifyContent: 'center', alignItems: 'center'}} message={this.state.loader.message}/>
}
else {
return (
<View style={{width: width, maxHeight: 600}}>
<ScrollView >
<View style={{flexDirection: 'row', width: width, flexWrap: 'wrap',marginBottom:40, justifyContent: 'space-between'}}>
{
this.state.photos.map((p, i) => {
return (
<SelectedPhoto
key={i}
index={i}
style={{
width: photoWidth,
height: photoWidth,
}}
borderColor = "white"
limit={this.state.supportLength}
photo={p}
onSelectPhoto={this.onSelectPhoto}
onDeselectPhoto={this.onDeselectPhoto}
/>
);
})
}
</View>
</ScrollView>
<View style={{ position:'absolute', right:-15, top:475 }}>
<Button
onPress={this.onNext}
containerViewStyle={{width: width}}
backgroundColor={Colors.red}
title='NEXT' />
</View>
</View>
);
}
}
render() {
return (
<View style={{flex: 1, backgroundColor: Colors.white}}>
{this.renderLoader()}
<SelectionLimitDialog ref={(el) => this.limitDialog = el} />
{renderMessageBar()}
</View>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
function mapStatesToProps(state) {
return {
isNewNotifications: state.isNewNotifications
};
}
export default connect(mapStatesToProps, mapDispatchToProps)(MultiSafeeScreen);