I have a webview source that I have opened using react-native-webview, I want to access any and every url and console it whenever I click on the webview, so that I can use it as source for another webview. However am unable to figure how to do it
I tried using NavigationStateChange and onShouldLoadSTartwithrequest but that did not help.
below is my code
import React, {useState, useRef, useEffect} from 'react';
import {WebView} from 'react-native-webview';
import {
View,
SafeAreaView,
ActivityIndicator,
StyleSheet,
TouchableOpacity,
Text,
Linking,
Alert,
BackHandler,
} from 'react-native';
import Footer from '../components/Footer';
import {useBackHandler} from '#react-native-community/hooks';
import OnlineConsultationWebviewScreen from './OnlineConsultationWebviewScreen';
export default function ConsultationHomeScreen(props) {
const uri = props.route.params.uri;
const [canGoBack, setCanGoBack] = useState(false);
const [canGoForward, setCanGoForward] = useState(false);
const [currentUrl, setCurrentUrl] = useState('');
const webviewRef = useRef(null);
const renderLoadingView = () => {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<ActivityIndicator size="large" />
</View>
);
};
const onMessage = (e) => {
// retrieve event data
var data = e.nativeEvent.data;
// maybe parse stringified JSON
try {
data = JSON.parse(data);
} catch (e) {}
// check if this message concerns us
if ('object' == typeof data && data.external_url_open) {
// proceed with URL open request
return Alert.alert(
'External URL',
'Do you want to open this URL in your browser?',
[
{text: 'Cancel', style: 'cancel'},
{text: 'OK', onPress: () => Linking.openURL(data.external_url_open)},
],
{cancelable: false},
);
}
};
const jsCode = `!function(){var e=function(e,n,t){if(n=n.replace(/^on/g,""),"addEventListener"in window)e.addEventListener(n,t,!1);else if("attachEvent"in window)e.attachEvent("on"+n,t);else{var o=e["on"+n];e["on"+n]=o?function(e){o(e),t(e)}:t}return e},n=document.querySelectorAll("a[href]");if(n)for(var t in n)n.hasOwnProperty(t)&&e(n[t],"onclick",function(e){new RegExp("^https?://"+location.host,"gi").test(this.href)||(e.preventDefault(),window.postMessage(JSON.stringify({external_url_open:this.href})))})}();`;
return (
<SafeAreaView style={{flex: 1}}>
<WebView
source={{
uri: uri,
}}
renderLoading={renderLoadingView}
javaScriptEnabled={true}
domStorageEnabled={true}
startInLoadingState={true}
ref={webviewRef}
injectedJavaScript={jsCode}
onMessage={onMessage}
onError={console.error.bind(console, 'error')}
// onShouldStartLoadWithRequest={(event) => {
// if (event.url !== uri ){
// Linking.openURL(event.url);
// console.log('Event', event.url);
// return false;
// }
// return true;
// }}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
tabBarContainer: {
padding: 20,
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#b43757',
},
button: {
color: 'white',
fontSize: 24,
},
});
Am stuck with this since long, please let me know how do I access any and every click and console it, so that I can sue it later as a new source for Webview.
Any suggestion would be great.
Try this :
<WebView
ref="webview"
source={uri}
onNavigationStateChange={this._onNavigationStateChange.bind(this)}
javaScriptEnabled = {true}
domStorageEnabled = {true}
injectedJavaScript = {this.state.cookie}
startInLoadingState={false}
/>
Add this function :
_onNavigationStateChange(webViewState){
console.log(webViewState.url)
}
FYI webviewState object consists of, use the url property:
{
canGoBack: bool,
canGoForward: bool,
loading: bool,
target: number,
title: string,
url: string,
}
let me know if it helps
Related
I am trying to use expo-image-picker to select a image on a user phone by clicking the camera icon select the image have it preview on screen then click add photo to add to database.
However, I am currently getting [TypeError: onChangeImage is not a function. (In 'onChangeImage(result.uri)', 'onChangeImage' is undefined)] after I select the image in camera roll and return to the upload screen component.
import React, {useEffect} from 'react';
import { StyleSheet, View , Image, TouchableWithoutFeedback, Alert, Text } from 'react-native'
import { MaterialCommunityIcons } from "#expo/vector-icons";
import * as ImagePicker from 'expo-image-picker';
import AppButton from '../components/AppButton';
function ChildPictureUploader({imageUri, onChangeImage}) {
useEffect(() => {
requestPermission();
}, []);
const requestPermission = async () => {
const {granted} = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!granted) alert("Please enable permission to access photo roll");
};
const handlePress = () => {
if(!imageUri) selectImage();
else Alert.alert('Delete', 'Are you sure you want to delete this image?',
[{text: "YES", onPress: () => onChangeImage(null)},
{text: "No"},
])
};
const selectImage = async () => {
try {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 0.5
});
if(!result.cancelled) onChangeImage(result.uri);
} catch (error) {
console.log('Error Reading an Image', error)
}
};
//add a button instead of icon
return (
<TouchableWithoutFeedback onPress={handlePress}>
<View style={styles.container}>
{/* <Text>Click Camera to select a photo</Text> */}
{!imageUri && <MaterialCommunityIcons name="camera" size={150} color={"#098731"}/>}
{imageUri && <Image source={{uri: imageUri}} style={styles.image}/>}
<AppButton title="Add Photo" onPress={()=> console.log('PHOTO ADDED')}></AppButton>
</View>
</TouchableWithoutFeedback>
);
}
const styles = StyleSheet.create({
container: {
flex:1,
backgroundColor: 'white',
borderRadius: 15,
justifyContent: 'center',
alignItems:'center',
// height: 250,
// width: 250,
overflow: 'hidden',
},
image:{
width:200,
height:200,
}
})
export default ChildPictureUploader;
It simply means you are not passing onChangeImage to ChildPictureUploader. Make sure the parent component is passing it as props
I just have some question.
First, I'm beginner... Very sorry.
I have been developed the react-native app (iOS) for recognize the QR Code.
I already have been succeed the recognize the QR Code on my App.
But My Plan is..
First, I scan the QR Code and I want to save the QR data.
For the purpose, there are many QR Code in the warehouse above many box and it is included Serial Number.
After scanning, I will continue the scan until I want to stop it. (In conclusion, I scan many times.)
I just thought "Let's save the Serial Number at array.
Second, I save the serial number through the scanning and I want to copy our clipboard.
Also, I want to print in my App.
How can I implement this? I have no idea about that.
Code is here.
import { StatusBar } from 'expo-status-bar';
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function App() {
const [hasPermisson, setHasPermisson] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('Not yet scanned')
const askForCameraPermisson = () => {
(async () => {
const {status} = await BarCodeScanner.requestPermissionsAsync();
setHasPermisson(status == 'granted')
})()
}
useEffect(() => {
askForCameraPermisson ();
}, [])
const handleBarCodeScanned = ({type, data}) => {
setScanned(true);
setText(text);
state = {
sn : [
{
type : type,
data : data
},
{
type : type,
data : data
}
]
}
console.log(state)
}
if (hasPermisson === null) {
return (
<View style={styles.container}>
<Text>Requesting for camera permisson</Text>
<StatusBar style="auto" />
</View>
)
}
if(hasPermisson === false) {
return (
<View style={styles.container}>
<Text>No acess to camera</Text>
<Button title={'Allow Camera'} onPress={() => askForCameraPermisson()}/>
</View>
)
}
return (
<View style={styles.container}>
<View style={styles.barcodebox}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style = {{ height:400, width:400}} />
</View>
<Text style={styles.maintext}>{text}</Text>
{scanned && <Button title={'scan again?'} onPress={( ) => setScanned(false)} color='tomato'/>}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
barcodebox: {
alignItems: 'center',
justifyContent: 'center',
height: 300,
width:300,
overflow:'hidden',
borderRadius:30,
backgroundColor:'tomato'
},
maintext: {
fontSize :16,
margin:20
}
});
When I click the hardware button on android the app closes, I want to go back on the previous page,
This is my code
import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { ActivityIndicator, Linking, SafeAreaView, StyleSheet, Text, View } from 'react-native';
import { WebView } from 'react-native-webview';
export default function App() {
const [isLoadong, setLoading] = useState(false);
return (
<SafeAreaView style={styles.safeArea}>
<WebView
originWhiteList={['*']}
source={{ uri: 'https://google.com' }}
style={styles.container }
onLoadStart={(syntheticEvent) => {
setLoading(true);
}}
onShouldStartLoadWithRequest={(event)=>{
if (event.navigationType === 'click') {
if (!event.url.match(/(google\.com\/*)/) ) {
Linking.openURL(event.url)
return false
}
return true
}else{
return true;
}
}}
onLoadEnd={(syntheticEvent) => {
setLoading(false);
}} />
{isLoadong && (
<ActivityIndicator
color="#234356"
size="large"
style={styles.loading}
/>
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#234356'
},
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
},
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
There are built-in goBack() method available in react-native-webview libraries
you can use the API method to implement back navigation of webview.
For this, you have to get the reference of react-native-webview component and call method from the reference object.
also, you are able to put the listener on the Android Native Back Button Press event to call the goBack() method of webview.
try the following code...
import { StatusBar } from 'expo-status-bar';
import React, { useState, useRef, useEffect } from 'react';
import { ActivityIndicator, Linking, SafeAreaView, StyleSheet, BackHandler } from 'react-native';
import { WebView } from 'react-native-webview';
export default function App() {
const webViewRef = useRef()
const [isLoadong, setLoading] = useState(false);
const handleBackButtonPress = () => {
try {
webViewRef.current?.goBack()
} catch (err) {
console.log("[handleBackButtonPress] Error : ", err.message)
}
}
useEffect(() => {
BackHandler.addEventListener("hardwareBackPress", handleBackButtonPress)
return () => {
BackHandler.removeEventListener("hardwareBackPress", handleBackButtonPress)
};
}, []);
return (
<SafeAreaView style={styles.safeArea}>
<WebView
originWhiteList={['*']}
source={{ uri: 'https://google.com' }}
style={styles.container}
ref={webViewRef}
onLoadStart={(syntheticEvent) => {
setLoading(true);
}}
onShouldStartLoadWithRequest={(event)=>{
if (event.navigationType === 'click') {
if (!event.url.match(/(google\.com\/*)/) ) {
Linking.openURL(event.url)
return false
}
return true
}
else{
return true;
}
}}
onLoadEnd={(syntheticEvent) => {
setLoading(false);
}}
/>
{isLoadong && (
<ActivityIndicator
color="#234356"
size="large"
style={styles.loading}
/>
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#234356'
},
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
},
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
first add ref for access your webview like that:
<WebView
ref={WEBVIEW_REF}
then for access to Hardware Back Button you can use this:
import { BackHandler } from 'react-native';
constructor(props) {
super(props)
this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonClick);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButtonClick);
}
handleBackButtonClick() {
this.refs[WEBVIEW_REF].goBack();
return true;
}
in handleBackButtonClick you can do back for webview and add this.refs[WEBVIEW_REF].goBack(); . I Hope that's helpful:)
Here is a simple solution using the magic of React's State.
Hope this helps.
import React, { useRef, useState } from 'react'
export default function Component () {
// This is used to save the reference of your webview, so you can control it
const webViewRef = useRef(null);
// This state saves whether your WebView can go back
const [webViewcanGoBack, setWebViewcanGoBack] = useState(false);
const goBack = () => {
// Getting the webview reference
const webView = webViewRef.current
if (webViewcanGoBack)
// Do stuff here if your webview can go back
else
// Do stuff here if your webview can't go back
}
return (
<WebView
source={{ uri: `Your URL` }}
ref={webViewRef}
javaScriptEnabled={true}
onLoadProgress={({ nativeEvent }) => {
// This function is called everytime your web view loads a page
// and here we change the state of can go back
setWebViewcanGoBack(nativeEvent.canGoBack)
}}
/>
)
}
i am having function that toggle the state variables value.
the initial value of the state variable is false
Here is my function...
expandLists(label){ // "label" is a state variable that passed as a string
let result = new Boolean();
console.log(this.state);
if(this.state.label){
result=false;
console.log('Result = false');
}
else{
result=true;
console.log('Result = true');
}
this.setState({[label]: result},console.log(this.state))
}
In the above expression at inital state the value is changed to false then it is not changing to true.
I have also tried.. the below method...
expandLists(label){
this.setState( preState => ({label: !this.preState.label}),console.log(this.state))
}
If you pass the label parameter as a string, then try this:
expandLists(label){ // "label" is a state variable that passed as a string
let result = new Boolean();
console.log(this.state);
if(this.state[label]){
result=false;
console.log('Result = false');
}
else{
result=true;
console.log('Result = true');
}
this.setState({[label]: result},console.log(this.state))
}
So the difference is in checking if the current value is truethy. In stead of using this.state.label, use this.state[label].
Check this way as you said "label" param type of string
if(this.state.label == "true"){
...
}
or
if(this.state[label]){
...
}
Easy way to achieve this is
toggleLabelValue = (label) => {
this.setState({ [label]: !this.state[label] }, () =>
console.log(this.state)
);
};
Try toggling state in this way:
import React from 'react';
import {
View,
Button,
} from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
label: false
}
}
changeLabel = (currentLabel) => {
this.setState({
label: currentLabel
});
};
toggleLabel = () => {
this.changeLabel(!this.state.label);
};
render() {
return (
<View>
<Button onPress={this.toggleLabel} title="Toggle Label" />
</View>
);
}
}
Here is another implementation using hooks:
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
export default function App() {
const [label, setLabel] = useState(false);
const toggleLable = () => {
let temp = label;
setLabel(!temp);
};
return (
<View style={styles.container}>
<TouchableOpacity
onPress={toggleLable}
style={[
styles.btn,
{ backgroundColor: label ? '#4f4' : '#f40' },
]}>
<Text style={styles.text}>{label? "TRUE": "FALSE"}</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
btn: {
width: 200,
height: 200,
borderRadius: 20,
justifyContent: "center"
},
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
alignItems: 'center',
},
text:{
fontSize: 40,
fontWeight: "bold",
color: "white",
textAlign: "center"
}
});
Screenshot:
You can play around with the code here: Toggle Button Example
this works for me using useState:
import React, { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import SeparateModal from 'components/SeparateModal';
export default function Parent() {
const [modalVisible, setModalVisible] = useState(false);
return (
<View>
<SeparateModal
modalVisible={modalVisible}
setModalVisible = {setModalVisible}
/>
<TouchableOpacity>
<Text onPress = { () => setModalVisible(true) }>Open Modal</Text>
</TouchableOpacity>
</View>
)
}
components/SeparateModal:
export default function SeparateModal({ modalVisible, setmodalVisible }) {
return (
<Modal
visible={ modalVisible }
animationType="slide"
>
<View>
<TouchableOpacity>
<Text onPress = { () => setModalVisible(false) }>Close Modal</Text>
</TouchableOpacity>
</View>
</Modal>
);
Google.logInAsync function which is supposed to used for OAuth in React Native, is not giving any response, or is stuck. Take a look at my code, from line 14 to 36. This signIn function is calling Google.logInAsync and is stuck there. No error, nothing. How ever when I press the button again, and the signIn function gets called again, an error is passed: Error: Cannot start a new task while another task is currently in progress: Get Auth
This is my code currently. It shows a label " Sign In With Google" and a Button on the screen. Tapping on the button starts this.signIn funtion, prints "Started logInAsync" on the debugger screen, but theGoogle.logInAsync never completes, no errors are thrown anywhere. console.log('Completed logInAsync') never gets executed!
import {Google} from "expo"
...
console.log('Starting logInAsync')
const result = await Google.logInAsync({
androidClientId:
"<CLIENT_ID>",
scopes: ["profile", "email"]
})
console.log('Completed logInAsync')
that gave an error saying install "expo-google-app-auth"
I installed that module and then changed the import and the function
import * as Google from "expo-google-app-auth"
...
console.log('Starting logInAsync')
const result = await Google.logInAsync({
...
})
console.log('Completed logInAsync')
And after there,
import { StyleSheet, Text, View, Image, Button } from "react-native"
import * as Google from "expo-google-app-auth"
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
signedIn: false,
name: "",
photoUrl: ""
}
}
signIn = async () => {
try {
console.log('Starting logInAsync')
const result = await Google.logInAsync({
androidClientId:
"860657237026-jfosg5hu52u1vedclccs1vgihghva534.apps.googleusercontent.com",
scopes: ["profile", "email"]
})
console.log('Completed logInAsync')
if (result.type === "success") {
this.setState({
signedIn: true,
name: result.user.name,
photoUrl: result.user.photoUrl
})
} else {
console.log("Cancelled!")
}
} catch (e) {
console.log("Error: ", e)
}
}
render() {
return (
<View style={styles.container}>
{this.state.signedIn ? (
<LoggedInPage name={this.state.name} photoUrl={this.state.photoUrl} />
) : (
<LoginPage signIn={this.signIn} />
)}
</View>
)
}
}
const LoginPage = props => {
return (
<View>
<Text style={styles.header}>Sign In With Google</Text>
<Button title="Sign in with Google" onPress={() => props.signIn()} />
</View>
)
}
const LoggedInPage = props => {
return (
<View style={styles.container}>
<Text style={styles.header}>Welcome:{props.name}</Text>
<Image style={styles.image} source={{ uri: props.photoUrl }} />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
},
header: {
fontSize: 25
},
image: {
marginTop: 15,
width: 150,
height: 150,
borderColor: "rgba(0,0,0,0.2)",
borderWidth: 3,
borderRadius: 150
}
})
No error occur in this code, except if you press the SignIn button twice. I am on the latest version of Node, React-Native and Expo as of now.