Skip to content

Commit

Permalink
(feats): Initialize database, sign-in/sign-out, header (#3)
Browse files Browse the repository at this point in the history
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
marycaserio and WeiViv authored Oct 27, 2024
1 parent 0918e1a commit fb92881
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@mui/icons-material": "^6.1.5",
"@mui/material": "^6.1.5",
"firebase": "^11.0.1",
"react": "^18.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.0",
"react-router-dom": "^6.27.0"
},
Expand Down
63 changes: 63 additions & 0 deletions src/components/common/Header.jsx
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;
9 changes: 8 additions & 1 deletion src/pages/Home.jsx
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;
47 changes: 47 additions & 0 deletions src/utils/auth.js
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);
}
};
77 changes: 77 additions & 0 deletions src/utils/firebase/createUserProfile.js
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);
}
};

0 comments on commit fb92881

Please sign in to comment.