mobile: fix login and signup flows

This commit is contained in:
Ammar Ahmed
2026-05-05 19:06:42 +05:00
parent 2ad39048da
commit 0801e73242
9 changed files with 79 additions and 104 deletions

View File

@@ -40,13 +40,13 @@ export const ForgotPassword = ({ userEmail }: { userEmail: string }) => {
createFormRef({
email: userEmail || ""
})
).current;
);
const emailInputRef = useRef<TextInput>(null);
const [loading, setLoading] = useState(false);
const [sent, setSent] = useState(false);
const sendRecoveryEmail = async () => {
if (formRef.validateField("email")) {
if (formRef.current.validateField("email")) {
ToastManager.show({
heading: strings.emailRequired(),
type: "error",
@@ -55,7 +55,7 @@ export const ForgotPassword = ({ userEmail }: { userEmail: string }) => {
return;
}
const values = formRef.getValues();
const values = formRef.current.getValues();
setLoading(true);
try {

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import { RouteProp, useRoute } from "@react-navigation/native";
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useState } from "react";
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import { DDS } from "../../services/device-detection";
@@ -37,15 +37,15 @@ import { DefaultAppStyles } from "../../utils/styles";
import { sleep } from "../../utils/time";
import { Dialog } from "../dialog";
import { Progress } from "../sheets/progress";
import AppIcon from "../ui/AppIcon";
import { Button } from "../ui/button";
import FormInput, { createFormRef, validators } from "../ui/input/form-input";
import FormInput, { validators } from "../ui/input/form-input";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { hideAuth } from "./common";
import { ForgotPassword } from "./forgot-password";
import { AuthHeader } from "./header";
import { useLogin } from "./use-login";
import AppIcon from "../ui/AppIcon";
const LoginSteps = {
emailAuth: 1,
@@ -64,14 +64,13 @@ export const Login = ({
const {
step,
setStep,
password,
email,
emailInputRef,
passwordInputRef,
loading,
setLoading,
login,
error
error,
formRef
} = useLogin(async () => {
eSendEvent(eUserLoggedIn, true);
await sleep(500);
@@ -94,24 +93,8 @@ export const Login = ({
});
const { width, height } = useWindowDimensions();
const isTablet = width > 600;
const formRef = useRef(
createFormRef({
email: "",
password: ""
})
).current;
const canContinue = () => {
if (step === LoginSteps.emailAuth) {
return !formRef.validateField("email");
}
return formRef.validate();
};
const onContinue = () => {
if (loading) return;
if (!canContinue()) return;
login();
};
@@ -224,9 +207,6 @@ export const Login = ({
name="email"
formRef={formRef}
fwdRef={emailInputRef}
onChangeText={(value) => {
email.current = value;
}}
testID="input.email"
returnKeyLabel="Next"
returnKeyType="next"
@@ -256,9 +236,6 @@ export const Login = ({
name="password"
formRef={formRef}
fwdRef={passwordInputRef}
onChangeText={(value) => {
password.current = value;
}}
testID="input.password"
returnKeyLabel={strings.done()}
returnKeyType="done"
@@ -282,9 +259,13 @@ export const Login = ({
paddingHorizontal: 0
}}
onPress={() => {
if (loading || !email.current) return;
if (loading) return;
presentSheet({
component: <ForgotPassword userEmail={email.current} />
component: (
<ForgotPassword
userEmail={formRef.current.getValue("email")}
/>
)
});
}}
textStyle={{

View File

@@ -43,29 +43,26 @@ import SheetProvider from "../sheet-provider";
import { Toast } from "../toast";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Input from "../ui/input";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { LoginSteps, useLogin } from "./use-login";
import { strings } from "@notesnook/intl";
import { getObfuscatedEmail } from "../../utils/functions";
import { DefaultAppStyles } from "../../utils/styles";
import FormInput, { validators } from "../ui/input/form-input";
export const SessionExpired = () => {
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
const [focused, setFocused] = useState(false);
const { step, password, email, passwordInputRef, loading, login } = useLogin(
() => {
eSendEvent(eUserLoggedIn, true);
setVisible(false);
setFocused(false);
useUserStore.setState({
disableAppLockRequests: false
});
},
true
);
const { step, passwordInputRef, loading, login, formRef } = useLogin(() => {
eSendEvent(eUserLoggedIn, true);
setVisible(false);
setFocused(false);
useUserStore.setState({
disableAppLockRequests: false
});
}, true);
const logout = async () => {
try {
@@ -92,6 +89,8 @@ export const SessionExpired = () => {
const open = React.useCallback(async () => {
try {
console.log("open...");
throw new Error("test");
const res = await db.tokenManager.getToken();
if (!res) throw new Error("no token found");
if (db.tokenManager._isTokenExpired(res))
@@ -104,7 +103,7 @@ export const SessionExpired = () => {
if (!complete) {
const user = await db.user.getUser();
if (!user) return;
email.current = user.email;
formRef.current.setValue("email", user.email);
setVisible(true);
setFocused(false);
return;
@@ -117,14 +116,14 @@ export const SessionExpired = () => {
} catch (e) {
const user = await db.user.getUser();
if (!user) return;
email.current = user.email;
formRef.current.setValue("email", user.email);
setFocused(false);
setVisible(true);
useUserStore.setState({
disableAppLockRequests: true
});
}
}, [email]);
}, [formRef]);
useEffect(() => {
const sub = eSubscribeEvent(eLoginSessionExpired, open);
@@ -155,6 +154,7 @@ export const SessionExpired = () => {
enableSheetKeyboardHandler={true}
visible={true}
>
<Dialog context="two_factor_verify" />
<View
style={{
width: focused ? "100%" : "99.9%",
@@ -192,17 +192,17 @@ export const SessionExpired = () => {
}}
>
{strings.sessionExpiredDesc(
getObfuscatedEmail(email.current as string)
getObfuscatedEmail(formRef.current.getValue("email") as string)
)}
</Paragraph>
</View>
{step === LoginSteps.passwordAuth ? (
<Input
<FormInput
fwdRef={passwordInputRef}
onChangeText={(value) => {
password.current = value;
}}
formRef={formRef}
name="password"
validators={[validators.required(strings.passwordRequired())]}
returnKeyLabel={strings.done()}
returnKeyType="next"
secureTextEntry
@@ -210,7 +210,7 @@ export const SessionExpired = () => {
autoCapitalize="none"
autoCorrect={false}
placeholder={strings.password()}
onSubmit={() => {
onSubmitEditing={() => {
login();
}}
/>

View File

@@ -68,7 +68,7 @@ export const Signup = ({
password: "",
confirmPassword: ""
})
).current;
);
const emailInputRef = useRef<TextInput>(null);
const passwordInputRef = useRef<TextInput>(null);
const confirmPasswordInputRef = useRef<TextInput>(null);
@@ -82,10 +82,10 @@ export const Signup = ({
const signup = async () => {
setErrorMessage(undefined);
if (!formRef.validate()) return;
if (!formRef.current.validate()) return;
if (loading) return;
const values = formRef.getValues();
const values = formRef.current.getValues();
setLoading(true);
try {
@@ -110,7 +110,7 @@ export const Signup = ({
if (
(e as Error).message === "Unable to create an account on this email."
) {
formRef.setError("email", (e as Error).message);
formRef.current.setError("email", (e as Error).message);
} else {
setErrorMessage((e as Error).message);
}

View File

@@ -28,6 +28,7 @@ import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSimpleDialog } from "../../utils/events";
import TwoFactorVerification from "./two-factor";
import { createFormRef } from "../ui/input/form-input";
export const LoginSteps = {
emailAuth: 1,
@@ -43,41 +44,29 @@ export const useLogin = (
const [loading, setLoading] = useState(false);
const setUser = useUserStore((state) => state.setUser);
const [step, setStep] = useState(LoginSteps.emailAuth);
const email = useRef<string>(undefined);
const password = useRef<string>(undefined);
const emailInputRef = useRef<TextInput>(null);
const passwordInputRef = useRef<TextInput>(null);
const validateInfo = () => {
if (
(!password.current && step === LoginSteps.passwordAuth) ||
(!email.current && step === LoginSteps.emailAuth)
) {
ToastManager.show({
heading: strings.allFieldsRequired(),
message: strings.allFieldsRequiredDesc(),
type: "error",
context: "local"
});
return false;
}
return true;
};
const formRef = useRef(
createFormRef({
email: "",
password: ""
})
);
const login = async () => {
if (!validateInfo() || error) return;
try {
if (loading) return;
setError(undefined);
setLoading(true);
switch (step) {
case LoginSteps.emailAuth: {
if (!email.current) {
if (formRef.current.validateField("email")) {
setLoading(false);
return;
}
const mfaInfo = await db.user.authenticateEmail(email.current);
const mfaInfo = await db.user.authenticateEmail(
formRef.current.getValue("email")
);
if (mfaInfo) {
TwoFactorVerification.present(
@@ -119,13 +108,14 @@ export const useLogin = (
break;
}
case LoginSteps.passwordAuth: {
if (!email.current || !password.current) {
if (!formRef.current.validate()) {
setLoading(false);
return;
}
const values = formRef.current.getValues();
await db.user.authenticatePassword(
email.current,
password.current,
values.email,
values.password,
undefined,
sessionExpired
);
@@ -142,7 +132,11 @@ export const useLogin = (
const finishWithError = async (e: Error) => {
if (e.message === "invalid_grant") setStep(LoginSteps.emailAuth);
setLoading(false);
setError(e);
if (e.message === "Password is incorrect.") {
formRef.current.setError("password", e.message);
} else {
setError(e);
}
};
const finishLogin = async () => {
@@ -169,13 +163,12 @@ export const useLogin = (
login,
step,
setStep,
email,
password,
passwordInputRef,
emailInputRef,
loading,
setLoading,
error,
setError
setError,
formRef
};
};

View File

@@ -69,8 +69,6 @@ export const SectionHeader = React.memo<
dataType as "note" | "notebook" | "searchResult"
);
console.log(item);
return (
<View
style={{

View File

@@ -323,7 +323,6 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
}
function getDate(item: Notebook | Note, groupType?: GroupingKey): number {
console.log(groupType);
return (
getSortValue(
groupType

View File

@@ -74,8 +74,10 @@ export function createFormRef(initialValues: FormValues = {}): FormRef {
errors,
setValue(name, value) {
values[name] = value;
if (errors[name]) delete errors[name];
notify();
if (errors[name]) {
delete errors[name];
notify();
}
},
getValue(name) {
return values[name] ?? "";
@@ -196,7 +198,7 @@ export const validators = {
interface FormInputProps extends TextInputProps {
name: string;
formRef: FormRef;
formRef: RefObject<FormRef>;
validators?: FieldValidator[];
fwdRef?: RefObject<TextInput | null>;
loading?: boolean;
@@ -251,19 +253,21 @@ export function FormInput({
const [, setVersion] = useState(0);
useEffect(() => {
formRef.registerField(name, fieldValidators);
const unsubscribe = formRef.subscribe(() => {
const form = formRef.current;
form.registerField(name, fieldValidators);
const unsubscribe = form.subscribe(() => {
setVersion((v) => v + 1);
});
return () => {
unsubscribe();
formRef.unregisterField(name);
form.unregisterField(name);
};
}, [fieldValidators, formRef, name]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formRef, name]);
const value = formRef.getValue(name);
const fieldError = error || formRef.getError(name);
const value = formRef.current.getValue(name);
const fieldError = error || formRef.current.getError(name);
const borderColor = useMemo(() => {
if (fieldError) return colors.error.accent;
@@ -296,7 +300,7 @@ export function FormInput({
};
const handleChangeText = (nextValue: string) => {
formRef.setValue(name, nextValue);
formRef.current.setValue(name, nextValue);
onChangeText?.(nextValue);
};
@@ -319,7 +323,7 @@ export function FormInput({
<TextInput
{...restProps}
value={value}
defaultValue={value}
ref={fwdRef}
editable={!loading && restProps.editable !== false}
onChangeText={handleChangeText}

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.3.20",
"version": "3.3.22",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.3.20",
"version": "3.3.22",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {