-
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3206 from KBVE/patch-atomic-preparing-the-expo-pr…
…ofile-update-11-10-2024-1731253886 [CI] Merge patch-atomic-preparing-the-expo-profile-update-11-10-2024-1731253886 into dev
- Loading branch information
Showing
5 changed files
with
304 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import React, { useMemo, useCallback, useEffect, useState, useRef } from 'react'; | ||
import { YStack, XStack, SizableText, Separator, ScrollView, Button } from 'tamagui'; | ||
import { TamaOnboard, LottieAnimation, TamaSheet, useBBQ } from '@kbve/expo-bbq'; | ||
import { useNavigation } from 'expo-router'; | ||
|
||
const Onboard = () => { | ||
|
||
// [C ~~> Nav[]] | ||
const navigation = useNavigation(); | ||
const router = useBBQ(); | ||
// [Sheet] | ||
const sheetRef = useRef<{ | ||
showSheet: () => void; | ||
hideSheet: () => void; | ||
} | null>(null); | ||
|
||
// [States] | ||
const [sheetMessage, setSheetMessage] = useState(''); | ||
const [isComplete, setIsComplete] = useState(false); | ||
const [hasError, setHasError] = useState(false); | ||
|
||
// [Lottie] => (Memo) -> JSON animation for the onboarding screen, since its a static asset. | ||
const lottieOnboardAnimation = useMemo( | ||
() => require('../../assets/json/360-vr.json'), | ||
[], | ||
); | ||
|
||
// [Navigation] => (useCallback) <~> (useEffect) | ||
const updateNavigationOptions = useCallback(() => { | ||
navigation.setOptions({ | ||
title: 'Onboarding', | ||
headerBackTitle: 'Back', | ||
}); | ||
|
||
}, [navigation]); | ||
|
||
useEffect( | ||
() => { | ||
updateNavigationOptions(); | ||
}, [updateNavigationOptions] | ||
); | ||
|
||
// [onSuccess] ?=> | ||
const handleSuccess = () => { | ||
setIsComplete(true); | ||
setHasError(false); | ||
// [i18n] ?? NULL => TamaI18N('[onboard_true']); ($->trigger) | ||
setSheetMessage('Onboarding completed successfully!'); | ||
if(sheetRef.current) { | ||
sheetRef.current.showSheet(); | ||
} | ||
}; | ||
|
||
// [onError] ?=> | ||
const handleError = (error: string) => { | ||
setIsComplete(false); | ||
setHasError(true); | ||
// [i18n] ?? NULL => TamaI18N('[onboard_error']); ($->trigger)[error:key] | ||
if(sheetRef.current) { | ||
sheetRef.current.showSheet(); | ||
} | ||
}; | ||
|
||
|
||
const renderSheetContent = () => { | ||
return ( | ||
<YStack ai="center" jc="center" paddingVertical={10}> | ||
<SizableText>{sheetMessage}</SizableText> | ||
{isComplete && !hasError && ( | ||
<Button | ||
onPress={() => { | ||
if (sheetRef.current) { | ||
sheetRef.current.hideSheet(); | ||
} | ||
router.go('/profile'); // Navigate to the profile page after successful onboarding | ||
}} | ||
color="green" | ||
size="$4" | ||
marginTop={10} | ||
> | ||
Go to Profile | ||
</Button> | ||
)} | ||
</YStack> | ||
); | ||
}; | ||
|
||
const MemoizedLottieAnimation = React.memo(LottieAnimation); | ||
|
||
return ( | ||
<ScrollView contentContainerStyle={{ flexGrow: 1 }}> | ||
<YStack f={1} jc="center" ai="center" padding="$4"> | ||
<SizableText size="$4" theme="alt2">Welcome to Onboarding</SizableText> | ||
<MemoizedLottieAnimation | ||
lottieJSON={lottieOnboardAnimation} | ||
style={{ | ||
width: 150, | ||
height: 'auto', | ||
aspectRatio: 1, | ||
maxWidth: 800, | ||
}} | ||
/> | ||
<TamaOnboard | ||
supabaseUrl="https://supabase.kbve.com" | ||
supabaseAnonKey="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJyb2xlIjogImFub24iLAogICJpc3MiOiAic3VwYWJhc2UiLAogICJpYXQiOiAxNzI0NTM2ODAwLAogICJleHAiOiAxODgyMzAzMjAwCn0._fmEmblm0afeLoPXxt8wP2mYpa9gzU-ufx3v8oRTFGg" | ||
onSuccess={handleSuccess} | ||
onError={handleError} | ||
/> | ||
</YStack> | ||
|
||
<TamaSheet ref={sheetRef} title="Onboarding Status"> | ||
{renderSheetContent()} | ||
</TamaSheet> | ||
</ScrollView> | ||
); | ||
|
||
}; | ||
|
||
export default Onboard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 174 additions & 0 deletions
174
packages/expo-bbq/src/components/auth/user/TamaOnboard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import React, { useState, useCallback, useMemo, useEffect } from 'react'; | ||
import { YStack, Input, Button, TextArea, Text } from 'tamagui'; | ||
import { createSupabaseClient } from '../../wrapper/Supabase'; | ||
|
||
export function TamaOnboard({ | ||
supabaseUrl, | ||
supabaseAnonKey, | ||
onSuccess, | ||
onError, | ||
}: { | ||
supabaseUrl: string; | ||
supabaseAnonKey: string; | ||
onSuccess?: () => void; | ||
onError?: (error: string) => void; | ||
}) { | ||
|
||
// [States] | ||
const [step, setStep] = useState(1); | ||
const [username, setUsername] = useState(''); | ||
const [bio, setBio] = useState(''); | ||
const [socials, setSocials] = useState(''); | ||
const [style, setStyle] = useState(''); | ||
const [loading, setLoading] = useState(true); | ||
|
||
|
||
// [Supabase] | ||
const supabase = useMemo( | ||
() => createSupabaseClient(supabaseUrl, supabaseAnonKey), | ||
[supabaseUrl, supabaseAnonKey], | ||
); | ||
|
||
// [User] -> Memoize the user object. | ||
// const user = useMemo(async () => { | ||
// return (await supabase.auth.getUser())?.data.user; | ||
// }, [supabase]); | ||
|
||
|
||
// Check if username is already set | ||
useEffect(() => { | ||
const checkExistingUsername = async () => { | ||
try { | ||
const user = (await supabase.auth.getUser())?.data.user; | ||
if (!user) throw new Error('User data not found'); | ||
|
||
const { data, error } = await supabase | ||
.from('user_profiles') | ||
.select('username') | ||
.eq('id', user.id) | ||
.single(); | ||
|
||
if (error) throw new Error(error.message); | ||
|
||
// If username exists, skip to step 2 | ||
if (data?.username) { | ||
setStep(2); | ||
} | ||
} catch (err) { | ||
if (onError) onError('Failed to check username. Please try again.'); | ||
console.error(err); | ||
} finally { | ||
setLoading(false); | ||
} | ||
}; | ||
|
||
checkExistingUsername(); | ||
}, [supabase, onError]); | ||
|
||
// Handler to submit the username | ||
const handleUsernameSubmit = useCallback(async () => { | ||
try { | ||
const user = (await supabase.auth.getUser())?.data.user; | ||
if (!user) throw new Error('User data not found'); | ||
|
||
// Lowercase the username for consistency | ||
const lowercasedUsername = username.toLowerCase(); | ||
|
||
|
||
// Call the stored function to handle the new user profile | ||
const { data, error } = await supabase | ||
.rpc('create_user_profile', { _username: lowercasedUsername }); | ||
|
||
|
||
if (error) throw new Error(JSON.stringify(error)); | ||
|
||
// Check if data was returned to confirm successful update | ||
if (data && data.length > 0) { | ||
setStep(2); // Move to the next step for bio/socials | ||
} else { | ||
throw new Error('Update failed, no data returned'); | ||
} | ||
} catch (err) { | ||
if (onError) onError('Failed to set username. Please try again.'); | ||
console.error(err); | ||
} | ||
}, [username, supabase, onError]); | ||
|
||
// Handler to submit the user card details | ||
const handleUserCardSubmit = useCallback(async () => { | ||
try { | ||
const user = (await supabase.auth.getUser())?.data.user; | ||
if (!user) throw new Error('User data not found'); | ||
|
||
// Prepare the payload, excluding `socials` and `style` if they're empty | ||
const payload: Record<string, any> = { id: user.id, bio }; | ||
if (socials) payload.socials = socials; | ||
if (style) payload.style = style; | ||
|
||
// Use upsert to insert or update the row | ||
const { data, error } = await supabase | ||
.from('user_cards') | ||
.upsert(payload, { onConflict: 'id' }) // Handle conflicts based on the user's id | ||
.select(); | ||
|
||
if (error) throw new Error(JSON.stringify(error)); | ||
if (!data || data.length === 0) throw new Error('Failed to save user card details'); | ||
|
||
if (onSuccess) onSuccess(); | ||
} catch (err) { | ||
if (onError) onError('Failed to save user card details. Please try again.'); | ||
console.error(err); | ||
} | ||
}, [bio, socials, style, supabase, onSuccess, onError]); | ||
|
||
|
||
if (loading) { | ||
return <Text>Loading...</Text>; | ||
} | ||
|
||
return ( | ||
<YStack | ||
padding="$4" | ||
alignItems="center" | ||
justifyContent="center" | ||
flex={1} | ||
gap={2}> | ||
{step === 1 ? ( | ||
<> | ||
<Text>Please choose your public username:</Text> | ||
<Input | ||
value={username} | ||
onChangeText={setUsername} | ||
placeholder="Username" | ||
/> | ||
|
||
<Button onPress={handleUsernameSubmit}>Next</Button> | ||
</> | ||
) : ( | ||
<> | ||
<Text>Tell us more about yourself:</Text> | ||
<TextArea | ||
value={bio} | ||
onChangeText={setBio} | ||
placeholder="Write a short bio" | ||
/> | ||
<Input | ||
value={socials} | ||
onChangeText={setSocials} | ||
placeholder="Your social links" | ||
/> | ||
<Input | ||
value={style} | ||
onChangeText={setStyle} | ||
placeholder="Preferred style" | ||
/> | ||
<Button onPress={handleUserCardSubmit}> | ||
Complete Onboarding | ||
</Button> | ||
</> | ||
)} | ||
</YStack> | ||
); | ||
} | ||
|
||
export default TamaOnboard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters