Reactive Native Web: Open linked PDF from Webview within the App - android

Is there an easy way to open/display a PDF which is linked on a website shown via WebView within react-native-webview within the App or at least within the default PDF app on the phone?
How to "change" behaviour of the website, right now a linked PDF (e.g. Link is opened in the normal browser and than downloaded... instead it should just open up.
I found react-native-pdf and react-native-view-pdf but they seem to simply open local PDFs... How to actually inject and modify links ON the website to have that behaviour?
Thanks
EDIT:
What I tried is:
import Pdf from 'react-native-pdf';
showPdf(pdfurl) {
const source = {uri: pdfurl,cache:true};
//const source = require('./test.pdf'); // ios only
//const source = {uri:'bundle-assets://test.pdf'};
//const source = {uri:'file:///sdcard/test.pdf'};
//const source = {uri:"data:application/pdf;base64,..."};
return (
<View style={styles.container}>
<Pdf
source={source}
onLoadComplete={(numberOfPages,filePath)=>{
console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page,numberOfPages)=>{
console.log(`current page: ${page}`);
}}
onError={(error)=>{
console.log(error);
}}
style={styles.defaults.pdf} />
</View>
)
}
Within the Webview config:
onShouldStartLoadWithRequest={(event) => {
if (event.url.includes('.pdf')) {
this.showPdf(event.url);
return false;
}
return true;
}}

Related

make browsers with react native

I have a mobile site and I want to make an android browser app where I want to open my site.
I have tried and react-native-browser. Something like..
import {
processColor, // make sure to add processColor to your imports if you want to use hex colors as shown below
} from 'react-native';
// at the top of your file near the other imports
var Browser = require('react-native-browser');
class SampleApp extends Component {
render() {
return (
<View style={{paddingTop:20, flex:1}}>
{Browser.open('https://google.com/')}
</View>
);
}
}
But got no success...
I just want to make a browser that opens my mobile site..
Is there any better way of doing this or if someone has any idea how to use react-native-browser ?
Thanks in advance
Looking at the source code, it seems this browser is only available on iOS.
you must search in this web https://js.coach/react-native?search=browser for example https://github.com/d-a-n/react-native-webbrowser
Why are you trying to look for an external library while there is a WebView Component already integrated with react-native itself?
WebView renders web content in a native view. You can use this
component to navigate back and forth in the web view's history and
configure various properties for the web content.
You can just add a WebView and open up the desired web url.
Example
import React, { Component } from 'react';
import { WebView } from 'react-native';
class MyWeb extends Component {
render() {
return (
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
);
}
}
You can use https://www.npmjs.com/package/react-native-webbrowser
Install:
npm i react-native-webbrowser --save
Use:
class SampleApp extends Component {
render() {
return (
<View style={{paddingTop:20, flex:1}}>
<Webbrowser
url="https://your-url.com"
hideHomeButton={true}
hideToolbar={true}
hideAddressBar={true}
hideStatusBar={true}
foregroundColor={'#efefef'}
backgroundColor={'#333'}
/>
</View>
);
}
Set all the hide's props to true to make your app display only the site

ReactNative - Webview not executing injectedJavaScript on Android

I'm having trouble getting the Webview on ReactNative to execute the injected JavaScript on a physical Android device. I've scoured the web as far as I could over the past 2 days and still haven't found a solution. Results for testing are as follows:
iOS simulator - All good
iPhone - All good
Android simulator - All good
Physical devices, Sony Xperia Z2, Sony Xperia Z5 Compact and LG G4 - NOTHING
My Webview is defined as follows:
<WebView
style={styles.webView}
source={{
html: html,
baseUrl: 'web/'
}}
injectedJavaScript={'render(' + JSON.stringify(this.state.data) + ');'}
javaScriptEnabledAndroid={true}
scrollEnabled={false}
bounces={false}
renderLoading={() => <LoadingIndicator />}
/>
I've tried specifying javaScriptEnabled as well, to no avail. I also tried smaller scripts to just colour elements on the page or post a message back to the app using window.postMessage, but nothing happens. I need to inject the data to the HTML, which will render graphs for me based on the supplied data. My last resort is to manually construct the HTML with the data appended as part of the markup being supplied to the Webview, but I'd really like to keep it simple and just get it to work the way it should.
I'm using the latest version of ReactNative (0.41) and the phones are running Android 6+.
I just discovered that the Android WebView appears to inject any JS as a single line, even if it includes line breaks. That means that missing semicolons can definitely cause issues, or, in my case, comments delimited by //. Using /* and */ for comments got my injected JavaScript working again.
Well, after leaving this question open for some time and finding a (not so elegant) solution to the problem, I decided I'd share what I ended up doing:
I declared the HTML to be passed to the WebView in a constant string along these lines: const html = '<html>...<script>...[INJECTEDSCRIPT]</script></html>';
I retrieved the data in the componentDidMount() event of the React component's lifecycle and set a state variable for it using this.setState({ data: retrievedData });
This, of course, forced the component to re-render itself, after which I now have the data available to "pass" to the WebView
Seeing as I couldn't find any elegant or usable way of using the injectedJavaScript property to work the way I want it to (as stated in the question), I resorted to replacing the [INJECTEDSCRIPT] value in the HTML constant with the serialized data.
Not the most elegant solution, I know, but it's the only one I could get working reliably across a multitude of devices and emulator configurations. Sample, edited for brevity, as below:
const html = `
<!DOCTYPE html>
<html>
<head>...</head>
<body>...</body>
<script>
var render = function (data) {
...
};
[INJECTEDSCRIPT]
</script>
</html>`;
export class GraphComponent extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount = () => {
SERVICE.getData().done((data) => {
this.setState({ data: data });
});
}
render = () => {
if (!this.state.data)
return <LoadingIndicator />;
let serializedData = JSON.stringify(this.state.data);
return
<WebView
style={styles.webView}
source={{
html: html.replace('[INJECTEDSCRIPT]', 'render(' + serializedData + ');'),
baseUrl: 'web/'
}}
scrollEnabled={false}
bounces={false}
renderLoading={() => <LoadingIndicator />}
/>;
}
}
I have been struggling to get Javascript (pre-embed and injected) to be executed as well. So far, a simple and better solution is to use WebViewBridge. After, everything worked as expected. Here a link to the package: cnpmjs.org/package/react-native-webview-bridge.
Here a demo:
import React, {
Component
} from 'react';
import PropTypes from 'prop-types';
import {
Platform,
WebView,
ActivityIndicator,
} from 'react-native';
import WevViewBridge from 'react-native-webview-bridge';
// TODO: Keep in mind that you should ALWAYS edit
// two separate file and keep them matched.
const IOS_WEB_PAGE_SOURCE = './webPage/wordBody.html';
const ANDROID_WEB_PAGE_SOURCE = 'file:///android_asset/webPage/wordBody.html';
export default class WordBody extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
}
_injectedScript = `
(function(){
let data = ${JSON.stringify(this.props.data)};
refresh(data);
})();
`;
render() {
/*
* We are using Platform.select here for two reasons:
* 0. Everythig work fine for IOS using `WebView` from RN
* 1. Android doesn't load web ressource with `require`
* 2. On Android, `WebView` from react-native doesn't
* execute JavaScript so we use `WebViewBridge`.
*/
return Platform.select({
ios: (<WebView
style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
scalesPageToFit
source = {require(IOS_WEB_PAGE_SOURCE)}
javaScriptEnabled={true}
originWhitelist={['*']}
injectedJavaScript = {this._injectedScript}
renderLoading={() => <ActivityIndicator/>}
/>),
android: (<WevViewBridge
style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
scalesPageToFit
source = {{uri:ANDROID_WEB_PAGE_SOURCE}}
javaScriptEnabled={true}
// originWhitelist={['*']}
injectedJavaScript = {this._injectedScript}
renderLoading={() => <ActivityIndicator/>}
/>)
});
};
}

React Native WebView, how to store username and password

i am creating a very simple app for android and iphone/ipad that uses only webview.
How can i store username and password so the user would not have to type them in every single time. Thanks in advance.
import React, { Component } from 'react';
import {
AppRegistry,
WebView
} from 'react-native';
export default class myapp extends Component {
render() {
return (
<WebView
source={{uri: 'https://mysecretappurl.com'}}
/>
);
}
}
AppRegistry.registerComponent('myapp', () => myapp);
Thank you Fabian for a quick response.
I got it solved with injectedJavascript and the data persist even if i close and relaunch the app both on android and ios. I got stuck as at first as tried to go with asyncstorage and reactnativ-webview-bridge but i failed to implement them due to my lack of knowledge.
import React, { Component } from 'react';
import {
AppRegistry,
WebView
} from 'react-native';
export default class myapp extends Component {
render() {
let jsCode = `
var cookie={};
document.cookie.split('; ').forEach(function(i){cookie[i.split('=')[0]]=i.split('=')[1]});
document.querySelector('#email').value=cookie['email'] || '';
document.querySelector('#password').value=cookie['password'] || '';
document.querySelector('#login button').onclick = function(){
document.cookie = 'email='+document.querySelector('#email').value;
document.cookie = 'password='+document.querySelector('#password').value;
};
`;
return (
<WebView
source={{uri: 'https://mysecretappurl.com'}}
injectedJavaScript={jsCode}
/>
);
}
}
AppRegistry.registerComponent('myapp', () => myapp);
I don't know if I understand you correctly:
You are writing a "minified browser" with react native only to show your webpage and you want to prefill the login form on that page?
If it's true you are searching for a possibility to exchange data from your React Native app with your page in the WebView component. Take a look at this tutorial of react-native-webview-bridge .
I would try the following:
Communicate with your Webpage and establish a listener for your login form to pass the credentials to your RN app
Use a module like react-native-simple-store to store the credentials in your RN app
If you start the app the next time check your storage and if the credentials are not empty send them to your webpage via bridge/injected javascript

Downloading data files with React Native for offline use

I am wanting to develop an app where a user can select different gamepacks to install on their android or ios device. A gamepack will consist of:
one or more JSON files
images
sounds
Right now I'm not really concerned if these are transferred individually or in a zip file (though a zip file would certainly be preferred). Of course the device will need to be connected to the Internet to get the current list of gamepacks and to download the ones that the user chooses. Once the gamepacks are downloaded to the phone the user will not need an Internet connection to play the game as they will have all the files.
How do I go about implementing this in my project?
How do I download the files? Would I use the react-native-fetch-blob library and save it in a specific location? This library refers to saving it as "cache" rather than permanently, so I am not sure if this is the correct solution. The specific section I am looking at on the library page is "Use Specific File Path". But because it is cache, should I be looking for something else that is more of a longer term storage? It does say on the page that files will not be deleted so I am a bit confused as to what the difference between permanent storage and cache is in this case.
Once the files are downloaded would I then be able to open images and display them, open sound files and play them, and open the json files and process them?
Check out React Native FS, specifically the documentation on downloadFile:
https://github.com/johanneslumpe/react-native-fs#downloadfileoptions-downloadfileoptions--jobid-number-promise-promisedownloadresult-
Here's a working example:
import React, { Component } from 'react';
import {
AppRegistry,
Text,
View,
Image,
} from 'react-native';
import RNFS from 'react-native-fs';
export default class downloadFile extends Component {
constructor() {
super()
this.state = {
isDone: false,
};
this.onDownloadImagePress = this.onDownloadImagePress.bind(this);
}
onDownloadImagePress() {
RNFS.downloadFile({
fromUrl: 'https://facebook.github.io/react-native/img/header_logo.png',
toFile: `${RNFS.DocumentDirectoryPath}/react-native.png`,
}).promise.then((r) => {
this.setState({ isDone: true })
});
}
render() {
const preview = this.state.isDone ? (<View>
<Image style={{
width: 100,
height: 100,
backgroundColor: 'black',
}}
source={{
uri: `file://${RNFS.DocumentDirectoryPath}/react-native.png`,
scale: 1
}}
/>
<Text>{`file://${RNFS.DocumentDirectoryPath}/react-native.png`}</Text>
</View>
) : null;
return (
<View>
<Text onPress={this.onDownloadImagePress}>Download Image</Text>
{preview}
</View>
);
}
}
AppRegistry.registerComponent('downloadFile', () => downloadFile);
It's important to know that the height and width must be set on the Image

Cache image on react native

Is there a good library or maybe some default react native components that cache the image from a url?
I've tried react-native-cache-image but there are a lot of issues with react-native-fs and react-native-sqlite-storage and as I am new to react native I dont know how to fix them properly.
yarn add react-native-fast-image
Ref: https://github.com/DylanVann/react-native-fast-image
import FastImage from 'react-native-fast-image'
<FastImage
style={{ width: 200, height: 200 }}
source={{ uri: 'https://unsplash.it/400/400?image=1' }}
resizeMode={FastImage.resizeMode.stretch}
/>
You may be interested in my higher order component module that adds performance related image caching and "permanent cache" functionality to the native <Image> component. My module depends on react-native-fetch-blob which is the goto well-respected and battle-tested library for downloading files, so you shouldn't have dependency problems.
React Native Image Cache HOC
Tl;DR Code Example:
import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
export default class App extends Component<{}> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/rc29s4bz61uz.png'}} />
<CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/hhhim0kc5swz.jpg'}} permanent={true} />
</View>
);
}
}
The first image will be cached until the total local cache grows past 15 MB (by default) then cached images are deleted oldest first until total cache is below 15 MB again.
The second image will be stored to local disk permanently. People use this as a drop in replacement for shipping static image files with your app.
It also sounds like you are interested in arbitrarily storing image files to local disk. You can do that with a CacheableImage static method like so:
import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
CacheableImage.cacheFile('https://i.redd.it/hhhim0kc5swz.jpg', true)
.then( localFileInfo => {
console.log(localFileInfo);
// Console log outputs:
//{
// url: 'https://i.redd.it/rc29s4bz61uz.png',
// cacheType: 'permanent',
// localFilePath: '/this/is/absolute/path/to/file.jpg'
//}
});
Hope this helps!
Straight from the Expo docs:
import React from 'react';
import Expo from 'expo';
import { Image, View } from 'react-native';
import logo from './assets/icon.png';
const cacheImages = images => images.map(image => {
if (typeof image === 'string') return Image.prefetch(image);
return Expo.Asset.fromModule(image).downloadAsync();
});
class View extends React.component {
state = {
appIsReady: false
}
componentWillMount() {
this.loadAssetsAsync();
}
async loadAssetsAsync = () => {
const imageAssets = cacheImages([icon]);
await Promise.all([...imageAssets]);
this.setState({ appIsReady: true });
}
render() {
return(
<View>
<Image source={logo} style={styles.imageStyle} />
</View>
);
}
}
I think the code snippet is straight forward. You will have a state that will set to true when all your images are loaded. You will have the cacheImages function that will handle the work for you. The only requirement you will need is Expo.
I have used this library and working in both android and ios phones. It is working in Both EXPO and ReactNative. In react native automatically stored catch images.
For Installation the library:
yarn add picache
then use in your js file like this, first import the file and used it. For more information click
import Picache from "picache";
const App = () => (
<Picache
style={{ height: 150, width: 350 }}
source={require("./square.png")}
/>
);
There's prefetch() method built in Image component.

Categories

Resources