Capture view-shot and save to device - React Native - 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

Related

How to use local mp4 file in expo-av react native

I have it all set up to load a uri its basically the exact same as the example in the expo docs. I cant figure out how to use my local file system instead. I have the mp4 in my file system but when I link the path to the file system the screen goes blank and nothing shows. I'll include the snippet of code that I'm using but can't seem to get it to work
import * as React from "react";
import { View, StyleSheet, Button } from "react-native";
import { Video, AVPlaybackStatus } from "expo-av";
export default function App() {
const video = React.useRef(null);
const [status, setStatus] = React.useState({});
return (
<View style={styles.container}>
<Video
ref={video}
style={styles.video}
source={{
videoURL: require("../assets/videos/TestVideo.mp4"),
}}
useNativeControls
resizeMode="contain"
isLooping
onPlaybackStatusUpdate={(status) => setStatus(() => status)}
/>
<View style={styles.buttons}>
<Button
title={status.isPlaying ? "Pause" : "Play"}
onPress={() =>
status.isPlaying
? video.current.pauseAsync()
: video.current.playAsync()
}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
video: {
flex: 1,
},
});
There is no mention of videoURL bit in the docs so just drop it and you'll see the video:
source={require("../assets/videos/TestVideo.mp4")}

React Native last child selector not working

I'm trying to use the last child selector remove the bottom border from the last Text tag here.
I've used both the EStyleSheet and the StyleSheet but it doesn't seem to be working – the last Text tag still has a bottom border.
I've wrapped the Text tags in View and applied the 'opt' style to the View instead and that also doesn't work.
What am I doing something wrong?
import React from 'react';
import EStyleSheet from 'react-native-extended-stylesheet';
import {
Text,
View,
Image,
} from 'react-native';
EStyleSheet.build()
const styles = EStyleSheet.create({
container:{
flex:1,
},
options:{
},
opt:{
padding:5,
fontSize:25,
borderWidth:1,
borderColor:'black',
},
'opt:last-child': {
borderBottomWidth:0,
}
});
const Settings = () => {
return <View style={styles.container}>
<View style={styles.options}>
<Text style={styles.opt}>Edit profile</Text>
<Text style={styles.opt}>Preferences</Text>
<Text style={styles.opt}>Account settings</Text>
</View>
</View>
};
export default Settings;
To accomplish an item-order aware styling, try something that makes use of EstyleSheet.child like this way:
const items = ['Edit profile', 'Preferences', 'Account settings'];
const Settings = () => (
<View style={styles.container}>
<View style={styles.options}>
{items.map((text, i) => {
const style = EStyleSheet.child(styles, 'opt', i, items.length);
return <Text style={style}>{text}</Text>;
})}
</View>
</View>
);

Creating Drop down list in react native

I am creating a UI for Coronavirus tracking. So I need a drop-down list to fetch data of different countries on clicking specific country.I am sharing the code segment.Any help would be appreciable.
import React from "react";
import Axios from "axios";
import { StyleSheet, View, ImageBackground, Text, Image, Picker } from "react-native";
import { Dropdown } from "react-native-material-dropdown";
export default class App extends React.Component {
constructor(props) {
super(props);
this.getCountryData = this.getCountryData.bind(this);
}
state = {
confirmed: 0,
recovered: 0,
deaths: 0,
countries: []
}
componentDidMount() {
this.getData();
}
async getData() {
const resApi = await Axios.get("https://covid19.mathdro.id/api");
const resCountries = await Axios.get("https://covid19.mathdro.id/api/countries");
const countries = [];
for (var i=0; i < resCountries.data.countries.length; i++) {
countries.push(resCountries.data.countries[i].name);
}
this.setState({
confirmed: resApi.data.confirmed.value,
recovered: resApi.data.recovered.value,
deaths: resApi.data.deaths.value,
countries
});
}
async getCountryData(event){
try {
const res = await Axios.get(`https://covid19.mathdro.id/api/countries/${event.target.value}`);
this.setState({
confirmed: res.data.confirmed.value,
recovered: res.data.recovered.value,
deaths: res.data.deaths.value
})}
catch (err) {
if(err.response.status === 404)
this.setState({
confirmed: "No data available",
recovered: "No data available",
deaths: "No data available"
})
}
}
renderCountryOptions() {
return this.state.countries.map((name, i) => {
return <Text key={name}>{name}</Text>
});
}
render() {
return (
<View style={styles.container}>
<View style={{justifyContent: 'center',alignItems: 'center'}}>
<View style={{height: 150, top:29,width:900, backgroundColor: 'steelblue'}} />
<Text style={styles.text}>COVID-19 Cases Overview</Text>
</View>
<Image
source={require("./assets/Covid-19.jpg")}
resizeMode="contain"
style={styles.image}
>
</Image>
<Text style={styles.text1}>Global Data</Text>
<View style={styles.dropDown}>
<Dropdown onChange={this.getCountryData}>
{this.renderCountryOptions()}
</Dropdown>
</View>
<View>
<View style={styles.boxConfirmed}>
<Text>Confirmed </Text>
<Text>{this.state.confirmed}</Text>
</View>
<View style={styles.boxRecovered}>
<Text>Recovered</Text>
<Text>{this.state.recovered}</Text>
</View>
<View style={styles.boxDeaths}>
<Text> Deaths</Text>
<Text>{this.state.deaths}</Text>
</View>
</View>
</View>
);
}
}
I am unable to extract data of individual country. Please suggest me how I can use Dropdown in React Native
You can use react native picker from react native community for the task...
<Picker
selectedValue={this.state.language}
style={{height: 50, width: 100}}
onValueChange={(itemValue, itemIndex) =>
this.setState({language: itemValue})
}>
this.state.list.map((val) => {
<Picker.Item label=val value=val />
})

How to open downloaded photo using gallery app in React Native Expo?

I am facing a problem, cannot figure out how to implement such action. I want to Download Photo from external source, and then I want to open it in gallery.
Checked this source: How to open iOS gallery app from React Native app
Here one developer suggest to use this code:
openPhotos = () =>{
switch(Platform.OS){
case "ios":
Linking.openURL("photos-redirect://");
break;
case "android":
Linking.openURL("content://media/internal/images/media");
break;
default:
console.log("Could not open gallery app");
}
}
This code does open gallery, but when I select default gallery app, it shows black screen, if I choose google photos app it opens the gallery without black screen.
My question would be how could I refactor my code, to be able to Download Photo, and open downloaded photo in gallery?
Component code:
import React from "react";
import {View,Text, StyleSheet,Platform,Image,Alert} from "react-native";
import PhotoComments from "./PhotoComments";
import moment from "moment";
import * as MediaLibrary from "expo-media-library";
import * as FileSystem from "expo-file-system";
import * as Permissions from "expo-permissions";
import { Button } from "react-native-elements";
import { Linking } from "expo";
function downloadFile(uri) {
let filename = uri.split("/");
filename = filename[filename.length - 1];
let fileUri = FileSystem.documentDirectory + filename;
FileSystem.downloadAsync(uri, fileUri)
.then(({ uri }) => {
saveFile(uri);
})
.catch(error => {
Alert.alert("Error", "Couldn't download photo");
console.error(error);
});
}
async function openPhotos(uri) {
switch (Platform.OS) {
case "ios":
Linking.openURL("photos-redirect://");
break;
case "android":
//Linking.openURL("content://media/internal/images/media/");
Linking.openURL("content://media/internal/images/media");
break;
default:
console.log("Could not open gallery app");
}
}
async function saveFile(fileUri) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === "granted") {
const asset = await MediaLibrary.createAssetAsync(fileUri);
const data = await MediaLibrary.createAlbumAsync("Download", asset, false);
console.log("deubeuger");
console.log(data);
console.log("buger");
Alert.alert("Success!", JSON.stringify(fileUri));
openPhotos(fileUri);
}
}
const PhotoRecord = ({ data }) => {
return (
<View style={styles.container}>
<View style={styles.infoContainer}>
<Text style={styles.usernameLabel}>#{data.username}</Text>
<Text style={styles.addedAtLabel}>
{moment(new Date(data.addedAt)).format("YYYY/MM/DD HH:mm")}
</Text>
</View>
<View style={styles.imageContainer}>
<Image source={{ uri: data.links.thumb }} style={styles.image} />
</View>
<PhotoComments comments={data.photoComments} />
<View style={styles.btnContainer}>
<Button
buttonStyle={{
backgroundColor: "white",
borderWidth: 1
}}
titleStyle={{ color: "dodgerblue" }}
containerStyle={{ backgroundColor: "yellow" }}
title="Add Comment"
/>
<Button
onPress={() => downloadFile(data.links.image)}
style={styles.btn}
title="Download"
/>
</View>
</View>
);
};
I managed to implement downloading from external source, but cannot find the working solutions on how to open downloaded photo through gallery app.
Maybe I am looking for solution which is not efficient, maybe there is a better way?
Couldn't find desirable solution for this problem. Decided to develop an app a little bit differently, if someone with similar problem will search for this thread. I made Download Button which will Download photo to the device
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
Image,
Alert,
TouchableOpacity
} from "react-native";
import PhotoComments from "./PhotoComments";
import moment from "moment";
import * as MediaLibrary from "expo-media-library";
import * as FileSystem from "expo-file-system";
import * as Permissions from "expo-permissions";
import { Button } from "react-native-elements";
import ZoomableImage from "./ZoomableImage";
function downloadFile(uri) {
let filename = uri.split("/");
filename = filename[filename.length - 1];
let fileUri = FileSystem.documentDirectory + filename;
FileSystem.downloadAsync(uri, fileUri)
.then(({ uri }) => {
saveFile(uri);
})
.catch(error => {
Alert.alert("Error", "Couldn't download photo");
console.error(error);
});
}
async function saveFile(fileUri) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === "granted") {
const asset = await MediaLibrary.createAssetAsync(fileUri);
await MediaLibrary.createAlbumAsync("Download", asset, false);
Alert.alert("Success", "Image was successfully downloaded!");
}
}
const PhotoRecord = ({ data }) => {
const [show, setShow] = useState(false);
return (
<View style={styles.container}>
<ZoomableImage
show={show}
setShow={setShow}
imageSource={data.links.image}
/>
<View style={styles.infoContainer}>
<Text style={styles.usernameLabel}>#{data.username}</Text>
<Text style={styles.addedAtLabel}>
{moment(new Date(data.addedAt)).format("YYYY/MM/DD HH:mm")}
</Text>
</View>
<TouchableOpacity
activeOpacity={1}
style={styles.imageContainer}
onLongPress={() => setShow(true)}
>
<Image source={{ uri: data.links.thumb }} style={styles.image} />
</TouchableOpacity>
<PhotoComments comments={data.photoComments} />
<View style={styles.btnContainer}>
<Button
buttonStyle={{
backgroundColor: "white",
borderWidth: 1
}}
titleStyle={{ color: "dodgerblue" }}
containerStyle={{ backgroundColor: "yellow" }}
title="Add Comment"
/>
<Button
onPress={() => downloadFile(data.links.image)}
style={styles.btn}
title="Download"
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
display: "flex",
flexDirection: "column"
},
infoContainer: {
borderBottomWidth: 1,
borderColor: "gainsboro",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
padding: 15
},
usernameLabel: {
fontSize: 18,
fontWeight: "bold"
},
addedAtLabel: {
paddingTop: 10,
color: "#404040"
},
imageContainer: {
width: "100%",
height: 380
},
image: {
width: "100%",
height: "100%",
resizeMode: "cover"
},
btnContainer: {
flex: 1,
flexDirection: "row",
marginBottom: 100,
justifyContent: "space-between"
}
});
export default PhotoRecord;
On my device it looks like this
If Download button clicked it will download the photo to the device
If user want to inspect the image, he can do long press on the photo and then the photo will be open in a web view modal
This is far from perfect, but I could figure out by myself.
The code for modal is here:
import React from "react";
import { Modal, Dimensions, StyleSheet, View } from "react-native";
import { WebView } from "react-native-webview";
const ZoomableImage = ({ show, setShow, imageSource }) => {
return (
<Modal
animationType={"fade"}
transparent={false}
visible={show}
onRequestClose={() => {
setShow(!show);
}}
>
<View style={styles.container}>
<WebView source={{ uri: imageSource }} style={styles.image} />
</View>
</Modal>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "space-between"
},
image: {
height: Math.round(Dimensions.get("window").height),
width: Math.round(Dimensions.get("window").width),
flex: 1
}
});
export default ZoomableImage;
Couldn't achieve what I wanted but came up with a slightly different solution, hopes this will help someone.

How can I take a screenshot (View) with a TouchableHighlight?

The Error has been resolved as per your guidelines.
But still can not take and save the screeshot of the desired ViewShot, where it has a react image.
I need to take and save the screeshot in the memory of my device.
Can you tell me if I'm on the right track or would I have another way to do it? Thanks!
Follow my current code.
import React, { Component } from 'react';
import { Text, View, Image, StyleSheet, TouchableHighlight } from 'react-native';
import { captureScreen } from "react-native-view-shot";
import ViewShot 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");
}
const btnZooMenos = ()=>{
alert("Zoo Menos");
}
const capitureScreen = ()=>{
captureScreen({
format: "jpg",
quality: 0.9,
}).then(
uri => console.log("Image saved to", uri),
error => console.error("Oops, snapshot failed", error)
);
}
export default class Monitor extends Component {
componentDidMount () {
this.refs.viewShot.capture().then(uri => {
console.log("do something with ", uri);
});
}
render() {
return (
<View>
<ViewShot ref="viewShot" options={{ format: "jpg", quality: 0.9 }} >
<Image source={{uri: 'https://facebook.github.io/react/logo-og.png'}} style={ style.video } />
</ViewShot>
<View style={style.menu}>
<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>
);
}
}
const style = StyleSheet.create({
corpo: {
},
menu:{
flexDirection:'row',
justifyContent: 'space-between'
},
imgMenu:{
//margin: 15
},
video: {
width: 400,
height: 450
}
});
Looks like you may not have either linked the package or rebuilt the app after linking.
Try
$ react-native link react-native-view-shot
$ react-native run-{ios|android}