Untitled

mail@pastecode.io avatar
unknown
json
2 months ago
17 kB
3
Indexable
Never
import React, {
  useCallback,
  useEffect,
  useLayoutEffect,
  useMemo,
  useState,
} from 'react';
import {
  ActivityIndicator,
  Image,
  ImageBackground,
  SafeAreaView,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

import {useHeaderHeight} from '@react-navigation/stack';
import {useAuth} from '@squantumengine/react-native-sqeid';
import axios from 'axios';
import Config from 'react-native-config';

import PhoneImage from '@assets/illustration/phone.webp';
import SinarmasSSOImage from '@assets/login/sinarmas-sso.webp';
import {SvgIcon} from '@assets/svg';
import {ToggleSwitch} from '@components/Atom';
import Button from '@components/Atom/Button';
import DevModeButton from '@components/Atom/DevModeButton';
import If from '@components/Atom/If';
import KeyboardAdjustedView from '@components/Atom/KeyboardAjustedView';
import TextField from '@components/Atom/TextField';
import {NanoModal} from '@components/Organism';
import HelpModal from '@components/Organism/HelpModal';
import RemoteAccessCheck from '@components/Organism/RemoteAccessCheck';
import {colors as staticColors} from '@design-system';
import {useLocale} from '@hooks/global/useLocale';
import useModal from '@hooks/global/useModal';
import useRemoteAccessApps from '@hooks/global/useRemoteAccessApps';
import {useTheme} from '@hooks/global/useTheme';
import useUserInfo from '@hooks/global/useUserInfo';
import useAnimation from '@hooks/local/useAnimation';
import useFirebaseRemoteState from '@hooks/local/useFirebaseRemoteState';
import useHelpCenter from '@hooks/local/useHelpCenter';
import useLogin from '@hooks/local/useLogin';
import useSegmentAnalytics from '@hooks/local/useSegmentAnalytics';
import {Translation} from '@hooks/local/useSegmentAnalytics/AnalyticPropertyEnum';
import {LoginScreenTestId} from '@models/ScreenTestId';
import {AuthStackProps} from '@navigator/AuthParamList';
import {removeAllSpace} from '@utils/formatUtils';
import {resolveImageSource} from '@utils/imageUtils';
import {isIphoneX} from '@utils/iPhoneXHelpers';
import {formatPhoneNumber, parsePhoneNumber} from '@utils/phoneNumHelper';
import {
  isValidPhoneNumber,
  isValidStartPhoneNumber,
} from '@utils/validators/inputValidator';

import getStyles from './style';

const LoginScreen = ({navigation}: AuthStackProps<'Login'>) => {
  const {
    login: loginSSOSinarmas,
    credentialsCallback,
    saveCredentials,
    renewCredentialsAfterPhoneNumberChanged,
  } = useAuth();

  const [isValid, setIsValid] = useState<boolean>(false);
  const [isInputError, setInputError] = useState<boolean>(false);
  const [phoneNumber, setPhoneNumber] = useState<string | undefined>(undefined);
  const countryCode = '62';
  const {translate: t, changeLocale, locale} = useLocale();
  const [, hideModal] = useModal(
    _ => undefined,
    action => action.hideModal,
  );
  const [, showSelectorModal] = useModal(
    _ => undefined,
    action => action.showSelectorModal,
  );
  const [, useUserInfoAPI] = useUserInfo(
    _ => undefined,
    action => action.useUserInfoAPI,
  );
  const {removeLocalData, updateLanguage, isLoadingPutUserInfo} =
    useUserInfoAPI();
  const {hapticEffect} = useAnimation();
  const {trackEvent, trackScreen} = useSegmentAnalytics('VerifyPhoneNumber');
  const [modalVisible, setModalVisible] = useState(false);
  const [isShowModalSSOSinarmas, setIsShowModalSSOSinarmas] = useState(false);
  const {
    checkAvailability,
    isCheckingPhoneAvailability,
    postSQEToken,
    isSSOSinarmasActivated,
  } = useLogin();

  const {showHelpCenter} = useHelpCenter();
  const {firebaseAppConfig, getFirebaseAppConfig} =
    useFirebaseRemoteState.AppConfig();
  const {
    colors: colorTheme,
    images: imagesTheme,
    theme,
    changeTheme,
  } = useTheme();
  const styles = useMemo(() => getStyles(colorTheme), [colorTheme]);
  // Language
  const languagesData = useMemo(() => {
    return ['English', 'Bahasa Indonesia'];
  }, []);

  const handleTextChanged = useCallback(
    (text: string) => {
      hapticEffect();
      text = text.replace(/\s|\D/g, '');
      if (removeAllSpace(text) === Config.STATIC_PHONE_NUMBER) {
        setIsValid(true);
        setInputError(false);
      } else {
        setIsValid(isValidPhoneNumber(parsePhoneNumber(text)));
        setInputError(!isValidStartPhoneNumber(parsePhoneNumber(text)));
      }
      if (text !== null) {
        setPhoneNumber(formatPhoneNumber(text));
        if (text.length >= 8) {
          trackEvent('VerifyPhoneNumberCheck');
        }
      }
    },
    [hapticEffect, trackEvent],
  );

  const handleCallbackResult = credentials => {
    if (credentials) {
      postSQEToken({requestBody: {sqeToken: credentials.accessToken}});
      saveCredentials(credentials);
    }
  };

  const handleSubmitNumber = useCallback(async () => {
    removeLocalData();
    if (phoneNumber) {
      // Overriding the baseURL if app detects below number are being used
      // Basically we forced the app to points to UAT env for this number
      // This number are used ONLY for Apple & Playstore QAs
      // For them to test main functionality of the App
      if (removeAllSpace(phoneNumber) === Config.STATIC_PHONE_NUMBER) {
        axios.defaults.baseURL = Config.STATIC_URL_BASE_PATH;
      } else {
        axios.defaults.baseURL = Config.BFF_WALLET_BASE_PATH;
      }
      const parsedNo = parsePhoneNumber(phoneNumber);
      checkAvailability({countryCode, phoneNumber: parsedNo});
      trackEvent('VerifyPhoneNumberNextTapped');
    }
  }, [phoneNumber, removeLocalData, checkAvailability, trackEvent]);

  const onSelectLanguage = useCallback(
    (value: string, index: number) => {
      const selectedLanguage = value === 'English' ? 'en' : 'id';
      trackEvent('VerifyPhoneNumberChooseLanguageTapped', {
        language_selected:
          selectedLanguage === 'en' ? Translation.EN : Translation.ID,
      });
      changeLocale(selectedLanguage);
      hideModal();
    },
    [changeLocale, hideModal, trackEvent],
  );

  const closeModalLanguage = useCallback(() => {
    trackEvent('VerifyPhoneNumberCloseLanguage');
    hideModal();
  }, [hideModal, trackEvent]);

  const handlePressLanguage = useCallback(() => {
    trackEvent('VerifyPhoneNumberChangeLanguageTapped');
    showSelectorModal({
      visible: true,
      data: languagesData,
      onSelectItem: onSelectLanguage,
      title: t('languages'),
      onClose: closeModalLanguage,
      onDismiss: closeModalLanguage,
      testID: LoginScreenTestId.LOGIN_LANGUAGE_SWITCH,
    });
  }, [
    closeModalLanguage,
    languagesData,
    onSelectLanguage,
    showSelectorModal,
    t,
    trackEvent,
  ]);

  const onChangeTheme = useCallback(() => {
    changeTheme(theme === 'DARK' ? 'LIGHT' : 'DARK');
  }, [changeTheme, theme]);

  const [remoteAppDeatils] = useRemoteAccessApps(
    state => state.remoteAppDeatils,
  );

  const onLanguageToggle = useCallback(
    (newMode: boolean) => {
      if (newMode) {
        setTimeout(() => {
          changeLocale('id');
        }, 150);
      } else {
        setTimeout(() => {
          changeLocale('en');
        }, 100);
      }
    },
    [changeLocale, updateLanguage],
  );

  const customSwitchLanguage = useMemo(() => {
    if (isLoadingPutUserInfo) {
      return <ActivityIndicator color={staticColors.primary.white} />;
    }

    return locale === 'en' ? (
      <Text style={styles.textLanguangeToggle}>EN</Text>
    ) : (
      <Text style={styles.textLanguangeToggle}>ID</Text>
    );
  }, [locale, styles.textLanguangeToggle, isLoadingPutUserInfo]);

  const TextInActiveComponent = useMemo(() => {
    return (
      <Text
        style={[
          styles.textInActive,
          locale === 'en' ? styles.isEnglish : styles.isIndonesia,
        ]}>
        {locale === 'en' ? 'ID' : 'EN'}
      </Text>
    );
  }, [locale, styles.isEnglish, styles.isIndonesia, styles.textInActive]);

  useLayoutEffect(() => {
    navigation.setOptions({
      headerTransparent: true,
      headerRight: () => (
        <If condition={!remoteAppDeatils?.isRemoteAccessAppsInstalled}>
          <ToggleSwitch
            switchStatus={locale === 'id'}
            onChange={onLanguageToggle}
            switchInactiveColor={colorTheme.shade.grey2}
            switchActiveColor={colorTheme.shade.grey2}
            customInputRangeAnimation={[0, 1.2]}
            customSwitchComponent={customSwitchLanguage}
            switchIndicatorStyle={styles.languageToggle}
            switchContainerStyle={styles.containerToggleStyle}
            customTextInactiveComponent={TextInActiveComponent}
          />
        </If>
      ),
      headerLeft: () => (
        <If
          condition={
            firebaseAppConfig.isDarkmodeToggleEnabled &&
            !remoteAppDeatils?.isRemoteAccessAppsInstalled
          }>
          <TouchableOpacity
            onPress={onChangeTheme}
            activeOpacity={0.7}
            style={styles.notifWrapper}
            testID={LoginScreenTestId.LOGIN_THEME_SWITCH + 'pressable'}
            accessibilityLabel={
              LoginScreenTestId.LOGIN_THEME_SWITCH + 'pressable'
            }
            accessible>
            <Image
              source={resolveImageSource(imagesTheme.homepage.toggle)}
              style={styles.notiIcon}
              testID={LoginScreenTestId.LOGIN_THEME_SWITCH + 'image'}
              accessibilityLabel={
                LoginScreenTestId.LOGIN_THEME_SWITCH + 'image'
              }
              accessible
            />
          </TouchableOpacity>
        </If>
      ),
    });
  }, [
    firebaseAppConfig,
    handlePressLanguage,
    imagesTheme.homepage.toggle,
    locale,
    navigation,
    onChangeTheme,
    remoteAppDeatils?.isRemoteAccessAppsInstalled,
    styles,
  ]);

  useEffect(() => {
    getFirebaseAppConfig();
    trackScreen();
  }, [trackScreen, getFirebaseAppConfig]);

  const _onOpenHelp = useCallback(() => {
    setModalVisible(true);
    trackEvent('VerifyPhoneNumberQuestionMarkTapped');
  }, [trackEvent]);

  const _closeRbSheet = useCallback(() => {
    setModalVisible(false);
  }, []);

  const _bsContent: () =>
    | {title: string; contents: {label: string; uri: string}[]}
    | undefined = useCallback(() => {
    return {
      title: t('button.need_help'),
      contents: [
        {
          label: t('help_list_number_inactive'),
          uri: '/requests/new?ticket_form_id=360000205478&tf_360011144817=This+is+another+value+for+the+text+field+from+the+config&tf_360025038718=dropdown_value_2',
        },
        {
          label: t('help_list_number_unused'),
          uri: '/requests/new?ticket_form_id=360000205478&tf_360011144817=This+is+another+value+for+the+text+field+from+the+config&tf_360025038718=dropdown_value_2',
        },
      ],
    };
  }, [t]);

  const LeftButtonTextInput = useCallback(
    () => (
      <TouchableOpacity
        style={styles.iconHelp}
        activeOpacity={0.75}
        onPress={_onOpenHelp}
        testID={LoginScreenTestId.LOGIN_PHONE_HELP + '-pressable'}
        accessibilityLabel={LoginScreenTestId.LOGIN_PHONE_HELP + '-pressable'}
        accessible>
        <SvgIcon.Question />
      </TouchableOpacity>
    ),
    [_onOpenHelp, styles.iconHelp],
  );

  const _onPress = useCallback(
    item => {
      showHelpCenter({
        uri: item.uri,
        beforeOpenHC: _closeRbSheet,
      });
    },
    [_closeRbSheet, showHelpCenter],
  );

  const _renderItemBsContent = useCallback(
    (item: {label: string; uri: string}, index: number = 0) => {
      return (
        <TouchableOpacity
          onPress={() => _onPress(item)}
          key={`${index}`}
          style={styles.contentContainerBs}
          testID={LoginScreenTestId.LOGIN_PHONE_HELP + '-itemList-' + index}
          accessibilityLabel={
            LoginScreenTestId.LOGIN_PHONE_HELP + '-itemList-' + index
          }
          accessible>
          <Text
            style={styles.contentTextBs}
            testID={LoginScreenTestId.LOGIN_PHONE_HELP + '-text-' + index}
            accessibilityLabel={
              LoginScreenTestId.LOGIN_PHONE_HELP + '-text-' + index
            }
            accessible>
            {item.label}
          </Text>
        </TouchableOpacity>
      );
    },
    [_onPress, styles.contentContainerBs, styles.contentTextBs],
  );

  const marginTop = useHeaderHeight();

  if (remoteAppDeatils?.isRemoteAccessAppsInstalled) {
    return <RemoteAccessCheck remoteApps={remoteAppDeatils?.installedApps} />;
  }

  const hideSSOModal = () => {
    setIsShowModalSSOSinarmas(false);
  };

  credentialsCallback(handleCallbackResult);

  const doLoginSSOSinarmas = () => {
    hideSSOModal();
    setTimeout(() => {
      loginSSOSinarmas();
    }, 250);
  };

  return (
    <ImageBackground
      source={imagesTheme.bgAccount.background}
      style={styles.background}>
      <SafeAreaView style={styles.container}>
        <KeyboardAdjustedView
          keyboardMargin={isIphoneX() ? 0 : 24}
          style={[styles.container, styles.keyboardView]}>
          <DevModeButton phoneNumber={phoneNumber?.replace(/\s/g, '') ?? ''}>
            <Image
              style={[styles.headerIcon, {marginTop}]}
              source={PhoneImage}
            />
          </DevModeButton>
          <View style={styles.textContainer}>
            <Text
              style={styles.contentTitle}
              testID={LoginScreenTestId.LOGIN_GREETING + '-text'}
              accessibilityLabel={LoginScreenTestId.LOGIN_GREETING + '-text'}
              accessible>
              {t('auth.login.greeting')}
            </Text>
            <Text
              style={styles.contentDesc}
              testID={LoginScreenTestId.LOGIN_ENTER_PHONE_NUMBER + '-text'}
              accessibilityLabel={
                LoginScreenTestId.LOGIN_ENTER_PHONE_NUMBER + '-text'
              }
              accessible>
              {t('auth.login.enter_phone_number')}
            </Text>
          </View>
          <TextField
            startText={`+${countryCode}`}
            placeholder={'821 001 00'}
            onChangeText={handleTextChanged}
            value={phoneNumber}
            maxLength={15}
            editable={!isCheckingPhoneAvailability}
            status={isInputError ? 'error' : 'normal'}
            errorText={t('auth.login.error_start_number')}
            leftButton={LeftButtonTextInput}
            testID={LoginScreenTestId.LOGIN_PHONE_INPUT_FIELD}
            accessibilityLabel={LoginScreenTestId.LOGIN_PHONE_INPUT_FIELD}
          />
          <View style={styles.buttonContainer}>
            <Button
              type="primary"
              disabled={!isValid}
              loading={isCheckingPhoneAvailability}
              text={t('button.next')}
              onPress={handleSubmitNumber}
              testID={LoginScreenTestId.LOGIN_GO_TO_NEXT}
              textStyle={styles.actionText}
            />
          </View>
          <If condition={isSSOSinarmasActivated}>
            <Text
              style={styles.otherOptionDesc}
              onPress={() => setIsShowModalSSOSinarmas(true)}>
              {t('auth.login.other_method')}
            </Text>
          </If>
        </KeyboardAdjustedView>
        <HelpModal
          visible={modalVisible}
          onClose={_closeRbSheet}
          testID={LoginScreenTestId.LOGIN_PHONE_HELP}
          title={_bsContent()?.title ?? ''}>
          {_bsContent()?.contents.map(_renderItemBsContent)}
        </HelpModal>
        <NanoModal
          onBackdropPress={hideSSOModal}
          onBackPress={hideSSOModal}
          visible={isShowModalSSOSinarmas}>
          <View style={styles.containerTempLockModal}>
            <Text style={styles.titleModalLock}>
              {t('auth.login.other_method_modal_title')}
            </Text>
            <View style={styles.containerButtonSSO}>
              <Button
                onPress={() =>
                  renewCredentialsAfterPhoneNumberChanged('+6285129999', {})
                }
                text={t('auth.login.sso_sinarmas')}
                type={'custom'}
                iconLeft={() => <Image source={SinarmasSSOImage} />}
                textColor={staticColors.primary.black}
                textStyle={styles.buttonTextSSO}
                containerStyle={styles.containerButtonSSO}
              />
            </View>
          </View>
        </NanoModal>
      </SafeAreaView>
    </ImageBackground>
  );
};

export default LoginScreen;
Leave a Comment