/* This file is part of the Notesnook project (https://notesnook.com/) Copyright (C) 2023 Streetwriters (Private) Limited This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import React, { useEffect, useRef, useState } from "react"; import { View } from "react-native"; import { db } from "../../common/database/index"; import useTimer from "../../hooks/use-timer"; import { eSendEvent, presentSheet, ToastEvent } from "../../services/event-manager"; import { useThemeColors } from "@notesnook/theme"; import { eCloseSheet } from "../../utils/events"; import { SIZE } from "../../utils/size"; import { Button } from "../ui/button"; import { IconButton } from "../ui/icon-button"; import Input from "../ui/input"; import { PressableButton } from "../ui/pressable"; import Seperator from "../ui/seperator"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; import { useCallback } from "react"; const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => { const { colors } = useThemeColors(); const code = useRef(); const [currentMethod, setCurrentMethod] = useState({ method: mfaInfo?.primaryMethod, isPrimary: true }); const { seconds, start } = useTimer(currentMethod.method); const [loading, setLoading] = useState(false); const inputRef = useRef(); const [sending, setSending] = useState(false); const codeHelpText = { app: "Enter the 6 digit code from your authenticator app to continue logging in", sms: "Enter the 6 digit code sent to your phone number to continue logging in", email: "Enter the 6 digit code sent to your email to continue logging in", recoveryCode: "Enter the recovery code to continue logging in" }; const secondaryMethodsText = { app: "I don't have access to authenticator app", sms: "I don't have access to my phone", email: "I don't have access to email", recoveryCode: "I don't have recovery codes" }; const onNext = async () => { if (!code.current || code.current.length < 6) return; setLoading(true); inputRef.current?.blur(); await onMfaLogin( { method: currentMethod.method, code: code.current }, (result) => { if (result) { eSendEvent(eCloseSheet, "two_factor_verify"); } setLoading(false); } ); setLoading(false); }; const onRequestSecondaryMethod = () => { setCurrentMethod({ method: null, isPrimary: false }); }; const methods = [ { id: "sms", title: "Send code via SMS", icon: "message-plus-outline" }, { id: "email", title: "Send code via email", icon: "email-outline" }, { id: "app", title: "Enter code from authenticator app", icon: "cellphone-key" }, { id: "recoveryCode", title: "I have a recovery code", icon: "key" } ]; const getMethods = () => { return methods.filter( (m) => m.id === mfaInfo?.primaryMethod || m.id === mfaInfo?.secondaryMethod || m.id === "recoveryCode" ); }; useEffect(() => { if (currentMethod.method === "sms" || currentMethod.method === "email") { onSendCode(); } }, [currentMethod.method, onSendCode]); const onSendCode = useCallback(async () => { if (seconds || sending) return; // TODO setSending(true); try { await db.mfa.sendCode(currentMethod.method, mfaInfo.token); start(60); setSending(false); } catch (e) { setSending(false); ToastEvent.error(e, "Error sending 2FA Code", "local"); } }, [currentMethod.method, mfaInfo.token, seconds, sending, start]); return ( {currentMethod.method ? "Two factor authentication" : "Select methods for two-factor authentication"} {codeHelpText[currentMethod.method] || "Select how you would like to recieve the code"} {currentMethod.method === "sms" || currentMethod.method === "email" ? (