Why is my camera roll modal empty? - javascript

I am attempting to follow the React Native docs on accessing the Camera Roll. I have tried wrapping the camera roll images in a Modal Component. When I press the 'Open Camera Roll' button, the Modal comes up from the bottom with the 'close modal' button visible but none of the images from the camera roll.
When I didn't have it wrapped in a Modal, the images would appear but they would ruin the layout of all the components on my screen, which is why I am (unsuccessfully) trying to place it in a Modal.
Also, if you could give me some tips on how to make the images selectable and appear with 3 images/row it would be greatly appreciated.
import React from 'react';
import { CameraRoll, Image, Modal, ScrollView, Text, View } from 'react-native';
import { Button } from 'react-native-elements';
export default class AddEvent extends React.Component {
constructor(props){
super(props)
this.state = {
modalVisible: false,
photos: [],
}
}
getPhotos = () => {
CameraRoll.getPhotos({
first: 100,
})
.then(r => this.setState({ photos: r.edges }))
.catch((err) => {
alert('Error loading camera roll');
return;
});
}
openModal() {
this.getPhotos();
this.setState({modalVisible:true});
}
closeModal() {this.setState({modalVisible:false});}
static navigationOptions = ({ navigation }) => {
return {
headerTitle: (<Text>Camera Roll Test</Text>),
}
};
render(){
return (
<View>
<Modal
visible={this.state.modalVisible}
animationType={'slide'}
onRequestClose={() => this.closeModal()}>
<ScrollView>
{this.state.photos.map((p, i) => {
return (
<Image
key={i}
style={{width: 300, height: 100,}}
source={{ uri: p.node.image.uri }}/>
);
})}
</ScrollView>
<Button
onPress={() => this.closeModal()}
title="Close modal"/>
</Modal>
<Button
onPress={() => this.openModal()}/>
</View>
);
}
}

Got it, needed flex: 1 for <ScrollView> style and a contentContainerStyle. Doesn't look great but the photos show. Credit to u/Mingli91 on reddit.
import React from 'react';
import { CameraRoll, Image, Modal, ScrollView, StyleSheet, Text, View } from 'react-native';
import { Button } from 'react-native-elements';
const Dimensions = require('Dimensions');
const window = Dimensions.get('window');
const screenWidth = window.width;
const screenHeight = window.height;
export default class AddEvent extends React.Component {
constructor(props){
super(props)
this.state = {
modalVisible: false,
photos: [],
}
}
getPhotos = () => {
CameraRoll.getPhotos({
first: 100,
})
.then(r => this.setState({ photos: r.edges }))
.catch((err) => {
alert('Error loading camera roll');
return;
});
}
openModal() {
this.getPhotos();
this.setState({modalVisible:true});
}
closeModal() {this.setState({modalVisible:false});}
static navigationOptions = ({ navigation }) => {
return {
headerTitle: (<Text>Camera Roll Test</Text>),
}
};
render(){
return (
<View>
<Modal style={styles.modal}
visible={this.state.modalVisible}
animationType={'slide'}
onRequestClose={() => this.closeModal()}>
<ScrollView style={{flex: 1}}
contentContainerStyle={{ height: 100, width: 300 }}>
{this.state.photos.map((p, i) => {
return (
<Image
key={i}
style={{width: 300, height: 100,}}
source={{ uri: p.node.image.uri }}/>
);
})}
</ScrollView>
<Button
onPress={() => this.closeModal()}
title="Close modal"/>
</Modal>
<Button
onPress={() => this.openModal()}/>
</View>
);
}
}
const styles = StyleSheet.create({
modal: {
flex: 1,
alignItems: 'center',
width: screenWidth,
height: screenHeight,
}
});

Android 10
If you started noticing this on Android 10, try adding this to your AndroidManifest.xml:
<application
...
android:requestLegacyExternalStorage="true" />
Android is moving to "scoped storage". They started using this in Android 10, with the option to opt-out by adding android:requestLegacyExternalStorage="true" to your AndroidManifest.xml file.
However, starting in Android 11, this is a forced change and your value of android:requestLegacyExternalStorage will be ignored.
Read more here:
https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage
Posting here because that was the issue with us and it surprisingly difficult to find out about.

Related

ScrollView in React Native

I created a simple app, that shows a few pictures with titles. It is something like a film gallery, where you can observe all enable films. But when I try to add ScrollView element it doesn't work when I try to scroll on my emulated Android device. How can I fix it? My code looks like this:
import React, { Component } from 'react'
import { View, ScrollView, StyleSheet } from 'react-native'
import { Header, ImageCard } from './src/components/uikit'
const url = 'https://s3.eu-central-1.wasabisys.com/ghashtag/RNForKids/00-Init/data.json'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
title: 'STAR GATE',
data: []
};
}
componentDidMount = async () => {
try {
const response = await fetch(url)
const data = await response.json()
this.setState({ data })
}
catch (e) {
console.log(e)
throw e
}
}
render() {
const { title, data } = this.state
const { container } = style
return (
<View>
<Header title={title} />
<ScrollView>
<View style={container}>
{
data.map(item => {
return <ImageCard data={item} key={item.id} />
})
}
</View>
</ScrollView>
</View>
)
}
}
const style = StyleSheet.create({
container: {
marginTop: 30,
flexDirection: 'row',
flexWrap: 'wrap',
flexShrink: 2,
justifyContent: 'space-around',
marginBottom: 150
}
})
I made it with a guide, so it should work. (Youtubes author hasn't this problem on his video).
The View inside ScrollView looks problematic. Try something like:
<ScrollView contentContainerStyle={container} >
{
data.map(item => {
return <ImageCard data={item} key={item.id} />
})
}
</ScrollView>

React Native Animated flatlist item expander does not trigger onLayout of view

I'm trying to implement a flatlist where the items can be expanded to display additional data.
My problem is the onLayout() sometimes (~half the time) won't give me the full height of the content (won't return bigger height than 0 => cannot display the additional data)
This is my custom component:
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import Animated, { Easing } from 'react-native-reanimated';
const { Value, timing } = Animated;
export default class ResultElement extends React.Component {
constructor(props) {
this.state = {
expanded: false,
contentHeight: 0,
};
this._initContentHeight = this._initContentHeight.bind(this);
}
height = new Value(0);
toggle() {
timing(this.height, {
toValue: this.state.expanded ? 0 : this.state.contentHeight,
duration: 300,
easing: Easing.inOut(Easing.ease),
}).start();
this.setState({ expanded: !this.state.expanded });
}
_initContentHeight(evt) {
if (this.state.contentHeight > 0) return;
const height = evt.nativeEvent.layout.height;
this.setState({ contentHeight: height });
this.height.setValue(this.state.expanded ? this.state.contentHeight : 0);
}
render() {
const item = this.props.item;
return (
<View key={item.id}>
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => { this.toggle(); }}>
<Text>Toggle content below</Text>
</TouchableOpacity>
<Animated.View style={[{ overflow: 'hidden' }, { height: this.height }]} onLayout={(evt) => this._initContentHeight(evt)}>
<Text>Random Height content here</Text>
</Animated.View>
</View>
</View>
);
}
}
and this is the flatlist:
<FlatList
data={plan.timeline}
renderItem={({ item }) => <ResultElement item={item} />}
ref={(ref) => { this.flatListRef = ref; }}
/>
Can someone explain why the problem does not occurs all the time?
Since I spent too much time debugging this issue without success, I decided to dump reanimated from this and use react native's built in solution:
In the screen:
constructor(props) {
super(props);
this.state = {};
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
}
I dumped flatlist and used scrollview instead with
array.map((item) => (<CustomComp props={yourprops} />)).
In the component, use:
toggleExpand = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ expanded: !this.state.expanded });
}
...
{this.state.expanded &&
<View>
{ item.type === 'driving' ? this._renderDrivingBody() :
item.type === 'point' ? this.renderPointBody() :
this.renderMinimumBody()}
</View> }
That's all, hope it will help someone in the future.

Position component in React Native with inline styling

I'm trying to move the pizza icon to be directly right of the search bar.
Not only have I not been able to move the search pizza icon to the right of the search bar, I have not been able to move it at all.
This main pizza icon is in MainPage as :
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import axios from 'axios';
import Card from './Card'
import NewCard from './NewCard'
export default class MainPage extends Component {
constructor(props) {
super(props)
this.createLibrary = this.createLibrary.bind(this)
this.state = {
librarys: []
}
}
componentDidMount() {
axios.get('http://localhost:3000/libraries')
.then(res => {
const librarys = res.data;
this.setState({ librarys: librarys });
console.log(this.state.librarys)
})
}
//Create card
createLibrary(library) {
axios.post('http://localhost:3000/libraries', { library })
.then(res => {
this.updateLibrary(library)
})
}
updateLibrary(library){
let newLibrarys = this.state.librarys.filter((f) => f.id !== library.id)
newLibrarys.unshift(library)
this.setState({
librarys: newLibrarys
})
}
render() {
return (
<View>
<NewCard
createLibrary={this.createLibrary}
style={{position: 'absolute',
left: 20,
top: 20}}
/>
<Card style={{justifyContent: 'flex-end'}} librarys={this.state.librarys}/>
</View>
);
}
}
This is the new card component:
import React, { Component } from 'react';
import { Text, View, TextInput, Dimensions } from 'react-native';
import { Header, Form, Item, Input, Icon, Button } from "native-base";
export default class MainPage extends Component {
constructor(props) {
super(props)
this.state = {
title: '',
desc: '',
markdown: '',
showHide: 'none'
}
}
submitForm = () => {
let title = this.state.title
let desc = this.state.desc
let markdown = this.state.markdown
let library = {title: title, desc: desc, markdown: markdown}
this.props.createLibrary(library)
}
showForm = () => {
this.state.showHide === 'none' ?
this.setState({showHide: 'flex'}) :
this.setState({showHide: 'none'})
}
render() {
const formStyle = {
display: this.state.showHide,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height
}
return (
<View>
<Icon
name='pizza'
onPress={this.showForm}
style={{display: this.state.showHide === 'none' ? 'flex' : 'none'}}
/>
<Form style={formStyle} >
<Item>
<Input
placeholder="Title"
name="title"
onChangeText={(value) => this.setState({title: value})}
/>
</Item>
<Item>
<Input
placeholder="Description"
name="desc"
onChangeText={(value) => this.setState({desc: value})}
/>
</Item>
<Item>
<Input
placeholder="Markdown"
name="markdown"
onChangeText={(value) => this.setState({markdown: value})}
/>
</Item>
<Button
light
onPress={this.submitForm.bind(this)}
>
<Text> Submit </Text>
</Button>
</Form>
</View>
);
}
}
The search bar is in the card component:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { Header, Form, Item, Input, Icon, Button, Accordion } from "native-base";
export default class MainPage extends Component {
constructor(props) {
super(props)
this.state = {
activeIndex: null,
search: '',
sortCards: "newest",
search: ''
}
}
render() {
var filteredCards = this.props.librarys.filter(
(library) => {
return library.title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1 || library.desc.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1
})
const dataArray = filteredCards.map(
(library) => {
return {title: library.title, content: library.desc}
}
)
return (
<View>
<Header searchBar rounded>
<Item>
<Icon name="ios-search" />
<Input placeholder="Search"
onChangeText={(value) => this.setState({search: value})}
/>
<Icon name="ios-people" />
</Item>
<Button transparent>
<Text>Search</Text>
</Button>
</Header>
<Accordion dataArray={dataArray} expanded={0}/>
</View>
);
}
}
I've tried different permutations of inline styling, but none of them seem to work. I'm very familiar with React, but I'm new to React Native.
Edit:
This is my current styling:
return (
<View>
<NewCard
createLibrary={this.createLibrary}
style={{
position: 'relative',
left: 20,
top: 20,
zIndex: 1
}}
/>
<Card style={{justifyContent: 'flex-end', position: 'absolute', zIndex: 0}} librarys={this.state.librarys}/>
</View>
);
}
}
Using a nested view with a "row" direction is useful here.
<view style={{flex:1}}>
<view style={{flexDirection:"row"}} >
<Searchabr/>
<Icon/>
...
</view>
</view>
If you want to position over the other item, you have to make the position: relative for the parent and set position: absolute for your icon. once you do this, you will now be able to place it using top, left, right, bottom and z-index properties accordingly.

How to pass values from master screen to detail screen in React Native with wordpress a blog API backend?

Im in the process of developing react native application blog. I'm trying to fetch and display wordpress blog posts.
Im struck with the master detail linking between screen. I need a help in passing value from master to detail page.
All I need is when clicked on the list or card, it has to take to the detail page with particular Id.
I have two files MainPage.js and DetailsPage.js
MainPage.js
import React, { Component } from 'react';
import { Container, Header, Body, Title, Content, Card, CardItem, Text,
Button, Left, Icon } from 'native-base';
import {
StyleSheet,
Dimensions,
TextInput,
View,
ActivityIndicator,
Image,
} from 'react-native';
import Moment from 'moment';
import HTML from 'react-native-render-html';
import {createStackNavigator} from 'react-navigation';
export default class MainPage extends Component {
static navigationOptions = {
title: 'All Posts',
};
constructor(props) {
super(props);
this.state = {
isLoading: true,
posts: [],
};
}
_onDetailButtonPressed = () => {
this.props.navigation.navigate(
'Details');
};
componentDidMount() {
fetch('http://whatsnewsnet.com/wp-json/wp/v2/posts?_embed')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
posts: responseJson,
})
})
.catch((error) => {
console.error(error);
});
}
render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading == true) {
return(
<Container>
<View style={{ flex: 1, flexDirection: 'column', justifyContent: 'center', alignItems: 'center',}}>
<ActivityIndicator size="large" color="#1C97F7" />
</View>
</Container>
)
}
else{
Moment.locale('en');
return (
<Container>
<Content>
{this.state.posts.map((item, index) => (
<Card style={{flex: 0}} key = {item.id}>
<CardItem>
<Left>
<Body>
<Text onPress={this._onDetailButtonPressed} style = {{ fontSize: 24, fontWeight:'bold' }}>{item.title.rendered}</Text>
<Text note>Published on: {Moment(item.date).format('d MMM Y')}</Text>
</Body>
</Left>
</CardItem>
<CardItem>
<HTML html={item.excerpt.rendered} imagesMaxWidth={Dimensions.get('window').width} />
</CardItem>
<CardItem>
<Left>
<Button onPress={() => navigate('Details', { I do not know how to pass particular id from here to detail page})} transparent textStyle={{color: '#87838B'}}>
<Text>Author:</Text>
{item._embedded.author.filter( element => element.id ==item.author).map((subitem, index) => (
<Text key = {item.id}>{subitem.name}</Text>
))}
</Button>
</Left>
</CardItem>
</Card>
))}
</Content>
</Container>
)
}
}
}

onSubmitEditing never fires?

Really simple question, why isn't onSubmitEditing firing when I hit 'Search' on the virtual keyboard?
Currently there are no errors thrown and the console.log in onSearch() never fires.
I'm using the EXPO SDK v.29.
import React from 'react';
import { StyleSheet, Text, View, TextInput, ScrollView, Image } from 'react-native';
import { WebBrowser } from 'expo';
import Icon from 'react-native-vector-icons/Ionicons';
import Styles from 'app/constants/Styles';
import Vars from 'app/constants/Vars';
import Menu from 'app/components/Menu';
import MiniMap from 'app/components/MiniMap';
import NewsList from 'app/components/NewsList';
import {get, post} from 'app/helpers/api';
export default class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: (<Image style={{width: 132, height: 30}} source={require('./../../assets/images/header_image.png')}/>)
};
};
constructor(props) {
super(props);
this.state = {
menu: [],
loadingMenu: true,
searchString: '',
};
}
onMenuPress = (item) => {
let next;
let route = item.page_type.slice(4);
if(route == "PageExternal") {
WebBrowser.openBrowserAsync(item.page.url);
} else {
data = item.page;
if(item.children.length > 0) {
route = 'Menu';
data = item.children;
}
this.props.navigation.navigate(route, {
data: data,
title: item.title
});
}
}
onSearch = (e) => {
console.log('onSearch', e);
//WebBrowser.openBrowserAsync('https://www.1177.se/Halland/Sok/?q=Diabetes&submitted=true');
}
async componentDidMount() {
console.log('Eat my shorrs');
menuitems = await get('content/menu');
this.setState({
menu: menuitems,
loadingMenu: false,
})
//this._getMenu();
}
render() {
return (
<ScrollView style={Styles.whiteBackground}>
<View style={[Styles.blueBackground, Styles.topPadding, Styles.horizontalPadding]}>
<View style={[Styles.searchBox, Styles.bottomMargin]}>
<View style={Styles.searchField}>
<TextInput
style = {Styles.searchInput}
placeholder = "Sök sjukdom/behandling"
onSubmitEditing = {(e) => (this.onSearch(e))}
underlineColorAndroid = "transparent"
returnKeyLabel = "Sök på 1177"
returnKeyType = "search"
/>
<Icon style = {Styles.searchIcon} name = "ios-search" size={18}/>
</View>
<Text style={[Styles.searchLabel]}>Söksvaren kommer från 1177.se</Text>
</View>
<Menu
data={this.state.menu}
loading={this.state.loadingMenu}
style={Styles.topPadding}
onItemPress={this.onMenuPress}
/>
</View>
<Text style={[Styles.h1, Styles.blackText, Styles.horizontalPadding]}>Hitta till oss</Text>
<MiniMap navigation={this.props.navigation}></MiniMap>
<Text style={[Styles.h1, Styles.blackText, Styles.horizontalPadding]}>Nyheter</Text>
<NewsList navigation={this.props.navigation}></NewsList>
</ScrollView>
);
}
}
<TextInput
onSubmitEditing = {(event) => (this.onSearch(event.nativeEvent.text))}
multiline={false}
/>
It does not work when multiline={true} is specified, perhaps your styles has that. See Documentation
You will find your text with event.nativeEvent.text
Try changing
onSubmitEditing = {(e) => (this.onSearch(e))}
to
onSubmitEditing = {this.onSearch}
Then keep
onSubmitEditing = {(e) => this.onSearch(e)}
like this and try by changing the function like below
function onSearch(e) {
console.log('onSearch', e);
//WebBrowser.openBrowserAsync('https://www.1177.se/Halland/Sok/?q=Diabetes&submitted=true');
}
Hope this will work
Check this out
https://snack.expo.io/#raajnadar/submit-text-input
Render method
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Sök sjukdom/behandling"
onSubmitEditing={this.onSearch}
underlineColorAndroid="transparent"
returnKeyLabel="Sök på 1177"
returnKeyType="search"
style={{ width: '100%', textAlign: 'center' }}
/>
</View>
);
}
On submit function
onSearch() {
console.log('onSearch')
}

Categories

Resources