diff --git a/apps/mobile/app/components/auth/auth-modal.js b/apps/mobile/app/components/auth/auth-modal.js
index 3f81926aa..d60f9cf40 100644
--- a/apps/mobile/app/components/auth/auth-modal.js
+++ b/apps/mobile/app/components/auth/auth-modal.js
@@ -19,9 +19,6 @@ along with this program. If not, see .
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
-import { Platform, View } from "react-native";
-import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
-import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import {
eSubscribeEvent,
eUnSubscribeEvent
@@ -31,9 +28,7 @@ import { eCloseLoginDialog, eOpenLoginDialog } from "../../utils/events";
import { sleep } from "../../utils/time";
import BaseDialog from "../dialog/base-dialog";
import { Toast } from "../toast";
-import { Button } from "../ui/button";
-import { IconButton } from "../ui/icon-button";
-import { hideAuth, initialAuthMode } from "./common";
+import { initialAuthMode } from "./common";
import { Login } from "./login";
import { Signup } from "./signup";
import { strings } from "@notesnook/intl";
@@ -51,7 +46,6 @@ const AuthModal = () => {
const [visible, setVisible] = useState(false);
const [currentAuthMode, setCurrentAuthMode] = useState(AuthMode.login);
const actionSheetRef = useRef();
- const insets = useGlobalSafeAreaInsets();
useEffect(() => {
eSubscribeEvent(eOpenLoginDialog, open);
@@ -99,81 +93,18 @@ const AuthModal = () => {
centered={false}
enableSheetKeyboardHandler
>
-
- {currentAuthMode !== AuthMode.login ? (
- setCurrentAuthMode(mode)}
- trial={AuthMode.trialSignup === currentAuthMode}
- welcome={initialAuthMode.current === AuthMode.welcomeSignup}
- />
- ) : (
- setCurrentAuthMode(mode)}
- />
- )}
-
-
-
-
- {initialAuthMode.current === AuthMode.welcomeSignup ? null : (
- {
- hideAuth();
- }}
- color={colors.primary.paragraph}
- />
- )}
-
- {initialAuthMode.current !== AuthMode.welcomeSignup ? null : (
-
-
+ {currentAuthMode !== AuthMode.login ? (
+ setCurrentAuthMode(mode)}
+ trial={AuthMode.trialSignup === currentAuthMode}
+ welcome={initialAuthMode.current === AuthMode.welcomeSignup}
+ />
+ ) : (
+ setCurrentAuthMode(mode)}
+ />
+ )}
diff --git a/apps/mobile/app/components/auth/header.tsx b/apps/mobile/app/components/auth/header.tsx
new file mode 100644
index 000000000..a67546256
--- /dev/null
+++ b/apps/mobile/app/components/auth/header.tsx
@@ -0,0 +1,80 @@
+/*
+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 { useThemeColors } from "@notesnook/theme";
+import React from "react";
+import { Platform, View } from "react-native";
+import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
+import { Button } from "../ui/button";
+import { IconButton } from "../ui/icon-button";
+import { hideAuth } from "./common";
+
+export const AuthHeader = (props: { welcome?: boolean }) => {
+ const { colors } = useThemeColors();
+ const insets = useGlobalSafeAreaInsets();
+ return (
+
+
+ {props.welcome ? null : (
+ {
+ hideAuth();
+ }}
+ color={colors.primary.paragraph}
+ />
+ )}
+
+ {!props.welcome ? null : (
+
+
+ );
+};
diff --git a/apps/mobile/app/components/auth/login.js b/apps/mobile/app/components/auth/login.js
index 6b75f44fe..d6f5c2f1e 100644
--- a/apps/mobile/app/components/auth/login.js
+++ b/apps/mobile/app/components/auth/login.js
@@ -302,4 +302,4 @@ export const Login = ({ changeMode }) => {
>
);
-};
+};
\ No newline at end of file
diff --git a/apps/mobile/app/components/auth/signup-context.ts b/apps/mobile/app/components/auth/signup-context.ts
new file mode 100644
index 000000000..567bce983
--- /dev/null
+++ b/apps/mobile/app/components/auth/signup-context.ts
@@ -0,0 +1,27 @@
+/*
+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, { useContext } from "react";
+
+export const SignupContext = React.createContext<{
+ signup?: () => Promise;
+}>({
+ signup: undefined
+});
+
+export const useSignupContext = () => useContext(SignupContext);
diff --git a/apps/mobile/app/components/auth/signup.js b/apps/mobile/app/components/auth/signup.js
index b3acab1a0..28c4541ec 100644
--- a/apps/mobile/app/components/auth/signup.js
+++ b/apps/mobile/app/components/auth/signup.js
@@ -21,24 +21,32 @@ import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useRef, useState } from "react";
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
+import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
import { ToastManager } from "../../services/event-manager";
import { clearMessage, setEmailVerifyMessage } from "../../services/message";
-import PremiumService from "../../services/premium";
-import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { openLinkInBrowser } from "../../utils/functions";
-import { AppFontSize } from "../../utils/size";
-import { sleep } from "../../utils/time";
+import { AppFontSize, SIZE } from "../../utils/size";
+import { Loading } from "../loading";
+import { PaywallComponent } from "../premium/component";
import { Button } from "../ui/button";
import Input from "../ui/input";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { hideAuth } from "./common";
-import { DefaultAppStyles } from "../../utils/styles";
+import { AuthHeader } from "./header";
+import { SignupContext } from "./signup-context";
-export const Signup = ({ changeMode, trial }) => {
+const SignupSteps = {
+ signup: 0,
+ selectPlan: 1,
+ createAccount: 2
+};
+
+export const Signup = ({ changeMode, welcome }) => {
+ const [currentStep, setCurrentStep] = useState(SignupSteps.signup);
const { colors } = useThemeColors();
const email = useRef();
const emailInputRef = useRef();
@@ -52,6 +60,7 @@ export const Signup = ({ changeMode, trial }) => {
const setLastSynced = useUserStore((state) => state.setLastSynced);
const { width, height } = useWindowDimensions();
const isTablet = width > 600;
+ const deviceMode = useSettingStore((state) => state.deviceMode);
const validateInfo = () => {
if (!password.current || !email.current || !confirmPassword.current) {
ToastManager.show({
@@ -70,23 +79,20 @@ export const Signup = ({ changeMode, trial }) => {
const signup = async () => {
if (!validateInfo() || error) return;
if (loading) return;
+
setLoading(true);
try {
+ setCurrentStep(SignupSteps.createAccount);
await db.user.signup(email.current.toLowerCase(), password.current);
let user = await db.user.getUser();
setUser(user);
setLastSynced(await db.lastSynced());
clearMessage();
setEmailVerifyMessage();
- hideAuth();
- SettingsService.setProperty("encryptedBackup", true);
- await sleep(300);
- if (trial) {
- PremiumService.sheet(null, null, true);
- } else {
- PremiumService.showVerifyEmailDialog();
- }
+ setCurrentStep(SignupSteps.selectPlan);
+ return true;
} catch (e) {
+ setCurrentStep(SignupSteps.signup);
setLoading(false);
ToastManager.show({
heading: strings.signupFailed(),
@@ -94,214 +100,265 @@ export const Signup = ({ changeMode, trial }) => {
type: "error",
context: "local"
});
+ return false;
}
};
return (
- <>
-
-
-
+ {currentStep === SignupSteps.signup ? (
+ <>
+
+
-
-
-
-
- {strings.createYourAccount()}
-
-
-
-
- {
- email.current = value;
- }}
- testID="input.email"
- onErrorCheck={(e) => setError(e)}
- returnKeyLabel={strings.next()}
- returnKeyType="next"
- autoComplete="email"
- validationType="email"
- autoCorrect={false}
- autoCapitalize="none"
- errorMessage={strings.email()}
- placeholder={strings.email()}
- onSubmit={() => {
- passwordInputRef.current?.focus();
- }}
- />
-
- {
- password.current = value;
- }}
- testID="input.password"
- onErrorCheck={(e) => setError(e)}
- returnKeyLabel={strings.next()}
- returnKeyType="next"
- secureTextEntry
- autoComplete="password"
- autoCapitalize="none"
- validationType="password"
- autoCorrect={false}
- placeholder={strings.password()}
- onSubmit={() => {
- confirmPasswordInputRef.current?.focus();
- }}
- />
-
- {
- confirmPassword.current = value;
- }}
- testID="input.confirmPassword"
- onErrorCheck={(e) => setError(e)}
- returnKeyLabel={strings.done()}
- returnKeyType="done"
- secureTextEntry
- autoComplete="password"
- autoCapitalize="none"
- autoCorrect={false}
- validationType="confirmPassword"
- customValidator={() => password.current}
- placeholder={strings.confirmPassword()}
- marginBottom={12}
- onSubmit={signup}
- />
-
-
- {strings.signupAgreement[0]()}
- {
- openLinkInBrowser("https://notesnook.com/tos", colors);
- }}
- style={{
- textDecorationLine: "underline"
- }}
- color={colors.primary.accent}
>
- {" "}
- {strings.signupAgreement[1]()}
- {" "}
- {strings.signupAgreement[2]()}
- {
- openLinkInBrowser("https://notesnook.com/privacy", colors);
- }}
- style={{
- textDecorationLine: "underline"
- }}
- color={colors.primary.accent}
- >
- {" "}
- {strings.signupAgreement[3]()}
- {" "}
- {strings.signupAgreement[4]()}
-
-
-
-
- {
- if (loading) return;
- changeMode(0);
- }}
- activeOpacity={0.8}
- style={{
- alignSelf: "center",
- marginTop: DefaultAppStyles.GAP_VERTICAL,
- paddingVertical: DefaultAppStyles.GAP_VERTICAL
- }}
- >
-
- {strings.alreadyHaveAccount()}{" "}
-
- {strings.login()}
+
+
+
+
+
+
+ {strings.createAccount()}
+
+
+
+
+ {
+ email.current = value;
+ }}
+ testID="input.email"
+ onErrorCheck={(e) => setError(e)}
+ returnKeyLabel="Next"
+ returnKeyType="next"
+ autoComplete="email"
+ validationType="email"
+ autoCorrect={false}
+ autoCapitalize="none"
+ errorMessage={strings.emailInvalid()}
+ placeholder={strings.email()}
+ blurOnSubmit={false}
+ onSubmit={() => {
+ if (!email.current) return;
+ passwordInputRef.current?.focus();
+ }}
+ />
+
+ {
+ password.current = value;
+ }}
+ testID="input.password"
+ onErrorCheck={(e) => setError(e)}
+ returnKeyLabel="Next"
+ returnKeyType="next"
+ secureTextEntry
+ autoComplete="password"
+ autoCapitalize="none"
+ blurOnSubmit={false}
+ validationType="password"
+ autoCorrect={false}
+ placeholder={strings.password()}
+ onSubmit={() => {
+ if (!password.current) return;
+ confirmPasswordInputRef.current?.focus();
+ }}
+ />
+
+ {
+ confirmPassword.current = value;
+ }}
+ testID="input.confirmPassword"
+ onErrorCheck={(e) => setError(e)}
+ returnKeyLabel="Signup"
+ returnKeyType="done"
+ secureTextEntry
+ autoComplete="password"
+ autoCapitalize="none"
+ autoCorrect={false}
+ blurOnSubmit={false}
+ validationType="confirmPassword"
+ customValidator={() => password.current}
+ placeholder={strings.confirmPassword()}
+ marginBottom={12}
+ onSubmit={signup}
+ />
+
+
+
+
+ {strings.signupAgreement[0]()}
+ {
+ openLinkInBrowser("https://notesnook.com/tos", colors);
+ }}
+ style={{
+ textDecorationLine: "underline"
+ }}
+ color={colors.primary.accent}
+ >
+ {" "}
+ {strings.signupAgreement[1]()}
+ {" "}
+ {strings.signupAgreement[2]()}
+ {
+ openLinkInBrowser("https://notesnook.com/privacy", colors);
+ }}
+ style={{
+ textDecorationLine: "underline"
+ }}
+ color={colors.primary.accent}
+ >
+ {" "}
+ {strings.signupAgreement[3]()}
+ {" "}
+ {strings.signupAgreement[4]()}
-
-
-
-
- >
+
+
+ >
+ ) : currentStep === SignupSteps.createAccount ? (
+ <>
+
+ >
+ ) : (
+ <>
+ {
+ hideAuth();
+ }}
+ setupAccount={() => {
+ setCurrentStep(SignupSteps.createAccount);
+ }}
+ isModal={false}
+ />
+ >
+ )}
+
);
};
diff --git a/apps/mobile/app/components/auth/use-login.js b/apps/mobile/app/components/auth/use-login.ts
similarity index 85%
rename from apps/mobile/app/components/auth/use-login.js
rename to apps/mobile/app/components/auth/use-login.ts
index f959fc9a3..98c60a1df 100644
--- a/apps/mobile/app/components/auth/use-login.js
+++ b/apps/mobile/app/components/auth/use-login.ts
@@ -18,8 +18,9 @@ along with this program. If not, see .
*/
import { useRef, useState } from "react";
+import { TextInput } from "react-native";
import { db } from "../../common/database";
-import { eSendEvent, ToastManager } from "../../services/event-manager";
+import { ToastManager, eSendEvent } from "../../services/event-manager";
import { clearMessage } from "../../services/message";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
@@ -34,15 +35,18 @@ export const LoginSteps = {
passwordAuth: 3
};
-export const useLogin = (onFinishLogin, sessionExpired = false) => {
+export const useLogin = (
+ onFinishLogin?: () => void,
+ sessionExpired = false
+) => {
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const setUser = useUserStore((state) => state.setUser);
const [step, setStep] = useState(LoginSteps.emailAuth);
- const email = useRef();
- const password = useRef();
- const emailInputRef = useRef();
- const passwordInputRef = useRef();
+ const email = useRef();
+ const password = useRef();
+ const emailInputRef = useRef(null);
+ const passwordInputRef = useRef(null);
const validateInfo = () => {
if (
@@ -69,11 +73,15 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
setLoading(true);
switch (step) {
case LoginSteps.emailAuth: {
+ if (!email.current) {
+ setLoading(false);
+ return;
+ }
const mfaInfo = await db.user.authenticateEmail(email.current);
if (mfaInfo) {
TwoFactorVerification.present(
- async (mfa, callback) => {
+ async (mfa: any, callback: (success: boolean) => void) => {
try {
const success = await db.user.authenticateMultiFactorCode(
mfa.code,
@@ -111,10 +119,14 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
break;
}
case LoginSteps.passwordAuth: {
+ if (!email.current || !password.current) {
+ setLoading(false);
+ return;
+ }
await db.user.authenticatePassword(
email.current,
password.current,
- null,
+ undefined,
sessionExpired
);
finishLogin();
@@ -123,11 +135,11 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
}
setLoading(false);
} catch (e) {
- finishWithError(e);
+ finishWithError(e as Error);
}
};
- const finishWithError = async (e) => {
+ const finishWithError = async (e: Error) => {
if (e.message === "invalid_grant") setStep(LoginSteps.emailAuth);
setLoading(false);
ToastManager.show({
@@ -146,7 +158,7 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
clearMessage();
ToastManager.show({
heading: strings.loginSuccess(),
- message: strings.loginSuccessDesc(),
+ message: strings.loginSuccessDesc(user.email),
type: "success",
context: "global"
});
diff --git a/apps/mobile/app/components/intro/index.tsx b/apps/mobile/app/components/intro/index.tsx
index 041a07a5b..33478f1f6 100644
--- a/apps/mobile/app/components/intro/index.tsx
+++ b/apps/mobile/app/components/intro/index.tsx
@@ -24,13 +24,13 @@ import { Linking, ScrollView, useWindowDimensions, View } from "react-native";
import { SwiperFlatList } from "react-native-swiper-flatlist";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import Navigation from "../../services/navigation";
+import SettingsService from "../../services/settings";
import { AppFontSize } from "../../utils/size";
+import { DefaultAppStyles } from "../../utils/styles";
+import { AuthMode } from "../auth/common";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
-import SettingsService from "../../services/settings";
-import { AuthMode } from "../auth/common";
-import { DefaultAppStyles } from "../../utils/styles";
const Intro = () => {
const { colors } = useThemeColors();
@@ -182,6 +182,17 @@ const Intro = () => {
type="accent"
title={strings.getStarted()}
/>
+
+