wix/react-native-navigation

Login screen to TabBasedApp and then log out back to Login screen

Closed this issue · 7 comments

Issue Description

I haven't found a proper solution for displaying a login screen before a TabBasedApp. I haven't found a way to switch between a single screen app and a tab based one, which would be the most ideal way so I could have the login/signup process as a "single screen app" and after logging in, switch to a tab based app. I've tried having a modal show before the tabs show, but have had no luck. I've also tried having a conditional for if the user is logged in, show tabs & if not, then show single app login. However, there's no way to log in or out, which would require switching between the two app types. Maybe I'm missing something, but if someone can give a concrete example, that would be great. I've only found open ended answers in past issue #'s or answers that don't solve logging in and out.

Steps to Reproduce / Code Snippets / Screenshots

I've tried:

if (!isLoggedIn) {
    Navigation.showModal({
        screen: "example.Login",
    });
} else {
    Navigation.startTabBasedApp({
        tabs: []
    })
}

Something like this was suggested in #1442, but if someone is not logged in and brought to the example.Login, there is no way to switch to the tabs after logging in and after logging out, no way to go back to the example.Login.


Environment

  • React Native Navigation version: 1.1.300
  • React Native version: 0.49.2
  • Platform(s) (iOS, Android, or both?): Both
  • Device info (Simulator/Device? OS version? Debug/Release?): iPhone 8 iOS 11.1 Simulator

Why don't you do the following?

// For Authentication
export function startLogin(){
  Navigation.startSingleScreenApp(...);
}

// Main app
export function startMainApp(){
  Navigation.startTabBasedApp(...);
}

// call from index.js
function init(){
  const isUserExists = /** magic */;

  if(!isUserExists){
    startLogin();
  } else {
    startMainApp();
  }
}

// Handle login logic
function login(){
  // bla bla bla
  if(everything_is_good){
    startMainApp();
  }
}

// handle logout logic
function logout(){
  // remove user data
  startLogin();
}

This is a basic example inspired from my setup (keeps in sync with redux store):

class App {

  appRoot = 'loading';
  store = {};

  constructor() {
    init()
      .then(this.attachToStore)
      .then(this.startApp)
      .catch(err => console.log('App init error', err.message))
  }

  onStoreUpdate = () => {
    const { sessionToken } = this.store.getState().login;
    // this can be complex business logic depending on requirements
    const newAppRoot = !!sessionToken ? 'app' : 'login';
    if(newAppRoot !== this.appRoot) {
      this.appRoot = newAppRoot;
      this.startApp();
    }
  }

  attachToStore = store => {
    const { sessionToken } = store.getState().login;
    // keep store ref, but it could also be passed to onStoreUpdate somehow?
    this.store = store;
    // this can be complex business logic depending on requirements
    this.appRoot = !!sessionToken ? 'app' : 'login';
    store.subscribe(this.onStoreUpdate);
  }

  startApp = () => {
    switch(this.appRoot) {
      case 'app': {
        Navigation.startTabBasedApp({
          tabs: [
            {
              label: 'Feed',
              screen: 'myApp.Feed',
              title: 'Feed',
              icon: appIcons['trending-up'],
              navigatorStyle,
            },
            {
              label: 'Profile',
              screen: 'myApp.Profile',
              title: 'Profile',
              icon: appIcons['face'],
              navigatorStyle
            },
            {
              label: 'More',
              screen: 'myApp.Settings',
              title: 'More',
              icon: appIcons['menu'],
              navigatorStyle
            }
          ],
          animationType: 'slide-down',
          appStyle: {
            forceTitlesDisplay: true,
            tabBarButtonColor: color.divider,
            tabBarSelectedButtonColor: color.primary
          }
        });
        break;
      }

      case 'login': {
        Navigation.startSingleScreenApp({
          screen: {
            screen: 'myApp.Login',
            title: 'Login',
            navigatorStyle: {
              navBarHidden: true
            }
          }
        });
        break;
      }

      default: {
        console.error('Aww, snap! App init went wrong :(');
      }
    }
  }
}

export default App;

Been a week since last response from OP. I'll assume the problem resolved.

@simonedavico how do you get your store in app.js? I thought it has to be registered in Navigation.registerComponent()

@witalobenicio I have it as the resolved value of the promise I return from the init function:

export const init = async (): Promise<AppStore> => {
  const provider: AppProvider = (Provider: any);
  const store: AppStore = configureStore();

  enableAndroidAnimations();

  const allResolved: [
    AppStore,
    boolean,
    { [string]: string }
  ] = await Promise.all([store, registerScreens(store, provider), loadIcons()]);

  return allResolved[0];
};

init calls configureStore:

export const configureStore = (
  initialState: AppState = appInitialState
): AppStore => {
  const enhancer: StoreEnhancer<*, *> = composeEnhancers(
    applyMiddleware(...middleware),
    ...enhancers,
    devToolsWorkaroundEnhancer
  );

  return createStore(rootReducer, initialState, enhancer);
};

how to add skip button in login

how to add skip button in login

@abhishekp8003 I think what you've got to remember is that the actual login functionality is outside of the scope of this library, and so how you achieve this is really up to you.

If you're using a setup similar to the solutions posted above, then you can just call your function that starts the tab based app when pressing the "skip" button on your login screen, assuming that's what you're asking.