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;