diff --git a/apps/admin/data/fitai.db b/apps/admin/data/fitai.db
index 6eae76a..d9191b2 100644
Binary files a/apps/admin/data/fitai.db and b/apps/admin/data/fitai.db differ
diff --git a/apps/mobile/src/app/(auth)/sign-in.tsx b/apps/mobile/src/app/(auth)/sign-in.tsx
index ac9f428..afd96d7 100644
--- a/apps/mobile/src/app/(auth)/sign-in.tsx
+++ b/apps/mobile/src/app/(auth)/sign-in.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import { useState, useEffect } from "react";
import { useSignIn, useAuth } from "@clerk/clerk-expo";
import { useRouter } from "expo-router";
import {
@@ -12,19 +12,20 @@ import {
Platform,
ScrollView,
} from "react-native";
+import { OAuthButtons } from "../../components/auth/OAuthButtons";
export default function SignInScreen() {
const { signIn, setActive, isLoaded } = useSignIn();
const { isSignedIn } = useAuth();
const router = useRouter();
- const [emailAddress, setEmailAddress] = React.useState("");
- const [password, setPassword] = React.useState("");
- const [loading, setLoading] = React.useState(false);
- const [error, setError] = React.useState("");
+ const [emailAddress, setEmailAddress] = useState("");
+ const [password, setPassword] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
// Redirect if already signed in
- React.useEffect(() => {
+ useEffect(() => {
if (isSignedIn) {
router.replace("/(tabs)");
}
@@ -64,7 +65,7 @@ export default function SignInScreen() {
setError(
err.errors?.[0]?.message ||
- "Failed to sign in. Please check your credentials.",
+ "Failed to sign in. Please check your credentials.",
);
} finally {
setLoading(false);
@@ -100,6 +101,14 @@ export default function SignInScreen() {
) : null}
+
+
+
+
+ OR
+
+
+
Email
@@ -256,4 +265,19 @@ const styles = StyleSheet.create({
color: "#2563eb",
fontWeight: "600",
},
+ dividerContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ marginBottom: 24,
+ },
+ dividerLine: {
+ flex: 1,
+ height: 1,
+ backgroundColor: "#ddd",
+ },
+ dividerText: {
+ marginHorizontal: 10,
+ color: "#666",
+ fontSize: 14,
+ },
});
diff --git a/apps/mobile/src/app/(auth)/sign-up.tsx b/apps/mobile/src/app/(auth)/sign-up.tsx
index 7ff1997..0326dc3 100644
--- a/apps/mobile/src/app/(auth)/sign-up.tsx
+++ b/apps/mobile/src/app/(auth)/sign-up.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import { useState, useEffect } from "react";
import { useSignUp, useAuth } from "@clerk/clerk-expo";
import { useRouter } from "expo-router";
import {
@@ -12,23 +12,24 @@ import {
Platform,
ScrollView,
} from "react-native";
+import { OAuthButtons } from "../../components/auth/OAuthButtons";
export default function SignUpScreen() {
const { signUp, setActive, isLoaded } = useSignUp();
const { isSignedIn } = useAuth();
const router = useRouter();
- const [emailAddress, setEmailAddress] = React.useState("");
- const [password, setPassword] = React.useState("");
- const [firstName, setFirstName] = React.useState("");
- const [lastName, setLastName] = React.useState("");
- const [pendingVerification, setPendingVerification] = React.useState(false);
- const [code, setCode] = React.useState("");
- const [loading, setLoading] = React.useState(false);
- const [error, setError] = React.useState("");
+ const [emailAddress, setEmailAddress] = useState("");
+ const [password, setPassword] = useState("");
+ const [firstName, setFirstName] = useState("");
+ const [lastName, setLastName] = useState("");
+ const [pendingVerification, setPendingVerification] = useState(false);
+ const [code, setCode] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
// Redirect if already signed in
- React.useEffect(() => {
+ useEffect(() => {
if (isSignedIn) {
router.replace("/(tabs)");
}
@@ -182,6 +183,14 @@ export default function SignUpScreen() {
) : null}
+
+
+
+
+ OR
+
+
+
First Name
@@ -372,4 +381,19 @@ const styles = StyleSheet.create({
color: "#2563eb",
fontWeight: "600",
},
+ dividerContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ marginBottom: 24,
+ },
+ dividerLine: {
+ flex: 1,
+ height: 1,
+ backgroundColor: "#ddd",
+ },
+ dividerText: {
+ marginHorizontal: 10,
+ color: "#666",
+ fontSize: 14,
+ },
});
diff --git a/apps/mobile/src/components/auth/OAuthButtons.tsx b/apps/mobile/src/components/auth/OAuthButtons.tsx
new file mode 100644
index 0000000..d0b08db
--- /dev/null
+++ b/apps/mobile/src/components/auth/OAuthButtons.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+import * as WebBrowser from "expo-web-browser";
+import { Button, View, StyleSheet, TouchableOpacity, Text } from "react-native";
+import { useOAuth } from "@clerk/clerk-expo";
+import { useWarmUpBrowser } from "../../hooks/useWarmUpBrowser";
+import { Ionicons } from "@expo/vector-icons";
+
+WebBrowser.maybeCompleteAuthSession();
+
+export function OAuthButtons() {
+ useWarmUpBrowser();
+
+ const { startOAuthFlow } = useOAuth({ strategy: "oauth_google" });
+
+ const onPress = React.useCallback(async () => {
+ try {
+ const { createdSessionId, signIn, signUp, setActive } =
+ await startOAuthFlow();
+
+ if (createdSessionId) {
+ setActive!({ session: createdSessionId });
+ } else {
+ // Use signIn or signUp for next steps such as MFA
+ }
+ } catch (err) {
+ console.error("OAuth error", err);
+ }
+ }, []);
+
+ return (
+
+
+
+ Continue with Google
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ width: "100%",
+ marginTop: 16,
+ },
+ button: {
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "center",
+ backgroundColor: "#fff",
+ borderWidth: 1,
+ borderColor: "#ddd",
+ paddingVertical: 12,
+ borderRadius: 8,
+ marginBottom: 12,
+ },
+ icon: {
+ marginRight: 10,
+ },
+ text: {
+ fontSize: 16,
+ fontWeight: "500",
+ color: "#333",
+ },
+});
diff --git a/apps/mobile/src/hooks/useWarmUpBrowser.ts b/apps/mobile/src/hooks/useWarmUpBrowser.ts
new file mode 100644
index 0000000..9dd2212
--- /dev/null
+++ b/apps/mobile/src/hooks/useWarmUpBrowser.ts
@@ -0,0 +1,13 @@
+import React from "react";
+import * as WebBrowser from "expo-web-browser";
+
+export const useWarmUpBrowser = () => {
+ React.useEffect(() => {
+ // Warm up the android browser to improve UX
+ // https://docs.expo.dev/guides/authentication/#improving-user-experience
+ void WebBrowser.warmUpAsync();
+ return () => {
+ void WebBrowser.coolDownAsync();
+ };
+ }, []);
+};