/* 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 { strings } from "@notesnook/intl"; import { useThemeColors } from "@notesnook/theme"; import React, { useCallback, useEffect, useRef, useState } from "react"; import { View } from "react-native"; import { ScrollView } from "react-native-actions-sheet"; import { db } from "../../common/database/index"; import useTimer from "../../hooks/use-timer"; import { eSendEvent, presentSheet, ToastManager } from "../../services/event-manager"; 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 { Pressable } from "../ui/pressable"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => { 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 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: strings.sendCodeSms(), icon: "message-plus-outline" }, { id: "email", title: strings.sendCodeEmail(), icon: "email-outline" }, { id: "app", title: strings.authAppCode(), icon: "cellphone-key" }, { id: "recoveryCode", title: strings.recoveryCode(), 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); ToastManager.error(e, "Error sending 2FA Code", "local"); } }, [currentMethod.method, mfaInfo.token, seconds, sending, start]); return ( {currentMethod.method ? strings["2fa"]() : strings.select2faMethod()} {strings["2faCodeHelpText"][currentMethod.method]?.() || strings.select2faCodeHelpText()} {currentMethod.method === "sms" || currentMethod.method === "email" ? (