-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(feats): Initialize database, sign-in/sign-out, header (#3)
Please include a summary of the changes and the related issue. ## Type of change - [ ] Bug fix - [ ] New feature - [ ] Code refactor - [ ] Breaking change - [ ] Style update - [ ] Documentation update ## Checklist - [ ] I have performed a self-review of my code - [ ] Single file within 200 lines of codes - [ ] Comments has added - [ ] My changes generate no new warnings --------- Co-authored-by: WeiViv <[email protected]>
- Loading branch information
1 parent
0918e1a
commit fb92881
Showing
6 changed files
with
197 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,63 @@ | ||
import { AppBar, Avatar, Box, Button, IconButton, Toolbar, Typography } from '@mui/material'; | ||
import { handleSignIn, handleSignOut } from '@utils/auth'; | ||
import { auth } from '@utils/firebaseConfig'; | ||
import { useEffect, useState } from 'react'; | ||
import { useNavigate } from 'react-router-dom'; | ||
|
||
const Header = () => { | ||
const [user, setUser] = useState(null); | ||
const navigate = useNavigate(); | ||
|
||
useEffect(() => { | ||
const unsubscribe = auth.onAuthStateChanged((user) => { | ||
setUser(user); | ||
}); | ||
return () => unsubscribe(); | ||
}, []); | ||
|
||
const handleAuthClick = async () => { | ||
if (user) { | ||
await handleSignOut(navigate); | ||
} else { | ||
await handleSignIn(); | ||
} | ||
}; | ||
|
||
return ( | ||
<AppBar position="sticky" sx={{ backgroundColor: 'primary.light', color: '#000' }}> | ||
<Toolbar sx={{ position: 'relative', justifyContent: 'space-between' }}> | ||
<Box sx={{ display: 'flex', alignItems: 'center' }}> | ||
{/* Placeholder Box for back button if needed in the future */} | ||
<Box sx={{ width: '48px' }} /> | ||
</Box> | ||
|
||
<Typography | ||
variant="h6" | ||
sx={{ | ||
position: 'absolute', | ||
left: '50%', | ||
transform: 'translateX(-50%)', | ||
fontWeight: '600', | ||
fontSize: '1.4rem', | ||
}} | ||
> | ||
Stepwise | ||
</Typography> | ||
|
||
<Box sx={{ display: 'flex', alignItems: 'center' }}> | ||
{user ? ( | ||
<IconButton edge="end" color="inherit" onClick={handleAuthClick}> | ||
<Avatar alt={user.displayName} src={user.photoURL} /> | ||
</IconButton> | ||
) : ( | ||
<Button color="inherit" onClick={handleAuthClick}> | ||
Sign In | ||
</Button> | ||
)} | ||
</Box> | ||
</Toolbar> | ||
</AppBar> | ||
); | ||
}; | ||
|
||
export default Header; |
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 |
---|---|---|
@@ -1,5 +1,12 @@ | ||
import Header from '@components/common/Header'; | ||
|
||
const Home = () => { | ||
return <h1>Home</h1>; | ||
return ( | ||
<div> | ||
<Header /> | ||
<h1>Home</h1> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Home; |
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,47 @@ | ||
import { createFirstUserProfile, fetchUserProfile } from '@utils/firebase/createUserProfile'; | ||
import { auth } from '@utils/firebaseConfig'; | ||
import { GoogleAuthProvider, signInWithPopup, signOut } from 'firebase/auth'; | ||
|
||
const provider = new GoogleAuthProvider(); | ||
|
||
// Google Sign-In function | ||
const signInWithGoogle = async () => { | ||
try { | ||
const result = await signInWithPopup(auth, provider); | ||
return result.user; | ||
} catch (error) { | ||
console.error('Error during sign-in:', error); | ||
return null; | ||
} | ||
}; | ||
|
||
// Handle Sign-In | ||
// Returns true if the user already exists in the database, false if a new profile was created | ||
export const handleSignIn = async () => { | ||
const user = await signInWithGoogle(); | ||
|
||
if (user) { | ||
// Try to fetch the existing user profile | ||
const { profile } = await fetchUserProfile(user.uid); | ||
|
||
// If no profile found, create one with default data | ||
if (!profile) { | ||
await createFirstUserProfile(user); | ||
return false; // New user profile created | ||
} | ||
return true; // User profile already exists | ||
} | ||
return false; // Sign-in failed or cancelled | ||
}; | ||
|
||
// Handle Sign-Out | ||
// Redirects to homepage after sign-out | ||
export const handleSignOut = async (navigate) => { | ||
try { | ||
await signOut(auth); | ||
console.log('Sign out successful'); | ||
navigate('/'); | ||
} catch (error) { | ||
console.error('Error during sign-out:', error); | ||
} | ||
}; |
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,77 @@ | ||
import { db } from '@utils/firebaseConfig'; | ||
import { doc, getDoc, setDoc, updateDoc } from 'firebase/firestore'; | ||
|
||
// Unified function to fetch and listen to user profile changes by UID | ||
// (supports both regular and transaction-based fetches) | ||
export const fetchUserProfile = async (uid, transaction = null) => { | ||
try { | ||
const userRef = doc(db, 'users', uid); | ||
|
||
// If transaction is provided, use it to fetch the document | ||
const userSnapshot = transaction ? await transaction.get(userRef) : await getDoc(userRef); | ||
|
||
if (!userSnapshot.exists()) { | ||
console.error(`User profile for ${uid} does not exist`); | ||
return { ref: userRef, profile: null }; // Return consistent format with null profile | ||
} | ||
|
||
const profile = userSnapshot.data(); | ||
return { ref: userRef, profile }; | ||
} catch (error) { | ||
console.error('Error fetching user profile:', error); | ||
return { ref: null, profile: null }; | ||
} | ||
}; | ||
|
||
// Check or create user profile in Firestore (uses fetchUserProfile to streamline code) | ||
export const createFirstUserProfile = async (user) => { | ||
try { | ||
const { uid, photoURL, displayName } = user; | ||
const defaultProfile = { | ||
uid, | ||
profilePic: photoURL || '', | ||
name: displayName || '', | ||
goals: [ | ||
{ | ||
name: '', | ||
progress: 0, | ||
microgoals: [ | ||
{ | ||
name: '', | ||
progress: 0, | ||
tasks: [{ name: '', completed: false }], | ||
}, | ||
], | ||
}, | ||
], | ||
streak: [], | ||
}; | ||
|
||
// If the profile does not exist, create it with the default data | ||
await setDoc(doc(db, 'users', uid), defaultProfile); | ||
console.warn('New user profile created with default data.'); | ||
|
||
return true; | ||
} catch (error) { | ||
console.error('Error checking or creating user profile:', error); | ||
return false; // Return false if an error occurs | ||
} | ||
}; | ||
|
||
// Get user profile by UID | ||
// (simple wrapper around fetchUserProfile for non-transaction use) | ||
export const getUserProfile = async (uid) => { | ||
const fetchedUser = await fetchUserProfile(uid); | ||
return fetchedUser ? fetchedUser.profile : null; // Return only the profile data | ||
}; | ||
|
||
// Update user profile by UID | ||
export const updateUserProfile = async (uid, updates) => { | ||
try { | ||
const userDocRef = doc(db, 'users', uid); | ||
await updateDoc(userDocRef, updates); | ||
console.warn('User profile updated'); | ||
} catch (error) { | ||
console.error('Error updating user profile:', error); | ||
} | ||
}; |