Files
notesnook/apps/mobile/app/components/auth/use-login.ts

187 lines
5.4 KiB
TypeScript
Raw Normal View History

2023-01-06 12:15:37 +05:00
/*
This file is part of the Notesnook project (https://notesnook.com/)
2023-01-16 13:44:52 +05:00
Copyright (C) 2023 Streetwriters (Private) Limited
2023-01-06 12:15:37 +05:00
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 <http://www.gnu.org/licenses/>.
*/
2025-09-29 10:01:59 +05:00
import { strings } from "@notesnook/intl";
2023-01-06 12:15:37 +05:00
import { useRef, useState } from "react";
2025-07-01 12:13:03 +05:00
import { TextInput } from "react-native";
2023-01-06 12:15:37 +05:00
import { db } from "../../common/database";
2025-07-01 12:13:03 +05:00
import { ToastManager, eSendEvent } from "../../services/event-manager";
2023-01-06 12:15:37 +05:00
import { clearMessage } from "../../services/message";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSimpleDialog } from "../../utils/events";
2023-01-06 12:15:37 +05:00
import TwoFactorVerification from "./two-factor";
export const LoginSteps = {
emailAuth: 1,
mfaAuth: 2,
passwordAuth: 3
};
2025-07-01 12:13:03 +05:00
export const useLogin = (
onFinishLogin?: () => void,
sessionExpired = false
) => {
2023-01-06 12:15:37 +05:00
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const setUser = useUserStore((state) => state.setUser);
const [step, setStep] = useState(LoginSteps.emailAuth);
2025-07-01 12:13:03 +05:00
const email = useRef<string>();
const password = useRef<string>();
const emailInputRef = useRef<TextInput>(null);
const passwordInputRef = useRef<TextInput>(null);
2023-01-06 12:15:37 +05:00
const validateInfo = () => {
if (
(!password.current && step === LoginSteps.passwordAuth) ||
(!email.current && step === LoginSteps.emailAuth)
) {
2023-08-29 20:42:45 +05:00
ToastManager.show({
2024-07-27 10:19:43 +05:00
heading: strings.allFieldsRequired(),
message: strings.allFieldsRequiredDesc(),
2023-01-06 12:15:37 +05:00
type: "error",
context: "local"
});
return false;
}
return true;
};
const login = async () => {
if (!validateInfo() || error) return;
try {
if (loading) return;
2023-01-06 12:15:37 +05:00
setLoading(true);
switch (step) {
case LoginSteps.emailAuth: {
2025-07-01 12:13:03 +05:00
if (!email.current) {
setLoading(false);
return;
}
2023-01-06 12:15:37 +05:00
const mfaInfo = await db.user.authenticateEmail(email.current);
2023-01-06 12:15:37 +05:00
if (mfaInfo) {
2024-11-06 10:57:04 +05:00
TwoFactorVerification.present(
2025-07-01 12:13:03 +05:00
async (mfa: any, callback: (success: boolean) => void) => {
2024-11-06 10:57:04 +05:00
try {
const success = await db.user.authenticateMultiFactorCode(
mfa.code,
mfa.method
);
2024-11-06 10:57:04 +05:00
if (success) {
setStep(LoginSteps.passwordAuth);
setLoading(false);
setTimeout(() => {
passwordInputRef.current?.focus();
}, 500);
callback && callback(true);
}
callback && callback(false);
} catch (e) {
callback && callback(false);
if (e.message === "invalid_grant") {
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
2024-11-06 10:57:04 +05:00
setLoading(false);
setStep(LoginSteps.emailAuth);
}
2023-01-06 12:15:37 +05:00
}
2024-11-06 10:57:04 +05:00
},
mfaInfo,
() => {
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
2024-11-06 10:57:04 +05:00
setLoading(false);
setStep(LoginSteps.emailAuth);
2023-01-06 12:15:37 +05:00
}
2024-11-06 10:57:04 +05:00
);
} else {
2024-08-15 15:39:53 +05:00
finishWithError(new Error(strings.unableToSend2faCode()));
2023-01-06 12:15:37 +05:00
}
break;
}
case LoginSteps.passwordAuth: {
2025-07-01 12:13:03 +05:00
if (!email.current || !password.current) {
setLoading(false);
return;
}
await db.user.authenticatePassword(
email.current,
password.current,
2025-07-01 12:13:03 +05:00
undefined,
sessionExpired
);
2023-01-06 12:15:37 +05:00
finishLogin();
break;
}
}
setLoading(false);
} catch (e) {
2025-07-01 12:13:03 +05:00
finishWithError(e as Error);
2023-01-06 12:15:37 +05:00
}
};
2025-07-01 12:13:03 +05:00
const finishWithError = async (e: Error) => {
if (e.message === "invalid_grant") setStep(LoginSteps.emailAuth);
2023-01-06 12:15:37 +05:00
setLoading(false);
2023-08-29 20:42:45 +05:00
ToastManager.show({
2024-07-27 10:19:43 +05:00
heading: strings.loginFailed(),
2023-01-06 12:15:37 +05:00
message: e.message,
type: "error",
context: "local"
});
};
const finishLogin = async () => {
const user = await db.user.getUser();
2024-08-15 15:39:53 +05:00
if (!user) throw new Error(strings.emailOrPasswordIncorrect());
2023-01-06 12:15:37 +05:00
PremiumService.setPremiumStatus();
setUser(user);
clearMessage();
2023-08-29 20:42:45 +05:00
ToastManager.show({
2024-07-27 10:19:43 +05:00
heading: strings.loginSuccess(),
2025-07-01 12:13:03 +05:00
message: strings.loginSuccessDesc(user.email),
2023-01-06 12:15:37 +05:00
type: "success",
context: "global"
});
SettingsService.set({
sessionExpired: false,
userEmailConfirmed: user?.isEmailConfirmed
});
onFinishLogin?.();
setLoading(false);
};
return {
login,
step,
setStep,
email,
password,
passwordInputRef,
emailInputRef,
loading,
setLoading,
error,
setError
};
};