Using React-native-notifications in React-navigation - react-native

I have an app with following structure:
AuthNavi: {
login1: login1,
login2: login2
}
TabNavi: {
tab1: TabNavi1
tab2: TabNavi2,
...
}
AppSwitchNavi: {
AuthLoading: LoadingScreen, (initial screen)
Auth: AuthNavi,
Content: TabNavi
}
The app started with AppSwitchNavi, if haven't login the app, it will go to AuthNavi, else go to TabNavi.
I would like to add request the notification permission after login, and handling of notification may also be added after login.
Does anyone know how to implement notification in at this situation, which only works in TabNavi?

This is the simple event-based implementation on components.
import { EventEmitter } from 'events';
const appEvents = new EventEmitter();
appEvents.setMaxListeners(0);
export default class AppEvents {
static removeEvent(subscribe) {
subscribe.remove();
}
static registerLoginEvent(method) {
return appEvents.addListener('Login-Required-Event', method);
}
static emitLoginEvent(data) {
appEvents.emit('Login-Required-Event', data);
}
static removeLoginEvent(method) {
appEvents.removeListener('Login-Required-Event', method);
}
}
Implemetation:
import React from 'react';
import AppEvents from './AppEvents';
import AppSwitchNavi from './Navigations';
export default class AppSwitchNaviWrapper extends React.Component {
componentDidMount() {
AppEvents.registerLoginEvent(this.loginEvent);
}
componentWillUnmount() {
AppEvents.removeLoginEvent(this.loginEvent);
}
loginEvent = () => {
// do something
};
render() {
return <AppSwitchNavi />;
}
}
Emit event from some situation as AppEvents.emitLoginEvent(UserData);
But IMO you should handle redux for such situations, Redux will help you out in long run. The event-based implementation makes problem to you & you have to very careful about the implementation and removing the event to prevent memory leaks and unexpected behavior of the app.

Related

How to create an Authentication middleware for a Flutter app?

This is my home.dart code and I want to write an Authentication Middleware for my app. At the moment my main.dart code looks like this:
void main() {
Get.put(MenuController());
Get.put(NavigationController());
Get.put(AuthController());
Get.put(AuthCard);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Obx(() => GetMaterialApp(
initialRoute: AuthController.instance.isAuth
? homeScreenRoute
: authenticationScreenRoute,
unknownRoute: GetPage(
name: '/not-found',
page: () => PageNotFound(),
transition: Transition.fadeIn),
getPages: [
GetPage(
name: rootRoute,
page: () {
return SiteLayout();
}),
GetPage(
name: authenticationScreenRoute,
page: () => const AuthenticationScreen()),
GetPage(name: homeScreenRoute, page: () => const HomeScreen()),
],
debugShowCheckedModeBanner: false,
title: 'BasicCode',
theme: ThemeData(
scaffoldBackgroundColor: light,
textTheme: GoogleFonts.mulishTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.black),
pageTransitionsTheme: const PageTransitionsTheme(builders: {
TargetPlatform.iOS: FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(),
}),
primarySwatch: Colors.blue,
),
));
}
}
And the isAuth variable that I checking at the initialRoute part of the code comes from the following line of codes, inside the auth_controller file that extends GetXController:
final _isAuth = false.obs;
bool get isAuth {
_isAuth.value= token != null;
return _isAuth.value;
}
String? get token {
if (_expiryDate != null &&
_expiryDate!.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
Everything seems good but the application sticks at the authentication page and won't move to home screen after the isAuth's value changed to true!
I searched for that and found another way by creating an authentication middleware. So I added the following code bellow the above code inside the main.dart file:
class AuthMiddlware extends GetMiddleware {
#override
RouteSettings? redirect(String route) => !AuthController.instance.isAuth
? const RouteSettings(name: authenticationScreenRoute)
: null;
}
But I get a red line under the redirect word with no error decription and don't know how to complete the middleware and make it work?
Example of how to implement an AuthGuard with FirebaseAuth and Getx.
(If not using FirebaseAuth, swap it to your preferred authentication provider in AuthGuardMiddleware.)
middleware.dart
import 'auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class AuthGuardMiddleware extends GetMiddleware {
var authService = Get.find<AuthService>();
#override
RouteSettings? redirect(String? route) {
return authService.isLoggedIn() ? null : RouteSettings(name: '/login');
}
}
auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:get/get.dart';
class AuthService extends GetxService {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
bool isLoggedIn() {
return _firebaseAuth.currentUser != null;
}
// IMPLEMENT additional FirebaseAuth methods here.
}
main.dart
import 'package:get/get.dart';
import 'middleware.dart';
...
GetPage(
name: '/protected',
page: () => Protected()),
middlewares: [
AuthGuardMiddleware(),
]),
...
Copy paste :)
class AuthMiddlware extends GetMiddleware {
#override
RouteSettings? redirect(String? route) => !AuthController.instance.isAuth
? const RouteSettings(name: authenticationScreenRoute)
: null;
}

React Native - Adding logic to Stack Navigation

I have the following Stack Navigation:
const WelcomeStack = createStackNavigator({
UpdateProfileScreen: {
screen: UpdateProfileScreen,
},
SelectProfileScreen: {
screen: SelectProfileScreen,
},
PresentationModal: {
screen: PresentationModal,
}
}, {
initialRouteName: 'UpdateProfileScreen',
headerMode: 'none'
})
When a user is new, I show "UpdateProfileScreen" first, then I move to "SelectProfileSecreen" and then "PresentationModal".
If for some reason after "UpdateProfileScreen" user closes the app, next time they log in I will show "SelectProfileSecreen" and "PresentationModal". If they complete data, next time, they will only see the "PresentationModal"
Since I have set "UpdateProfileScreen" as initialRoute, it will always load first, even if it does not show.
So I was wondering if programatically I could change the initialRoute and/or if I do:
this.props.navigation.navigate("WelcomeStack")
I can add some logic in the same stack to handle the page to show?
I think your best option is using SwitchNavigator(RouteConfigs, SwitchNavigatorConfig) you can simply create it with createSwitchNavigator as it controls all the switching of navigators used mainly for authentication flow in most react native apps.
With that said, for your case i think its the most suitable way to achieve the desired behavior.
Please be careful if publishing an app using something along the lines of :
this.props.navigation.navigate("WelcomeStack")
as it can be a serious security vulnerability
docs: https://reactnavigation.org/docs/1.x/switch-navigator/
snack example: https://snack.expo.io/#react-navigation/auth-flow
I had a similar problem here how i resolved it:
Added SwitchNavigator like so:
let Navigation = createSwitchNavigator(
{
AuthLoading: AuthLoading,
LoginNavigator: LoginNavigator,
HomeNavTab: tabNavigator,
LoggedInChose: LoggedInChose
},
{
initialRouteName: "AuthLoading"
}
);
The AuthLoading stack is the first to load and decides where the app go next:
import React from "react";
import { connect } from "react-redux";
class AuthLoading extends React.Component {
constructor(props) {
super(props);
this.checkUserSession();
}
checkUserSession = () => {
const userData = this.props.userData;
if (userData) {
const residencesNumber = userData.nbreResidences;
if (residencesNumber == 1) {
this.props.navigation.navigate("HomeNavTab");
} else {
this.props.navigation.navigate("LoggedInChose");
}
} else {
this.props.navigation.navigate("LoginNavigator");
}
};
// Render any loading content that you like here
render() {
return (
null
);
}
}
const mapStateToProps = state => {
return {
userData: state.userData
};
};
export default connect(mapStateToProps)(AuthLoading);
Maybe you get an idea from this.

React Native Navigation 5.x NavigationInjectedProps

I am currently switching to navigation 5.x from 4.x. I'm using
import { NavigationInjectedProps } from 'react-navigation'
across the entire application, but I cannot find anything equivalent in navigation 5. I'm using typescript.
Could anyone lead me in the right direction?
First of all, the logic behind obtaining params is changed. In v5, you have to access them from a route, which is passed as an additional property for class components, unlike using navigation in the previous versions.
To type your params in the right way, you have to type the route. For previously written code:
import { NavigationInjectedProps } from 'react-navigation'
type Params = {
param1: string
}
type Props = NavigationInjectedProps<Params>
class MyComponent extends Component<Props> {
render() {
const { param1 } = this.props.navigation.state.params
}
}
the rough equivalent will be:
import { StackScreenProps } from '#react-navigation/stack'
type RootParamsList = {
MyComponent: {
param1: string
}
}
type Props = StackScreenProps<RootParamsList, 'MyComponent'>
class MyComponent extends Component<Props> {
render() {
const { param1 } = this.props.route.params.param1
return null
}
}

dispatching 'userLogoutAction' when JWT has expired throws an error in 'Menu.js'

In order to do an automatic log-off (triggered by the react app, no user interaction and no API calls) from my react-admin app (react-admin version 2.8.3) as soon as the JWT expires. I do a periodic check and dispatch the userLogout action which I imported from ra-core/authActions the same way the Logout button (ra-ui-materialui/auth/Logout) does.
Unfortunately ra-ui-materialui/esm/layout/Menu.js throws an error at line 116 complaining about an undefined property: TypeError: Cannot read property 'pathname' of null
return {
114 | open: state.admin.ui.sidebarOpen,
115 | resources: getResources(state),
> 116 | pathname: state.router.location.pathname
117 | };
Using the Chrome redux tools I can see that the redux-store router has a property location that is null.
Is there a better way to log out the user after the JWT has expired? If not, what do I need to do to make the userLogout action work?
Maybe someone can explain why the Menu.js seems to be tightly coupled to the userLogout action.
The source code of my component looks like this:
import React from 'react'
import { connect } from 'react-redux'
import { userLogout } from 'react-admin'
import { hasValidJWT } from '../authProvider'
class PeriodicAuthCheck extends React.Component {
constructor(props) {
super(props)
// interval in seconds
this.interval = 60
}
authCheck(loginPath, dispatchAction) {
if (!hasValidJWT()) {
if (window.location.hash !== `#${loginPath}`) {
console.log(`JWT has expired, redirecting to ${loginPath}`)
/*
this is my workaround:
window.location.replace(`/#${loginPath}`)
*/
dispatchAction(loginPath)
}
}
}
componentDidMount() {
this.authCheck(this.props.loginPath, this.props.logout)
this.interval = setInterval(this.authCheck, this.interval * 1000, this.props.loginPath, this.props.logout)
}
componentWillUnmount() {
if (this.interval) {
clearInterval(this.interval)
this.interval = undefined
}
}
render() {
return null
}
}
const mapDispatchToProps = dispatch => {
return {
logout: (loginPath) => dispatch(userLogout(loginPath))
}
}
export default connect(null, mapDispatchToProps)(PeriodicAuthCheck)
Thank you in advance!
Thomas
You can update the httpClient for your dataProvider.
For me it worked only if I removed the token explicitly and forwarded the browser to the login form.
if (401 === responseStatusCode) {
localStorage.removeItem('token');
window.location = '/#/login';
}
It isn't the nicest solution imo but it get's the job done.
Would love to see other solutions to this problem as well.

How to get the device token in react native

I am using react-native 0.49.3 version, My Question is how to get the device token in react native for both IOS and Android I tried with this link but it not working for me, right now I tried in IOS. how to resolve it can one tell me how to configure?
I tried different solutions and I've decided to use React Native Firebase.
Here you will find everything about Notifications.
Also, you can use the others libraries that come with Firebase, like Analytics and Crash Reporting
After set up the library you can do something like:
// utils/firebase.js
import RNFirebase from 'react-native-firebase';
const configurationOptions = {
debug: true,
promptOnMissingPlayServices: true
}
const firebase = RNFirebase.initializeApp(configurationOptions)
export default firebase
// App.js
import React, { Component } from 'react';
import { Platform, View, AsyncStorage } from 'react-native';
// I am using Device info
import DeviceInfo from 'react-native-device-info';
import firebase from './utils/firebase';
class App extends Component {
componentDidMount = () => {
var language = DeviceInfo.getDeviceLocale();
firebase.messaging().getToken().then((token) => {
this._onChangeToken(token, language)
});
firebase.messaging().onTokenRefresh((token) => {
this._onChangeToken(token, language)
});
}
_onChangeToken = (token, language) => {
var data = {
'device_token': token,
'device_type': Platform.OS,
'device_language': language
};
this._loadDeviceInfo(data).done();
}
_loadDeviceInfo = async (deviceData) => {
// load the data in 'local storage'.
// this value will be used by login and register components.
var value = JSON.stringify(deviceData);
try {
await AsyncStorage.setItem(config.DEVICE_STORAGE_KEY, value);
} catch (error) {
console.log(error);
}
};
render() {
...
}
}
Then you can call the server with the token and all the info that you need.