COMP_SCI 392 - Fall 2024 - Northwestern University
This project uses a component-based structure with a focus on clear separation of concerns. Key files and folders:
.
├── LICENSE
├── README.md # Project documentation and usage guide
├── prettier.config.cjs # Prettier configuration file
├── eslint.config.mjs # Eslint configuration file
├── vite.config.js # Vite configuration file
├── jsconfig.json # Javascript config file
├── firebase.json # Firebase configuration for hosting
├── package.json # Dependencies
└── src # Source code
├── components # Shared components and features
│ └── common # Common components used across the app
| └── Home # Home page components
| └── Streak # Streak page components
├── contexts # Contexts for managing global app state
│ └── UserContext.jsx # Provides user authentication data globally
├── hooks # Custom hooks for specialized logic
│ └── useGaolsUpdater # All goals related hooks
├── pages # Application pages
├── utils # Utility functions and Firebase configurations
└── styles # Styles.
The main components and utilities are organized under src/components
and src/utils
, while UserContext
manages user state globally to avoid redundant data fetching.
-
Why: Avoid developing directly on
main
. Keepingmain
in sync withorigin/main
makes it easier to update and manage changes. -
How: Create and switch to a new branch for your feature, and remember to push it to
origin
:git switch -c feat/new-feature-name git push -u origin feat/new-feature-name
-
Switch Back to
main
: Ensure you're onmain
before updating:git switch main
-
Stash Your Work(if needed): If you have uncommitted changes, stash them to avoid conflicts while pulling:
git stash
-
Pull Latest Changes: Bring in the latest updates from
origin/main
:git pull origin main
-
Switch Back to Your Feature Branch:
git switch feat/new-feature-name
-
Rebase: Apply your feature branch changes on top of the latest
main
:git rebase main
-
Apply Stash(if you stashed changes): Reapply your saved changes once main is updated:
git stash pop
-
Resolve Conflicts (if any): If conflicts occur, Git will prompt you to resolve them. After resolving, use:
git add <conflicted-files> git rebase --continue
-
Push Changes:
-
If you have NOT previously pushed code to the remote:
git push
-
If you HAVE previously pushed code (with conflicting changes), you may need to force-push to align with the rebased history. (Do NOT use this on
main
)git push --force-with-lease
-
By following these steps, you ensure that main
remains in sync with origin/main
, while your feature branch incorporates the latest updates without directly modifying main
. This keeps your work organized and minimizes conflict risks.
Context is a React feature that enables data sharing across multiple components without needing to pass props manually at every level. It's especially useful for managing global states, like user authentication data, that are needed by many components.
In our previous StudyBuddy
project, we relied on hooks to fetch user data directly from Firebase. This led to extremely high read counts (thousands of reads per second) whenever users navigated across components, quickly exceeding Firebase’s free limits and degrading performance. By centralizing user data with UserContext
, data is fetched only once per session and remains available globally, reducing Firebase reads and data-fetching costs.
-
Enable Global UserContext:
UserProvider
has already been implemented to wrap the main application inApp.jsx
. This enablesUserContext
throughout the app.import { UserProvider } from '@contexts/UserContext' const App = () => <UserProvider>{/* App Components */}</UserProvider>
-
Access User Data in Components: Use the
useUser
hook to access user data (anything inside their profile you can all access just by e.g.user.goals
) and authentication functions in any component:import { useUser } from '@contexts/UserContext' const MyComponent = () => { { /* You can decide what to use from `useUser` */ } const { user, loading, handleSignIn, handleSignOut } = useUser() return user ? ( <div> <h1>Welcome, {user.displayName}</h1> <button onClick={handleSignOut}>Sign Out</button> </div> ) : ( <button onClick={handleSignIn}>Sign In</button> ) }
Use the updateProfile
function to update user profile information:
const { updateProfile } = useUser()
updateProfile({ displayName: 'New Name' })
The updateProfile
function accepts an object with updated user fields and syncs them with both the context and Firebase, ensuring efficient data sharing across components and reducing the need for direct database access.
The useGoalsUpdater
custom hook provides functions to manage user goals, microgoals, and tasks by:
- updating the user's profile, including both goals and (optionally) the streak,
- adding items (goal, microgoal, task),
- deleting items (goal, microgoal, task),
- toggling completion status for tasks with streak updates, and
- expanding or collapsing goals and microgoals.
This hook enables efficient management of goal-related actions across components, reducing repetitive code and ensuring consistent updates.
useUser
: A custom context hook for accessing and updating the user's profile information, including user goals.
- addGoal: Adds a new goal with a specified name, and category (HEX color code).
addGoal('New Goal Name', category)
- addMicrogoal: Adds a new microgoal within a specified goal.
addMicrogoal(goalIndex, 'New Microgoal Name')
- addTask: Adds a new task within a specified microgoal with optional dueDate.
addTask(goalIndex, microGoalIndex, 'New Task Name', dueDate?)
- deleteGoal: Deletes a goal.
deleteGoal(goalIndex)
- deleteMicrogoal: Deletes a microgoal within a specified goal.
deleteMicrogoal(goalIndex, microGoalIndex)
- deleteTask: Deletes a task within a specified microgoal.
deleteTask(goalIndex, microGoalIndex, taskIndex)
- toggleTaskCompletion: Toggles the completion status of a task and updates the streak accordingly.
toggleTaskCompletion(goalIndex, microGoalIndex, taskIndex)
- toggleGoalExpansion: Toggles the expansion state of a goal.
toggleGoalExpansion(goalIndex)
- toggleMicroGoalExpansion: Toggles the expanded/collapsed state of a microgoal within a specified goal.
toggleMicroGoalExpansion(goalIndex, microGoalIndex)
These functions directly update the UserContext
, ensuring consistent data and logging status messages for clear feedback on each action performed.