How to share an image to Instagram Stories from Expo / React Native - react-native

How do I share an image directly to an Instagram story using Expo / React Native?
This issue has already been solved for iOS for normal Instagram posts, but not stories.
Facebook's docs explain how to do this for iOS and Android, but not React Native.
Expected behavior:
Open Instagram story with the selected image.
Current behavior:
Opens Instagram story with blank screen.
The problem I'm facing:
I'm not quite sure how to generate a URI that fits Instagram's schema, as referenced in their docs.
Reproducible Snack
https://snack.expo.io/#nandorojo/share-to-instagram-story
Thank you so much!
Here is my code from the snack above:
import * as React from 'react';
import { Text, View, StyleSheet, Linking, Image } from 'react-native';
import * as FileSystem from 'expo-file-system';
const url = 'https://source.unsplash.com/daily';
export default class App extends React.Component {
_shareToStory = async () => {
// download to device
const { uri } = await FileSystem.downloadAsync(
url,
`${FileSystem.documentDirectory}meme.jpg`
).catch(e => console.log('instagram share failed', JSON.stringify(e), url));
try {
const encodedUrl = encodeURIComponent(uri);
const instagramUrl = `instagram-stories://share?backgroundImage=${encodedUrl}`;
Linking.openURL(instagramUrl);
} catch (error) {
console.log(error);
}
};
render() {
return (
<View style={styles.container}>
<Image source={{ uri: url }} style={{ height: 100, width: 100 }} />
<Text style={styles.paragraph} onPress={this._shareToStory}>
Share to Instagram Story
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});

If you're using Expo Managed Workflow, your options are very limited. As of SDK v38, afaik it's only possible to accomplish this on Android, and only if you're sharing a background image (due to missing Expo APIs for content permissions and pasteboard), then something like this might work:
import * as FileSystem from 'expo-file-system'
import * as IntentLaucher from 'expo-intent-launcher'
// url of the file to share
const url = '....'
// some random temporary filename
const localFile = `${FileSystem.cacheDirectory}${uuid()}.jpg`
try {
FileSystem.downloadAsync(url, localFile)
.then(({ uri }) => {
FileSystem.getContentUriAsync(uri).then(contentUri => {
IntentLauncher.startActivityAsync(
'com.instagram.share.ADD_TO_STORY',
{
data: contentUri,
flags: 1, // FLAG_GRANT_READ_URI_PERMISSION
type: 'image/jpeg', // or other based on your file type
}
).catch(console.warn)
})
})
.catch(console.warn)
} finally {
FileSystem.deleteAsync(localFile).catch(console.warn)
}
However, if you're using React Native (or have ejected to Expo Bare Workflow), then you have more options. I'd recommend using the RN community supported react-native-share library that works on both Android and iOS and supports all of the sharing options (plus many other services):
import Share from 'react-native-share'
Share.shareSingle({
method: Share.InstagramStories.SHARE_BACKGROUND_IMAGE,
backgroundImage: '....' // url of the file to share
social: Share.Social.INSTAGRAM_STORIES,
})

Related

Print react native text and QR generated

Hello I need to generate a PDF o HTML view to print a document from an Android device. So I started to use Print from 'expo-print'.
I can make this
Print.printAsync({
html: "<h3>Hello World</h3>"
});
And works just fine. But I want to generate and include a QR code inside that HTML, I wanted to use react-native-qrcode but I don't know how to include it inside that.
clarification: the QR code needs to be created without connection too
Thanks
I just came across the same problem and here's my solution.
I use react-native-qrcode-svg because it has a getRef props for you to further work with the QR data.
Below, you can find my rough implementation (My main code is on another computer). You can further customize it to hide QRCode component or using Redux to store QRData but it should work fine.
import QRCode from 'react-native-qrcode-svg';
...
constructor(props) {
super(props)
this.state = { qrData: "" }
}
componentDidMount () {
this.getDataURL(); // => Calling this in here to make sure the QRCode component did mount
}
... more code ...
print = () => {
Print.printAsync({
html: `
<h3>Hello World</h3>
<img src="data:image/jpeg;base64,${this.state.qrData}"/>
`
});
}
...
getDataURL() {
this.svg.toDataURL(this.callback);
}
callback(dataURL) {
this.setState({qrData: dataURL});
}
render() {
return (
<QRCode
value="Just some string value"
getRef={(c) => (this.svg = c)}
/>
<Button title="Print QR to HTML" onPress={this.print} />
);
}
You can use rn-qr-generator module (https://www.npmjs.com/package/rn-qr-generator). You need to pass value to the lib and it will return you uri and base64 data of the generated QRCode
import RNQRGenerator from 'rn-qr-generator';
componentDidMount() {
RNQRGenerator.generate({
value: 'otpauth://totp/Example:alice#google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example', // required
height: 300,
width: 300,
base64: false, // default 'false'
backgroundColor: 'black', // default 'white'
color: 'white', // default 'black'
})
.then(response => {
const { uri, width, height, base64 } = response;
this.setState({ imageUri: uri });
})
.catch(error => console.log('Cannot create QR code', error));
}
If the html code can be printed, it appears that the code used in html can be used. You will be able to use an img tag.
const htmlcode = '<html>'
+ '<head>'
+ '<title>Testing QR code</title>'
+ '</head>'
+ '<body>'
+ '<img id='barcode' src="https://api.qrserver.com/v1/create-qr-code/?data=HelloWorld&size=100x100" alt="" title="HELLO" width="50" height="50" />'
+ '</body>'
+ '</html>';
...
Print.printAsync({
html: htmlcode
});
Additional answers due to edited questions:
If you want to use QRCODE, you can't solve it with the module you want to use. Instead, you can solve this through 'React-native-web.' I will attach the link to the official document of Expo for the web setup. Once the setup is complete, use the existing app development method. However, the QRcode module does not currently work on the Web. The solution to this problem is to set QRCODE as an image file path, not as a web path in my first answer, and to show it when it's offline.
QRcode Example
import React, { Component } from 'react';
import QRCode from 'react-native-qrcode';
import { StyleSheet, View, TextInput } from 'react-native';
export default class HelloWorld extends Component {
state = {
text: 'testQRcode',
};
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
onChangeText={(text) => this.setState({text: text})}
value={this.state.text}
/>
<QRCode
value={this.state.text}
size={200}
bgColor='purple'
fgColor='black'/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
margin: 10,
borderRadius: 5,
padding: 5,
}
});

React native access front camera

I am using "react-native camera" library to access the camera. So here is my code
.
import React, { Component } from "react";
import {
AppRegistry,
Dimensions,
StyleSheet,
Text,
TouchableHighlight,
View
} from "react-native";
import Camera from "react-native-camera";
import { RNCamera, FaceDetector } from "react-native-camera";
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Camera
ref={cam => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}
>
<Text style={styles.capture} onPress=
{this.takePicture.bind(this)}>
take photo
</Text>
</Camera>
</View>
);
}
takePicture() {
const options = {};
//options.location = ...
this.camera
.capture({ metadata: options })
.then(data => console.log(data))
.catch(err => console.error(err));
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row"
},
preview: {
flex: 1,
justifyContent: "flex-end",
alignItems: "center"
},
capture: {
flex: 0,
backgroundColor: "#fff",
borderRadius: 5,
color: "#000",
padding: 10,
margin: 40
}
});
Here i am able to access the back camera and whenever i am clicking on take photo, it capture image and show that instantly. But i need two changes here.
Using this code, the back camera is getting accessed but i want to access the front camera.
Also, the image i captured, should not be shown instantly. I want a button , and whenever i click on that button, it navigates to different page and show all the images there.
Is it possible in react native ?? If yes, then please suggest me the changes that i require to make in this code
To answer your second question specifically, you have two options.
A) After taken the photo, store it as base64 uri into your state or redux, then whenever you need it, display the base64 uri as a image.
B) Utilize the package react-native-fs to access file system, storing the taken photo in iOS as cache, and retrieving it whenever needed.
Based on personal experience dealing with it, I would recommend option A
To switch your camera to back camera to front camera there exists a prop in Camera component called mirrorMode, if it is true it will show the front camera, otherwise, it will be the default mode that is back camera:
<Camera
...
mirrorImage={this.state.mirrorMode}
>
You can create a state and a button that change the state to switch between that 2 cameras.

React Native: I am getting error while trying to get image from https://cataas.com api

I am getting SyntaxError: Json Parse error: JSON Parse error: Unrecognized token '<'
I'm using https://cataas.com api for a react native app, my task is to generate a list of random kitten images. I tried using fetch method, but also i get error sorce.uri should not be an empty string. How can i solve this problem?
Here is my code:
import React, { Component } from 'react';
import {
Image,
StyleSheet,
Text,
View,
FlatList
} from 'react-native';
class App extends Component {
state = {
photos: '',
}
componentDidMount() {
fetch('https://cataas.com/cat?width=100')
.then(res => res.json())
.then(data => {
this.setState({
photos: data
})
.catch(err => {
console.log('error', err);
alert(err)
})
})
}
render() {
console.log(this.state.photos)
return (
<View style={styles.container}>
<Image
source={{url: this.state.photos}}
style={{height: 100, width: 100}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#ecf0f1',
}
});
export default App;
There is a typo in your code
Replace url with uri as in the docs
<Image
source={{uri: this.state.photos}}
style={{height: 100, width: 100}}
/>
You don't have to call this api manually, you could directly use the link in the Image component :
<Image
source={{uri: "https://picsum.photos/100/100"}}
style={{height: 100, width: 100}}
/>
EDIT:
Ok it's not as easy as I thought !
I created a first basic version : https://snack.expo.io/#sanjar/so-53434400
And contrary to what I thought it's always the same picture that is displayed.
It's because of react-native cache system that see the same url and decide to not execute the http request again.
then I checked the doc and founda way to fix this issue, but for ios only
I just had to change :
source={{uri: "https://source.unsplash.com/random"}}
by :
source={{uri: "https://source.unsplash.com/random", cache: 'reload'}}
It should work on ios (I don't have a mac with me now), for android I don't know yet, I'll probably investigate later.

React Native sharing content

I am trying to learn the basics of react native and have started with the basic tab template. I am trying to create a social media sharing screen, where you get the list of apps and airdrop on iOS to display but unfortunately this is not working. The code I have displays an empty view and I was expecting the share dialog to appear over the top. I get the view but no sharing options.
Here is the code
`
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { Share } from 'react-native';
var ActionSheetIOS = React;
export default class ShareScreen extends React.Component {
static navigationOptions = {
title: 'Share',
};
showShareActionSheet() {
ActionSheetIOS.showShareActionSheetWithOptions({
url: 'https://www.someurl.org',
});
}
render() {
return (
<View style={styles.container}>
this.showShareActionSheet();
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 15,
backgroundColor: '#000000',
},
});
`
any ideas why this isn't working?
You need to wrap your this.showShareActionSheet(); with brackets to get it working.
<View style={styles.container}>
{this.showShareActionSheet()}
</View>
Otherwise it's gonna be considered as plain text and not as an instruction
You can use this package https://www.npmjs.com/package/react-native-share for sharing, This package will help you share the content to WhatsApp, FB, and other social apps.

React Native Camera Roll

Haven't noticed much sample code/guides on how to use the CameraRoll library from React Native, I found the example in the docs a bit "vague" and confusing.
First time I'm using any of the API's so I do not fully understand how I'm suppose to use the library either. So far I've imported it like:
import {
AppRegistry,
Image,
StyleSheet,
TextInput,
Navigator,
CameraRoll,
Alert,
TouchableHighlight,
Button,
Text,
View
} from 'react-native';
quite confused with "Linking" etc, but as far as I know, this should be all I need to do in order to use the lib.
And how do I use it for something as simple as to open the gallery on the click of a button and let the user choose an image that should then be displayed in the app.
Thanks in advance, hope someone has some code to clarify this.
Here is some sample code that will grab the first 25 photos from your camera roll and display them in a ScrollView. I modified this from an example I found online here
import React, { Component, PropTypes } from 'react'
import {
CameraRoll,
Image,
ScrollView,
StyleSheet,
TouchableHighlight,
View,
} from 'react-native';
class CameraRollView extends Component {
constructor(props) {
super(props)
var controls = props.controls
this.state = {
images: [],
selected: '',
fetchParams: { first: 25 },
groupTypes: 'SavedPhotos',
}
this._storeImages = this._storeImages.bind(this)
this._logImageError = this._logImageError.bind(this)
this._selectImage = this._selectImage.bind(this)
}
componentDidMount() {
// get photos from camera roll
CameraRoll.getPhotos(this.state.fetchParams, this._storeImages, this._logImageError);
}
// callback which processes received images from camera roll and stores them in an array
_storeImages(data) {
const assets = data.edges;
const images = assets.map( asset => asset.node.image );
this.setState({
images: images,
});
}
_logImageError(err) {
console.log(err);
}
_selectImage(uri) {
// define whatever you want to happen when an image is selected here
this.setState({
selected: uri,
});
console.log('Selected image: ', uri);
}
render() {
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
<ScrollView style={styles.container}>
<View style={styles.imageGrid}>
{ this.state.images.map(image => {
return (
<TouchableHighlight onPress={() => this._selectImage(image.uri)}>
<Image style={styles.image} source={{ uri: image.uri }} />
</TouchableHighlight>
);
})}
</View>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
imageGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center'
},
image: {
width: 100,
height: 100,
margin: 10,
},
});
export default CameraRollView
Hmm, the package you are seeking is probably react-native-image-picker. It allows you to take a photo or select one from your native device gallery.
LINK: https://github.com/react-community/react-native-image-picker
In response to the linking issue. When you save a package using:
npm install --save react-native-image-picker
What is happening here is the --save part prepares the packages dependencies to be connected to native iOS and Android. This linking is done using the command react-native link.
In some cases packages require some manual linking aswell (for example, this package requires a small amount of native iOS and Android configuration)