teamdigitale/italia-app

View on GitHub
ts/features/payments/onboarding/hooks/useWalletOnboardingWebView.tsx

Summary

Maintainability
A
35 mins
Test Coverage
import { openAuthenticationSession } from "@pagopa/io-react-native-login-utils";
import * as pot from "@pagopa/ts-commons/lib/pot";
import * as E from "fp-ts/lib/Either";
import * as TE from "fp-ts/lib/TaskEither";
import { pipe } from "fp-ts/lib/function";
import * as React from "react";
import URLParse from "url-parse";
import { useIODispatch, useIOSelector } from "../../../../store/hooks";
import { selectPaymentOnboardingRequestResult } from "../store/selectors";
import { paymentsStartOnboardingAction } from "../store/actions";
import {
  WalletOnboardingOutcome,
  WalletOnboardingOutcomeEnum
} from "../types/OnboardingOutcomeEnum";
import { ONBOARDING_CALLBACK_URL_SCHEMA } from "../utils";

type WalletOnboardingWebViewProps = {
  onOnboardingOutcome: (
    outcome: WalletOnboardingOutcome,
    walletId?: string
  ) => void;
};

export type WalletOnboardingWebView = {
  isLoading: boolean;
  isError: boolean;
  isPendingOnboarding: boolean;
  startOnboarding: (paymentMethodId: string) => void;
};

/**
 * This hook handles the onboarding webview flow and returns a function to start the onboarding
 * @param onOnboardingOutcome callback called when the onboarding flow is completed
 */
export const useWalletOnboardingWebView = ({
  onOnboardingOutcome
}: WalletOnboardingWebViewProps): WalletOnboardingWebView => {
  const dispatch = useIODispatch();

  const onboardingUrlPot = useIOSelector(selectPaymentOnboardingRequestResult);

  const [isPendingOnboarding, setIsPendingOnboarding] =
    React.useState<boolean>(false);
  const isLoading = pot.isLoading(onboardingUrlPot);
  const isError = pot.isError(onboardingUrlPot);

  const handleOnboardingResult = React.useCallback(
    (resultUrl: string) => {
      const url = new URLParse(resultUrl, true);

      const outcome = pipe(
        url.query.outcome,
        WalletOnboardingOutcome.decode,
        E.getOrElse(() => WalletOnboardingOutcomeEnum.GENERIC_ERROR)
      );

      onOnboardingOutcome(outcome, url.query.walletId);
    },
    [onOnboardingOutcome]
  );

  React.useEffect(() => {
    if (isPendingOnboarding) {
      return;
    }

    void pipe(
      onboardingUrlPot,
      pot.toOption,
      TE.fromOption(() => undefined),
      TE.chain(({ redirectUrl }) =>
        TE.tryCatch(
          () => {
            setIsPendingOnboarding(true);
            return openAuthenticationSession(
              redirectUrl,
              ONBOARDING_CALLBACK_URL_SCHEMA
            );
          },
          () => {
            onOnboardingOutcome(WalletOnboardingOutcomeEnum.CANCELED_BY_USER);
          }
        )
      ),
      TE.map(handleOnboardingResult)
    )();
  }, [
    isError,
    isLoading,
    isPendingOnboarding,
    onboardingUrlPot,
    handleOnboardingResult,
    onOnboardingOutcome,
    dispatch
  ]);

  React.useEffect(
    () => () => {
      setIsPendingOnboarding(false);
      dispatch(paymentsStartOnboardingAction.cancel());
    },
    [dispatch]
  );

  const startOnboarding = (paymentMethodId: string) => {
    setIsPendingOnboarding(false);
    dispatch(paymentsStartOnboardingAction.request({ paymentMethodId }));
  };

  return {
    startOnboarding,
    isLoading,
    isError,
    isPendingOnboarding
  };
};