React Native inactivity logout - react-native

Hello i want to make an inactivity log out for my app, so if the user doesn't do anything on the app for 3 minutes, the app will comeback to the login screen.
I'm using expo, react native navigation V6, and functional components.
i haven't been able to figure how to do it. please help.

I think i was able to do that on an app that i made 1 year ago, i think this code can you help you.
const ManageExpenses = ({ route, navigation }) => {
const [time, setTime] = useState(0);
useEffect(() => {
let mounted = true;
if (mounted) {
tick();
}
return () => mounted = false;
}, []);
function tick() {
let timer = setInterval(() => {
setTime((prevTime) => (prevTime = prevTime + 1));
}, 1000);
}
if (time >= 10) {
navigation.goBack();
}
function pressHandler() {
setTime(0)
}
return (
<Pressable onPress={pressHandler} style={styles.container}>
</Pressable>
);
export default ManageExpenses;
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
I created a Pressable component around the entire screen and it redefine the time state when the user press the screen. I hope this will help you!

Related

React native logout for inactivity

I have to make a react-native app (not using expo nor hooks) that can login into a user, read some simple info and then logout through a logout button or automatically due to inactivity.
I have no issues with the login, setting the timer, nor the logout button, however I have no idea of how to detect 'inactivity', is this posible with states? and how exactly?
General concensus seems to be to use PanResponder:
get user inactivity in react native
Check for Inactivity in a React Native App
state = {};
_lastInteraction = new Date();
_panResponder = {};
componentWillMount() {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: this.handleStartShouldSetPanResponder,
onMoveShouldSetPanResponder: this.handleMoveShouldSetPanResponder,
onStartShouldSetPanResponderCapture: () => false,
onMoveShouldSetPanResponderCapture: () => false,
onPanResponderTerminationRequest: () => true,
onShouldBlockNativeResponder: () => false,
});
this._maybeStartWatchingForInactivity();
}
_maybeStartWatchingForInactivity = () => {
if (this._inactivityTimer) {
return;
}
this._inactivityTimer = setInterval(() => {
if (
new Date() - this._lastInteraction >= TIME_TO_WAIT_FOR_INACTIVITY_MS
) {
this._setIsInactive();
}
}, INACTIVITY_CHECK_INTERVAL_MS);
};
// NOTE: you almost certainly want to throttle this so it only fires
// every second or so!
_setIsActive = () => {
this._lastInteraction = new Date();
if (this.state.timeWentInactive) {
this.setState({ timeWentInactive: null });
}
this._maybeStartWatchingForInactivity();
};
_setIsInactive = () => {
this.setState({ timeWentInactive: new Date() });
clearInterval(this._inactivityTimer);
this._inactivityTimer = null;
};
render() {
return (
<View
style={styles.container}
collapsable={false}
{...this._panResponder.panHandlers}>
<Text style={styles.paragraph}>
Put your app here
{' '}
{this.state.timeWentInactive &&
`(inactive at: ${this.state.timeWentInactive})`}
</Text>
<Button
title="Here is a button for some reason"
onPress={() => alert('hi')}
/>
</View>
);
You can use import AsyncStorage from '#react-native-async-storage/async-storage';
So basically, whenever user visits the app, you can store the time in which user logged in.
like this
const storeData = async (value) => {
try {
await AsyncStorage.setItem('#last_visited', new Date().toString())
} catch (e) {
// saving error
}
}
and then when user again comes back to visit the app, you can check for the difference in that time and the time stored in Async storage.
first
const getData = async () => {
try {
const value = await AsyncStorage.getItem('#last_visited')
if(value !== null) {
if(new Date() - value > 5){
// here check if time diff is what as per you is inactive then logout user
// for example ive kept 5 hours
logout()
}
// value previously stored
}
} catch(e) {
// error reading value
}
}
Hope it helps. feel free for doubts

react native restart application

I created a barcode scanner App using expo-barcode-scanner.
I have some problems.
The purpose of the scanner is to get the barcode number and send it to barcode.monster and get product details. It works, but I have two main problems which I dont know what should I look for and how to resolve.
After the scanner get a barcode, I want to send to a confirmation screen, where the User should add the product into a category.
const handleBarCodeScanned = ({ type, data }) => {
reqForProduct(data);
setScanned(true);
setText(data);
navigation.navigate('Confirmation');
};
The function above is executed when the barcode camera find a number.
const reqForProduct = async barcode => {
try {
const Product = await axios.get(`https://barcode.monster/api/${barcode}`);
console.log(Product.data);
} catch (error) {
console.log(error);
}
}
The function above is responsible to get the product data.
THE NAVIGATION WORKS, BUT IF I PRESS THE BACK BUTTON AFTER THE FUNCTION SEND ME TO THE CONFIRMATION SCREEN, I CANNOT RESCAN OTHER BARCODE UNLESS I PRESS R (RELOAD) IN THE CONSOLE... THIS IS MY FIRST PROBLEM. Moreover, after coming back to the screen, the console is stucked with the last product fetched from the api.
The second problem is is to transfer the data fetched to the confirmation screen. I tried with the navigation prop like navigation.navigate('Confirmation', {fetchedDataObj} but is not working....
<Stack.Screen
name='Confirmation'
component={AddToContainerScreen} />
THE FULL PAGE CODE BELLOW ----------------------------------------------------
import {View, Text, Button, StyleSheet} from 'react-native';
import {useState, useEffect} from 'react';
import { BarCodeScanner } from 'expo-barcode-scanner';
import axios from 'axios';
const Scanner = ({navigation}) => {
const [permission, setPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('');
const permissionCamera = () => {
( async () => {
const {status} = await BarCodeScanner.requestPermissionsAsync();
setPermission(status == 'granted');
})()
}
const reqForProduct = async barcode => {
try {
const Product = await axios.get(`https://barcode.monster/api/${barcode}`);
console.log(Product.data);
} catch (error) {
console.log(error);
}
}
// Execute permission
useEffect(() => {
permissionCamera();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
reqForProduct(data);
setScanned(true);
setText(data);
navigation.navigate('Confirmation');
};
if (!permission) {
return (
<View>
<Text>Requesting camera permission</Text>
</View>
)
}
return (
<View style={styles.wrapper}>
<BarCodeScanner
style={StyleSheet.absoluteFill}
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
/>
</View>
)
};
const styles = StyleSheet.create({
wrapper: {
flex: 1
}
})
export default Scanner;
Can someone please help me?
BTW THE PRODUCT DATA FROM tHE API COMES SLOWeR THAN the APP MOVES TO THE CONFIRMATION SCREEN...
Problem 1: I think you need to reinitialize it on a focus even listener or something.
useEffect(() => {
permissionCamera();
}, []);
since useEffect() is basically a componentOnMount and it only fires the first time you load the page. When you navigate back this is not gonna fire. Please check if this is the case. You can do a simple log to confirm this.
For the 2nd problem, I can't help you much since there is only very little data. If you really need help, you could dm me on skype. I'll be glad to help you out.

Pass useAnimatedGestureHandler via forwardRef

I'm about to swap the old React Native Animated library with the new React Native Reanimated one to gain performance issues but I have encountered one problem I could not solve.
In all examples I found online, I saw that the GestureHandler, created with useAnimatedGestureHandler, is in the same component as the Animated.View. In reality that is sometimes not possible.
In my previous app, I just pass the GestureHandler object to the component via forwardRef but it seems React Native Reanimated is not able to do that. I don't know whether I have a syntax error or it is just a bug.
const App = () => {
const handlerRef = useAnimatedRef();
const y = useSharedValue(0);
handlerRef.current = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startY = y.value;
},
onActive: ({translationX, translationY}, ctx) => {
y.value = translationY;
},
onEnd: () => {},
});
const animatedStyles = useAnimatedStyle(() => ({transform: [{translateY: withSpring(y.value)}]}));
const UsingHandlerDirect = () => (
<PanGestureHandler onGestureEvent={handlerRef.current} >
<Animated.View style={[styles.blueBox, animatedStyles]} />
</PanGestureHandler>
)
const UsingHandlerForwardRef = forwardRef(({animatedStyles}, ref) => (
<PanGestureHandler onGestureEvent={ref?.handlerRef?.current}>
<Animated.View style={[styles.redBox, animatedStyles]} />
</PanGestureHandler>
));
return (
<SafeAreaView>
<View style={styles.container}>
<UsingHandlerForwardRef ref={handlerRef} animatedStyles={animatedStyles}/>
<UsingHandlerDirect />
</View>
</SafeAreaView>
);
}
I have saved the GestureHandler in a useAnimatedRef handlerRef.current = useAnimatedGestureHandler({}) to make things more representable. Then I pass the the ref directly into the PanGestureHandler of the UsingHandlerDirect component. The result is that when I drag the blue box the box will follow the handler. So this version works.
But as soon as I pass the handlerRef to the UsingHandlerForwardRef component non of the gesture events get fired. I would expect that when I drag the red box will also follow the handler but it doesn't
Has someone an idea whether it's me or it's a bug in the library?
Cheers
I have given up on the idea to pass a ref around instead, I created a hook that connects both components with each other via context.
I created a simple hook
import { useSharedValue } from 'react-native-reanimated';
const useAppState = () => {
const sharedXValue = useSharedValue(0);
return {
sharedXValue,
};
};
export default useAppState;
that holds the shared value using useSharedValue from reanimated 2
The child component uses this value in the gestureHandler like that
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = sharedXValue.value;
},
onActive: (event, ctx) => {
sharedXValue.value = ctx.startX + event.translationX;
},
onEnd: (_) => {
sharedXValue.value = withSpring(0);
},
});
and the Parent just consumes the hook value
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: -sharedXValue.value,
},
],
};
});
I have created a workable Snack which contains the 2 components - a Child with a blue box and a Parent with a red box

Check if screen is getting blurred or focus in React native?

i am using this
useEffect(() => {
const navFocusListener = navigation.addListener('didFocus', () => {
console.log('focus');
});
return () => {
navFocusListener.remove();
};
}, []);
I am using this code also tried other listeners. but there is no benefit, i am using react-native-immediate-call package for ussd dialing but as it doesn't have any callback. So i i call this function a dialer open for dialing for the USSD code. So now i want that when ussd dialing completes then comes back to screen and a api will call to get response. So how can i detect that USSD dialing is running running or completed so that i can make a request to the api.
For focus listener; you must change 'didFocus' to 'focus', If you are using react navigation v5+ and you should update like below:
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// do something
});
return unsubscribe;
}, []);
You can examine its documentation from here.
in react-navigation 5 you can do this to check screen is focus or blur,
try this in react navigation 5 using usefocuseffect-hook
useEffect(
() => navigation.addListener('focus', () => {}),
[navigation]
);
useEffect(
() => navigation.addListener('blur', () => {}),
[navigation]
);
Try this thanks
import { NavigationEvents } from "react-navigation";
callback=()=>{
alert('I m always working when you come this Screen')
}
in return (
<Your Code>
<NavigationEvents onWillFocus={() => callback()} />
<Your Code/>
)
Actually, you need to detect app state if it is in foreground or background or needs to add callback function into react-native-immediate-call by writing native code of android or ios package like this
import React, { useRef, useState, useEffect } from "react";
import { AppState, StyleSheet, Text, View } from "react-native";
const AppStateExample = () => {
const appState = useRef(AppState.currentState);
const [appStateVisible, setAppStateVisible] = useState(appState.current);
useEffect(() => {
AppState.addEventListener("change", _handleAppStateChange);
return () => {
AppState.removeEventListener("change", _handleAppStateChange);
};
}, []);
const _handleAppStateChange = (nextAppState) => {
if (
appState.current.match(/inactive|background/) &&
nextAppState === "active"
) {
console.log("App has come to the foreground!");
}
appState.current = nextAppState;
setAppStateVisible(appState.current);
console.log("AppState", appState.current);
};
return (
<View style={styles.container}>
<Text>Current state is: {appStateVisible}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
export default AppStateExample;

How to update the header while the component is still rendered using React Navigation?

I'm writing a React Native app and I'm using React Navigation (V2) with it. I want to update the navigationOptions and add a new button, after my component has updated. Here is the code with which I tried it:
static navigationOptions = ({ navigation }) => {
const options = {
headerTitle: SCREEN_TEXT_MENU_HEADER,
headerStyle: {
borderBottomWidth: 0,
marginBottom: -5
}
};
if (navigation.getParam("drawer", true)) {
options["headerLeft"] = (
<HeaderIconButton
onClick={() => {
navigation.openDrawer();
}}
icon={require("../../assets/icons/burgerMenu.png")}
/>
);
}
if (navigation.getParam("renderBillButton", false)) {
options["headerRight"] = (
<HeaderIconButton
onClick={() => {
navigation.navigate("BillScreen");
}}
type="primary"
icon={require("../../assets/icons/euro.png")}
/>
);
}
return options;
};
componentDidUpdate = prevProps => {
const { navigation, orders } = this.props;
if (prevProps.orders.length !== orders.length) {
navigation.setParams({
renderBillButton: orders.length > 0
});
}
};
The problem with this approach is, that the navigationOptions do not get reset after componentDidUpdate(). How can I dynamically adjust the header with React Navigation?
You can use this.props.navigation.setParams() function to update the navigation state params.
Reference: https://reactnavigation.org/docs/en/headers.html#updating-navigationoptions-with-setparams
Okay here is what went wrong: I also had to call the same code within componentDidMount(), otherwise it would not affect the page upon loading. So in addition to the code of my question I added:
componentDidMount = () => {
const { navigation, order } = this.props;
navigation.setParams({
renderBillButton: orders.length > 0
});
}