Selecting pictures in react native - react-native

I am using the react native camera roll function to retrieve videos from my phone's camera roll. I am able to get the videos to show up in a gallery in my app, but I can't seem to figure out how to make a button to open up this video and actually play it. Here is my code so far:
import React, {Component} from 'react';
import {
View,
Image,
FlatList,
PermissionsAndroid,
Platform,
} from 'react-native';
import CameraRoll from '#react-native-community/cameraroll';
class showVideo extends Component {
constructor(props) {
super(props);
this.state = {
data: '',
};
}
async componentDidMount() {
if (Platform.OS === 'android') {
const result = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
{
title: 'Permission Explanation',
message: 'ReactNativeForYou would like to access your photos!',
},
);
if (result !== 'granted') {
console.log('Access to pictures was denied');
return;
}
}
CameraRoll.getPhotos({
first: 50,
assetType: 'All',
})
.then(res => {
this.setState({data: res.edges});
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<View>
<FlatList
data={this.state.data}
numColumns={3}
renderItem={({item}) => (
<Image
style={{
width: '33%',
height: 150,
}}
source={{uri: item.node.image.uri}}
/>
)}
/>
</View>
);
}
}
export default showVideo;
Any pointers or recommendations? All I have is a static overview of videos pulled from my camera roll in my app.

I may suggest you to use <TouchableHighlight /> added outside the <Image/> tag to catch when onPress. Is this what you want? It could know just press on image.
import { TouchableHighlight } from "react-native";
<TouchableHighlight onPress={() => this.functionToPlayVideo(item.node.image.uri)}>
<Image
style={{
width: '33%',
height: 150,
}}
source={{uri: item.node.image.uri}}
/>
</TouchableHighlight>

Related

How Can I Display SVG Data In React Native?

I have some SVG image data that I would like to import into my React Native App.
Is there any way to take my raw SVG image data and display it without saving it to a file?
I have an api that will create an image based from any given text (coolProgrammer123) and will return the SVG image data.
https://api.kwelo.com/v1/media/identicon/coolProgrammer123?format=base64
Any Ideas? Thanks.
I'll suggest you try out react-native-svg-uri
import SvgUri from 'react-native-svg-uri';
const TestSvgUri = () => (
<View style={styles.container}>
<SvgUri
width="200"
height="200"
source={{uri:'http://thenewcode.com/assets/images/thumbnails/homer-simpson.svg'}}
/>
</View>
);
UPDATE
Here is a code snippet on how you can use to use axios to get the SVG from the URL and pass it to the Image tag without using the above npm package.
import React, { Component } from 'react';
import { View, Image } from 'react-native';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.state = {
imageUri: ''
}
}
componentDidMount() {
this.getSvg();
}
getSvg = () => {
let image_base64;
axios.get('https://api.kwelo.com/v1/media/identicon/coolProgrammer123?format=base64')
.then(response => {
image_base64 = response.data;
this.setState({
...this.state,
imageUri: image_base64
})
})
.catch(function (error) {
console.log(error);
});
return image_base64;
}
render(){
return (
<View>
<Image
style={{
width: 81,
height: 81,
resizeMode: 'contain',
marginTop: 180,
marginLeft: 20,
marginRight: 20,
alignSelf: "center"
}}
source={{ uri: this.state.imageUri }}
/>
</View>
);
}
};
export default App;

Pause video in flatlist when out of view

I have a flatlist showing videos. I want that when a video goes out of the view it should be paused. I am maintaining the pause state in each of the Posts component.
class Posts extends React.PureComponent {
constructor() {
super()
this.state = {
pause: true,
}
return(){
<Video
pause={this.state.pause}
//other props
/>
}
}
I am using react-native-video.
I have tried using onViewableItemsChanged prop of Flatlist but it doesn't change the state.
I tried this .
But it doesn't seem to work for me.
How should I proceed ?
Here is a possible solution using react-native-inviewport. This dependency is only a single index file that contains a component that has a callback when view is in the viewport. It could easily be modified to suit your needs.
I have constructed a very simple app that has a FlatList. The 11th item in the FlatList is a video. That should mean that the video is off the screen when the App renders so the viddeo won't be playing, once the video comes fully into the viewport it should then start playing.
App.js
import * as React from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
import VideoPlayer from './VideoPlayer';
export default class App extends React.Component {
state = {
data: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
}
renderItem = ({item, index}) => {
if (index === 10) {
return <VideoPlayer />
} else {
return (
<View style={{height: 100, backgroundColor: '#336699', justifyContent: 'center', alignItems: 'center'}}>
<Text>{index}</Text>
</View>
)
}
}
keyExtractor = (item, index) => `${index}`;
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
}
});
VideoPlayer.js
This is a component that contains the Video component. The video is wrapped in the InViewPort component that has a callback function. The callback returns true when the component it surrounds is completely in the viewport and false when it is not fully in the viewport. The callback calls this.handlePlaying which in turn calls either this.playVideo or this.pauseVideo depending on the boolean value.
import React, {Component} from 'react';
import { View, StyleSheet } from 'react-native';
import { Video } from 'expo-av';
import InViewPort from './InViewPort';
//import InViewPort from 'react-native-inviewport; // this wouldn't work in the snack so I just copied the file and added it manually.
export default class VideoPlayer extends React.Component {
pauseVideo = () => {
if(this.video) {
this.video.pauseAsync();
}
}
playVideo = () => {
if(this.video) {
this.video.playAsync();
}
}
handlePlaying = (isVisible) => {
isVisible ? this.playVideo() : this.pauseVideo();
}
render() {
return (
<View style={styles.container}>
<InViewPort onChange={this.handlePlaying}>
<Video
ref={ref => {this.video = ref}}
source={{ uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4' }}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="cover"
shouldPlay
style={{ width: 300, height: 300 }}
/>
</InViewPort>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center'
}
});
Here is a snack showing it working https://snack.expo.io/#andypandy/video-only-playing-when-in-viewport
I should point out that if the video is not fully in the viewport then it will not play. I am sure some tweaking could be done to react-native-inviewport so that it would play the video if it was partially in the viewport if that is what you wanted, perhaps by passing the height of the video to the InViewPort component.
here is how i simply did the trick
inside my card component that have video
<Video ...
paused={currentIndex !== currentVisibleIndex}
/>
both currentIndex and currentVisibleIndex are passed the component from the FlatList parent
my FlatList pass the renderItem index as currentIndex
<FlatList
data={[...]}
renderItem={({ item, index }) => (
<GalleryCard
{...item}
currentIndex={index}
currentVisibleIndex={currentVisibleIndex}
/>
)}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={{
viewAreaCoveragePercentThreshold: 90
}}
finally my this is how to calculate currentVisibleIndex
please make sure to read viewabilityConfig
onViewableItemsChanged = ({ viewableItems, changed }) => {
if (viewableItems && viewableItems.length > 0) {
this.setState({ currentVisibleIndex: viewableItems[0].index });
}
};
please let me know if this is helpful
I finally did this using redux. Not sure whether it is the right way.
Home.js
_renderItem = ({item, index}) => <Posts item={item} />
viewableItemsChanged = (props) => {
let {changed} = props;
let changedPostArray = [];
changed.map((v,i) => {
let {isViewable, item} = v;
if(!isViewable) {
let {post_id, type} = item;
if(type === 1)
changedPostArray.push(post_id);
}
});
if(changedPostArray.length != 0)
this.props.sendPostToPause(changedPostArray);
}
render() {
return(
<View style={{flex: 1}} >
<FlatList
ref={(ref) => this.homeList = ref}
refreshing={this.state.refreshing}
onRefresh={async () => {
await this.setState({refreshing: true, postsId: [0], radiusId: 0, followingId: 0});
this.getPosts();
}}
onEndReached={async () => {
if(!this.state.isEnd) {
await this.setState({isPaginate: true})
this.getPosts()
}
}}
onEndReachedThreshold={0.2}
removeClippedSubviews={true}
contentContainerStyle={{paddingBottom: 80}}
data={this.state.followersRes}
renderItem={this._renderItem}
viewabilityConfig={this.viewabilityConfig}
onViewableItemsChanged={this.viewableItemsChanged}
/>
</View>
<Footer callback={this.scrollToTopAndRefresh} />
</View>
)
}
export default connect(null, {sendPostToPause})(Home);
Posts.js
class Posts extends React.PureComponent {
constructor(){
super()
this.state: {
pause: false
}
}
componentDidUpdate(prevProps) {
if(prevProps != this.props) {
this.props.postIdArray.map((v) => {
if(v === prevProps.item.post_id) {
this.setState({pause: true})
}
})
}
}
render(){
return(
<Video
pause={this.state.pause}
//Other props
/>
)
}
}
const mapStateToProps = (state) => {
const {postId} = state.reducers;
return {
postIdArray: postId
}
}
export default connect(mapStateToProps, {sendPostToPause})(withNavigation(Posts));
Whenever the viewableItemsChanged is trigger I am adding the changed posts id in an array and calling the action with the array of post ids.
In the Posts component I am checking if the post ids match, if so I am setting the pause state to true.

State does not change until the second button press? Adding a call back throws an error "invariant violation: invalid argument passed"

I'm trying to render a Flatlist that contains a store name. Upon clicking the store name, more information about the store should be displayed.
I attempted to change state and then use
{this.renderMoreDetails(item) && this.state.moreDetailsShown}
to get the additional information to appear. However, it was clear via console.log that state was only changed after a second button press.
From what I read in this article 1 and this article 2 it seemed as though I needed a callback function as an additional parameter to my setState().
I tried adding a callback function(probably incorrect, but I'm extremely lost at this point) and only got more errors.
I know that I have access to all of the data from renderItem inside FlatList. How do I make it appear upon click?
import React, { Component } from 'react';
import {
View,
FlatList,
Text,
TouchableWithoutFeedback,
ListView,
ScrollView
} from 'react-native'
import { NavigationActions } from 'react-navigation';
import { Header, Card, CardSection } from '../common';
import Config from '../Config';
export default class Costco extends Component<Props> {
constructor(props) {
super(props);
this.state = {
stores: [],
selectedItem: {id: null},
};
}
componentWillMount() {
const obj = Config.costcoThree;
const costcoArr = Object.values(obj);
this.setState({
stores: costcoArr,
})
}
renderListOfStores() {
return <FlatList
data={this.state.stores}
renderItem={ ({item}) => this.singleStore(item)}
keyExtractor={(item) => item.id}
extraData={this.state.selectedItem} />
}
singleStore(item) {
console.log(this.state.selectedItem.id)
return item.id === this.state.selectedItem.id ?
<TouchableWithoutFeedback
onPress={() => this.selectStore(item)}
>
<View>
<Text>{item.branchName}</Text>
<Text>Opening Time {item.openingTime}</Text>
<Text>Closing Time {item.closingTime}</Text>
<Text>Address {item.dongAddKor}</Text>
</View>
</TouchableWithoutFeedback>
:
<TouchableWithoutFeedback>
<View>
<Text>{item.branchName}</Text>
<Text>Only showing second part</Text>
</View>
</TouchableWithoutFeedback>
}
selectStore(item) {
this.setState({selectedItem: item});
console.log('repssed');
}
render() {
return(
<ScrollView>
<Header headerText="Costco"/>
<Text>hello</Text>
{this.renderListOfStores()}
</ScrollView>
)
}
}
as a workaround you can have a state that maintain your selected item to expand(or something like another view) and then you can use some conditions to work for render your flat list data.
Consider the below code and let me know if you need some more clarification.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,FlatList,TouchableNativeFeedback,
View
} from 'react-native';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
response: [],
selectedItem: {id: null},
};
}
userListLayout() {
const {response} = this.state;
return <FlatList data={response} renderItem={ ({item}) => this.singleUserLayout(item)} keyExtractor={(item) => item.email} extraData={this.state.selectedItem} />
}
singleUserLayout(item) {
return item.id == this.state.selectedItem.id ?
<TouchableNativeFeedback onPress={() => this.userSelected(item)}>
<View style={{flex:1, borderColor: 'black', borderWidth: 1}}>
<Text style={{padding: 10, fontSize: 25}}>{item.email}</Text>
<Text style={{padding: 10,fontSize: 20}}>{item.name}</Text>
</View>
</TouchableNativeFeedback>
: <TouchableNativeFeedback onPress={() => this.userSelected(item)}><Text style={{padding: 10,fontSize: 20}}>{item.email}</Text></TouchableNativeFeedback>
}
userSelected(item) {
console.log(item)
this.setState({selectedItem: item})
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(data => data.json())
.then(response => this.setState({response}))
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>HI THERE</Text>
{this.userListLayout()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});

Capture view-shot and save to device - React Native

I am developing an application where I have a button (TouchableHighlight) when pressing this button it is necessary to capture a screeshot of the current screen and save the file in the device.
My code does not show error, but when I press the button (TouchableHighlight) I get the message:
Image saved to file: ///data/user/0/com.appcamerav4/cache/ReactNative-snapshot-image8525057299267209213.jpg through Remote JS Debugging .
I can not open this directory and need to save the image to the device.
I'm new to react-native.
Follow my code below:
import React, { Component } from 'react';
import { Text, View, Image, StyleSheet, TouchableHighlight, WebView, StatusBar, Button } from 'react-native';
import { captureScreen } from "react-native-view-shot";
const zooMais = require('../imgs/zooMais.png');
const zooMenos = require('../imgs/zooMenos.png');
const imgScreeshot = require('../imgs/screeshot.png');
const btnZooMais = ()=>{
alert("Zoo Mais");
console.log("Zoom +");
}
const btnZooMenos = ()=>{
alert("Zoo Menos");
console.log("Zoom +");
}
const capitureScreen = ()=>{
captureScreen({
format: "jpg",
quality: 0.8,
}).then(
uri => console.log("Image saved to", uri),
error => console.error("Oops, snapshot failed", error)
);
}
export default class Monitor extends Component {
render() {
return (
<View style={ style.viewPrincipal }>
<StatusBar hidden />
<View style={ style.viewImagem } >
<WebView
style={style.video}
automaticallyAdjustContentInsets={true}
scalesPageToFit={true}
startInLoadingState={false}
contentInset={{top: 0, right: 0, left: 0, bottom: 0}}
scrollEnabled={true}
source={{uri: 'https://facebook.github.io/react/logo-og.png'}}
onNavigationStateChange = {this.handleNavigationStateChange}
/>
</View>
<View style={ style.viewRodape }>
<View style={style.viewMenu}>
<View >
<TouchableHighlight onPress={ btnZooMais } >
<Image style={style.imgMenu} source={zooMais } />
</TouchableHighlight>
</View>
<View>
<TouchableHighlight onPress={ capitureScreen }>
<Image style={style.imgMenu} source={ imgScreeshot } />
</TouchableHighlight >
</View>
<View>
<TouchableHighlight onPress={ btnZooMenos } >
<Image style={style.imgMenu} source={ zooMenos } />
</TouchableHighlight>
</View>
</View>
</View>
</View>
);
}
}
const style = StyleSheet.create({
viewPrincipal:{
flex: 1
},
viewImagem:{
flex:10,
justifyContent:'center',
alignItems:'stretch'
},
viewRodape:{
flex:1.3
},
viewMenu:{
flexDirection:'row',
justifyContent: 'space-between'
},
imgMenu:{
margin: 0,
marginBottom:0
},
video:{
flex:1
}
});
Make sure react-native-view-shot is correctly linked in XCode (might require a manual installation,
refer to React Native doc).
import React, { useRef } from "react"; // import useRef hook on top
const cardRef = useRef(); // Use this hook inside your func. component *important
// Define a function like this
const saveAsImage = async () => {
try {
const result = await captureRef(cardRef, {
result: "tmpfile",
quality: 1,
format: "png",
});
MediaLibrary.saveToLibraryAsync(result);
} catch (e) {
console.log(e);
}
};
Apply a prop eg. parentRef={cardRef} to your component, make sure the ref name matches which in this case is "cardRef".
Give any Button/TouchableOpacity
onPress={() => {saveAsImage();}}
To solve this problem you have to go on your App permission on your real mobile and allow for camera storage then you can easily save your ViewShot on Your Mobile.
go to App Permisssion in your App.info
allow Camera accesss storage
To save the screenshot to the camera roll, use this one: https://facebook.github.io/react-native/docs/cameraroll.html#savetocameraroll
More info: https://github.com/gre/react-native-view-shot Check the FAQ section

Shoutem fetch data not displaying

First I want to start by saying I am a total noob at React Native and Shoutem. I am not sure if I should write Shoutem or React Native in the subject of my questions, but here is my problem.
I have two screens:
Screen1.js
Screen2.js
Screen1 displays a list of items returned from a fetch. Once I click on the item, it will open the second screen which is the details screen.
I am passing data from screen1 to screen2. In screen2 I need to make another fetch call for different data, but it does not work. I am doing exactly the same thing on both screens.
Here is my code for Screen1:
import React, {
Component
} from 'react';
import {
ActivityIndicator,
TouchableOpacity
} from 'react-native';
import {
View,
ListView,
Text,
Image,
Tile,
Title,
Subtitle,
Overlay,
Screen
} from '#shoutem/ui';
import {
NavigationBar
} from '#shoutem/ui/navigation';
import {
navigateTo
} from '#shoutem/core/navigation';
import {
ext
} from '../extension';
import {
connect
} from 'react-redux';
export class List extends Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.state = {
isLoading: true,
content: null,
}
}
componentDidMount() {
return fetch('https://www.cannabisreports.com/api/v1.0/strains').then((response) => response.json()).then((responseData) => {
this.setState({
isLoading: false,
content: responseData
});
}).done();
}
renderRow(rowData) {
const { navigateTo } = this.props;
return (
//<Text>{rowData.name}, {rowData.createdAt.datetime}</Text>
<TouchableOpacity onPress={() => navigateTo({
screen: ext('Strain'),
props: { rowData }
})}>
<Image styleName="large-banner" source={{ uri: rowData.image &&
rowData.image ? rowData.image : undefined }}>
<Tile>
<Title>{rowData.name}</Title>
<Subtitle>none</Subtitle>
</Tile>
</Image>
</TouchableOpacity>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{flex: 1, paddingTop: 20}}>
<ListView
data={this.state.content.data}
renderRow={rowData => this.renderRow(rowData)}
/>
</View>
);
}
}
// connect screen to redux store
export default connect(
undefined,
{ navigateTo }
)(List);
I am passing rowData to Screen2. I then need to make another fetch calling using data from rowData as it is a path parameter needed for the API call in Screen2.
So basically I need to make a fetch call in Screen2 like this:
fetch('https://mywebsite.com/'+rowData.something+'/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
content: responseJson.data
})
})
.catch((error) => {
console.error(error);
});
Here is my code for screen2:
export default class Strain extends Component {
constructor(props) {
super(props);
this.state = {
content: null,
}
}
componentDidMount() {
return fetch('https://mywebsite.com/'+rowData.something+'/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
content: responseJson.data
})
})
.catch((error) => {
console.error(error);
});
}
renderRow(dataContent) {
return (
<Text>{dataContent.name}</Text>
// This does not work either -> <Text>{dataContent}</Text>
);
}
render() {
const { rowData } = this.props; //This is coming from screen1.js
return (
<ScrollView style = {{marginTop:-70}}>
<Image styleName="large-portrait" source={{ uri: rowData.image &&
rowData.image ? rowData.image : undefined }}>
<Tile>
<Title>{rowData.name}</Title>
<Subtitle>{rowData.createdAt.datetime}</Subtitle>
</Tile>
</Image>
<Row>
<Text>Seed Company: {rowData.seedCompany.name}</Text>
</Row>
<Divider styleName="line" />
<Row>
<Icon name="laptop" />
<View styleName="vertical">
<Subtitle>Visit webpage</Subtitle>
<Text>{rowData.url}</Text>
</View>
<Icon name="right-arrow" />
</Row>
<Divider styleName="line" />
<View style={{flex: 1, paddingTop: 20}}>
<ListView
data={content}
renderRow={dataContent => this.renderRow(dataContent)}
/>
</View>
<Divider styleName="line" />
</ScrollView>
);
}
}
My fetch URL returns data like this:
{
data: {
name: "7.5833",
another_name: "8.6000",
different_name: "5.7500",
}
}
This only returns one data object like what you see above.
When I run the code I get this error:
null is not an object (evaluating 'Object.keys(e[t])')
Please let me know if you need me to provide more info.
I have tried so many different things and nothing seems to work so I am in need of some help. What am I doing wrong?
Not sure why this works but it does.
I used a function to fetch my data and then call the function in componentDidMount like this:
getData() {
return fetch('https://mywebsite.com/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
data: responseJson.data
});
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getData();
}
Then to get the values from the JSON response I am doing this:
this.state.data.name
I am having another issue, but I will create another question.