import React, { useCallback, useEffect, useState } from "react"; import { AsyncStorage, useSendAnalytics } from "@gooddollar/web3sdk-v2"; import { isEmpty, noop } from "lodash"; import moment from "moment"; import { IContainerProps, Spinner, Text } from "native-base"; import { Wizard } from "react-use-wizard"; import { WizardHeader } from "../wizards"; import { OnboardScreenSimple, OnboardScreenProps } from "../screens/OnboardScreenSimple"; import { useFVModalAction, useGoodId } from "../../../hooks"; import { SegmentationController } from "./SegmentationController"; import { BasicStyledModal, ModalFooterCta } from "../../../core/web3/modals"; export interface OnboardControllerProps { account: string; withNavBar: boolean; name?: string | undefined; fvSig?: string; isDev?: boolean; isWallet?: boolean; onFV?: () => void; onSkip: () => void; onDone: (e?: any) => Promise; onExit: () => void; } const LocationPermissionNotice = () => ( {`Sharing your location \n (even just allowing one time) \n will allow for location-based offers and other benefits.`} ); export const OnboardController = ( props: Pick & OnboardControllerProps & IContainerProps ) => { const { onDone, onExit, onFV, onSkip, account, name, fvSig, withNavBar, isDev = false, isWallet = false } = props; const { certificates, certificateSubjects, expiryDate, expiryFormatted, isWhitelisted } = useGoodId(account); const [isPending, setPendingSignTx] = useState(false); const [doingSegmentation, setDoingSegmentation] = useState(false); const [shouldUpgrade, setShouldUpgrade] = useState(false); const [startOnboard, setStartOnboard] = useState(false); const [tosAccepted, setTosAccepted] = useState(undefined); const { track } = useSendAnalytics(); const expiryWithin1Month = useCallback(() => { const { expiryTimestamp } = expiryDate ?? {}; if (expiryTimestamp === undefined) return false; const expiry = moment(expiryTimestamp.toNumber()); const oneMonthFromNow = moment().clone().add(1, "months"); const shouldDoFV = isWhitelisted === false || expiry.isBefore(oneMonthFromNow); return shouldDoFV; }, [expiryDate, isWhitelisted]); useEffect(() => { if (tosAccepted === undefined) { void (async () => { const accepted = await AsyncStorage.getItem("tos-accepted"); await AsyncStorage.setItem("goodid_permission", "false"); setTosAccepted(accepted); })(); } // segmentation flow if (isEmpty(certificates) || expiryDate === undefined) return; const shouldFv = expiryWithin1Month(); if (shouldFv) { setShouldUpgrade(true); return; } const hasValidCertificates = certificates.some(cert => cert.certificate); if (hasValidCertificates && !doingSegmentation) { onSkip(); return; } else { // because of the reactivity of the certificates query, // we need to prevent onSkip being triggered during segmentation flow setDoingSegmentation(true); } }, [certificates, expiryDate, doingSegmentation]); const storeFvSig = async (fvSig: string) => { // the link will be requested to send a user to the fv-flow // we want to prevent a user to have to sign again when it redirects // so we store the fv-sig locally await AsyncStorage.setItem("fvSig", fvSig); }; const { verify } = useFVModalAction({ firstName: name ?? "", method: "redirect", chainId: 42220, onClose: noop, onFvSig: storeFvSig }); const doFV = async () => { // Should go to FaceVerificationIntro (wallet) || GoodID server (third parties) try { if (onFV) { onFV(); } else { setPendingSignTx(true); await verify(); } } catch (e) { setPendingSignTx(false); await AsyncStorage.removeItem("tos-accepted"); } finally { setPendingSignTx(false); } }; const onCompleted = useCallback( async (error?: Error | boolean) => { track("goodid_success"); void onDone(error); }, [onDone] ); const locationNotice = () => { setStartOnboard(true); }; const handleShouldFV = useCallback(async () => { setStartOnboard(false); try { track("goodid_start"); await AsyncStorage.setItem("tos-accepted", true); const { expiryTimestamp } = expiryDate ?? {}; // if someone is whitelisted we want to verify their timestamp // to determine if they should re-do the fv-flow if (isWhitelisted !== undefined && expiryTimestamp !== undefined) { const shouldDoFV = expiryWithin1Month(); // if the expiry date is within 1 month, we should re-do the fv-flow if (shouldDoFV) { void doFV(); } else { setTosAccepted(true); } } } catch (e: any) { track("goodid_error", { error: "handleShouldFv failed", message: e?.message, e }); console.error(e); throw new Error(e); } }, [doFV, isWhitelisted, expiryDate]); if (isEmpty(certificates) || isWhitelisted === undefined || tosAccepted === undefined) return ; if (isWhitelisted === true && tosAccepted && !shouldUpgrade) return ( ); return ( <> } footer={} type={"cta"} show={startOnboard} withOverlay={"dark"} onClose={handleShouldFV} withCloseButton /> }> ); };