diff --git a/app.json b/app.json
index 68e9a3c..b2a3bad 100644
--- a/app.json
+++ b/app.json
@@ -1,7 +1,7 @@
{
"expo": {
"name": "Tech Tracker",
- "slug": "tech-tracker",
+ "slug": "tech-tracker-expo",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
diff --git a/components/auth/AppleSignIniOS.tsx b/components/auth/AppleSignIniOS.tsx
index e74a059..98f073b 100644
--- a/components/auth/AppleSignIniOS.tsx
+++ b/components/auth/AppleSignIniOS.tsx
@@ -1,11 +1,12 @@
import React, { useState, useEffect } from 'react';
import { supabase } from '@/lib/supabase';
-import * as AppleAuthentication from 'expo-apple-authentication'
+import * as AppleAuthentication from 'expo-apple-authentication';
import { useColorScheme } from '@/hooks/useColorScheme';
import { ThemedView } from '@/components/theme';
-import { StyleSheet, Platform } from 'react-native';
+import { StyleSheet, Platform } from 'react-native';
import Constants from 'expo-constants';
import * as Notifications from 'expo-notifications';
+import type { updateUser } from '@/constants/Types';
const AppleSignInButton = () => {
const scheme = useColorScheme() ?? 'dark';
@@ -21,18 +22,14 @@ const AppleSignInButton = () => {
//const projectId = Constants.expoConfig?.extra?.projectId;
//if (!projectId) throw new Error('No projectId found in expo.config.json');
//const pushToken = await Notifications.getExpoPushTokenAsync({
- //projectId,
+ //projectId,
//});
if (credential.identityToken) {
const email = credential.email;
- const full_name = (
- credential.fullName &&
- credential.fullName.givenName &&
- credential.fullName.familyName
- )
- ? `${credential.fullName.givenName} ${credential.fullName.familyName}`
- : null;
-
+ const full_name =
+ credential.fullName && credential.fullName.givenName && credential.fullName.familyName
+ ? `${credential.fullName.givenName} ${credential.fullName.familyName}`
+ : null;
const {
error,
data: { user, session },
@@ -40,32 +37,32 @@ const AppleSignInButton = () => {
provider: 'apple',
token: credential.identityToken,
});
- console.log(JSON.stringify({ error, user }, null, 2))
- if (!error) {
- if (user && session) {
- const data: any = {};
- if (email) data.email = email;
- if (full_name) data.full_name = full_name;
- const { error: updateError } = await supabase.auth.updateUser({
+ console.log(JSON.stringify({ error, user }, null, 2));
+ if (!error && session) {
+ if (email) {
+ const data: updateUser = {
+ email,
+ full_name: full_name ?? '',
+ };
+ const { error: authUpdateError } = await supabase.auth.updateUser({
data,
+ })
+ const { error: updateError } = await supabase
+ .from('profiles')
+ .upsert({
+ id: session.user.id,
+ full_name,
+ email,
+ provider: 'apple',
+ updated_at: new Date(),
});
if (updateError) {
console.error('Error updating user metadata:', updateError);
}
- const { error: updateProfileError } =
- await supabase.from('profiles').upsert({
- id: session.user.id,
- username: data?.email.split('@')[0],
- full_name: data?.full_name,
- updated_at: new Date(),
- });
- if (updateProfileError) {
- console.error('Error updating profile:', updateProfileError);
- }
}
}
} else {
- throw new Error('No identityToken.')
+ throw new Error('No identityToken.');
}
} catch (e: any) {
if (e.code === 'ERR_REQUEST_CANCELED') {
@@ -76,26 +73,26 @@ const AppleSignInButton = () => {
console.log('Error signing in with Apple:', e);
}
}
- }
+ };
if (Platform.OS !== 'ios') return ;
- else return (
-
-
-
-
- );
-}
+ else
+ return (
+
+
+
+ );
+};
export default AppleSignInButton;
const styles = StyleSheet.create({
@@ -105,4 +102,4 @@ const styles = StyleSheet.create({
alignItems: 'center',
marginTop: 20,
},
-});
+});
diff --git a/components/auth/Auth.tsx b/components/auth/Auth.tsx
index bd6bda6..5b8b631 100644
--- a/components/auth/Auth.tsx
+++ b/components/auth/Auth.tsx
@@ -4,6 +4,7 @@ import { supabase } from '@/lib/supabase';
import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
import AppleSignInButton from '@/components/auth/AppleSignIniOS';
import AzureSignIn from './AzureSignIn';
+import type { updateUser } from '@/constants/Types';
// Tells Supabase Auth to continuously refresh the session automatically if
// the app is in the foreground. When this is added, you will continue to receive
@@ -20,6 +21,7 @@ if (Platform.OS !== 'web') {
}
const Auth = () => {
+ const [full_name, setFullName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
@@ -50,11 +52,26 @@ const Auth = () => {
data: { session },
error,
} = await supabase.auth.signUp({
- email: email,
- password: password,
+ email,
+ password,
});
if (error) Alert.alert(error.message);
else if (!session) Alert.alert('Please check your inbox for email verification!');
+ else {
+ const { error: updateProfileError } = await supabase
+ .from('profiles')
+ .upsert({
+ id: session.user.id,
+ full_name,
+ email,
+ provider: 'email',
+ updated_at: new Date(),
+ });
+ if (updateProfileError) {
+ Alert.alert('Error updating profile:', updateProfileError.message);
+ console.error('Error updating profile:', updateProfileError.message);
+ }
+ }
setLoading(false);
};
@@ -67,6 +84,15 @@ const Auth = () => {
+
+ setFullName(text)}
+ value={full_name}
+ placeholder='Full Name'
+ />
+
+
{
const signInWithAzure = async () => {
try {
setLoading(true);
- console.log('Starting Azure sign-in with tenant-specific endpoint');
- console.log('Redirect URI:', redirectUri);
// Create the MSAL auth request
const request = new AuthSession.AuthRequest({
@@ -44,31 +43,27 @@ const AzureSignIn = () => {
console.log('Generated auth URL:', authUrl);
// Open the auth URL in a browser
- const result = await WebBrowser.openAuthSessionAsync(
- authUrl,
- redirectUri,
- {
- showInRecents: true,
- }
- );
+ const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUri, {
+ showInRecents: true,
+ });
console.log('Auth session result type:', result.type);
if (result.type === 'success' && result.url) {
// Parse the URL to get the authorization code
const { params, errorCode } = QueryParams.getQueryParams(result.url);
-
+
if (errorCode || params.error) {
const errorMessage = params.error_description || params.error || errorCode;
throw new Error(`Error during authentication: ${errorMessage}`);
}
-
+
if (!params.code) {
throw new Error('No authorization code received');
}
-
+
console.log('Authorization code received');
-
+
// Exchange the code for tokens
const tokenResult = await AuthSession.exchangeCodeAsync(
{
@@ -79,26 +74,54 @@ const AzureSignIn = () => {
code_verifier: request.codeVerifier || '',
},
},
- discovery
+ discovery,
);
-
+
console.log('Token exchange successful');
-
+
if (!tokenResult.idToken) {
throw new Error('No ID token received');
}
-
+
// Now use the ID token to sign in with Supabase
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'azure',
token: tokenResult.idToken,
});
-
+
+ // Check if profies table already has info (User is signing in, not signing up)
+ const { data: profileData, error: profileError } = await supabase
+ .from('profiles')
+ .select('*')
+ .eq('id', data.user?.id)
+ .single();
+
+ if (profileData.email === '' || !profileData.email && data.session?.user.email) {
+ const updateData: updateUser = {
+ email: data.session?.user.email ?? '',
+ };
+ const { error: updateAuthError } = await supabase.auth.updateUser({
+ data: updateData,
+ });
+ if (updateAuthError)
+ Alert.alert('Error updating auth info:', updateAuthError.message);
+ const { error: updateProfileError } = await supabase
+ .from('profiles')
+ .upsert({
+ id: data.session?.user.id ?? '',
+ email: data.session?.user.email ?? '',
+ provider: 'azure',
+ updated_at: new Date(),
+ });
+ if (updateProfileError)
+ Alert.alert('Error updating profile:', updateProfileError.message);
+ }
+
if (error) {
console.error('Supabase sign-in error:', error);
throw error;
}
-
+
console.log('Successfully signed in with Azure via Supabase');
return data;
} else {
@@ -114,16 +137,13 @@ const AzureSignIn = () => {
return (
-
+
- {loading ? "Signing in..." : "Sign in with Microsoft"}
+ {loading ? 'Signing in...' : 'Sign in with Microsoft'}
diff --git a/constants/Types.ts b/constants/Types.ts
index 56b0bbe..c07878a 100644
--- a/constants/Types.ts
+++ b/constants/Types.ts
@@ -1,3 +1,12 @@
+export type updateUser = {
+ id?: string;
+ updated_at?: Date;
+ email?: string;
+ full_name?: string;
+ avatar_url?: string;
+ provider?: string;
+};
+
export type NotificationMessage = {
sound?: string;
title: string;
diff --git a/services/PushNotificationManager.tsx b/services/PushNotificationManager.tsx
index c673189..6a85a3c 100644
--- a/services/PushNotificationManager.tsx
+++ b/services/PushNotificationManager.tsx
@@ -13,12 +13,15 @@ Notifications.setNotificationHandler({
}),
});
-export const sendPushNotification = async(expoPushToken: string | null, notification: NotificationMessage) => {
+export const sendPushNotification = async (
+ expoPushToken: string | null,
+ notification: NotificationMessage,
+) => {
if (!expoPushToken) {
Alert.alert('Error', 'No push token found.');
return;
}
- const message = {
+ const message = {
to: expoPushToken,
sound: notification.sound ?? 'default',
title: notification.title,
@@ -86,18 +89,20 @@ async function registerForPushNotificationsAsync() {
const PushNotificationManager = ({ children }: { children: React.ReactNode }) => {
const [expoPushToken, setExpoPushToken] = useState('');
- const [notification, setNotification] = useState(undefined);
+ const [notification, setNotification] = useState(
+ undefined,
+ );
const notificationListener = useRef();
const responseListener = useRef();
useEffect(() => {
- registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
+ registerForPushNotificationsAsync().then((token) => setExpoPushToken(token));
- notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
+ notificationListener.current = Notifications.addNotificationReceivedListener((notification) => {
setNotification(notification);
});
- responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
+ responseListener.current = Notifications.addNotificationResponseReceivedListener((response) => {
console.log(response);
// Handle notification response here
});
@@ -109,5 +114,5 @@ const PushNotificationManager = ({ children }: { children: React.ReactNode }) =>
}, []);
return <>{children}>;
-}
+};
export default PushNotificationManager;