feat(edit-profile): allow switch gravar / Google avatar picture#1001
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a feature allowing users with Google OAuth accounts to toggle between using their Google profile picture and a Gravatar avatar as their profile image.
Changes:
- Added backend API endpoint
/users/current/avatar(POST) to handle avatar switching with appropriate user lookup and URL generation - Added frontend UI components (AvatarField) to display avatar selection toggle with contextual help links
- Updated avatar display logic throughout the application to use stored avatar URL with auth0Picture fallback, and removed the legacy
getAvatarUrlhelper function
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/models.d.ts | Added auth0Picture and auth0Id optional fields to User type |
| src/helpers/avatar.js | Removed legacy getAvatarUrl helper function |
| src/helpers/authProvider.ts | Added new helper to detect auth provider from auth0Id |
| src/components/MemberArea/model.js | Reorganized avatar field configuration, removed validation |
| src/components/MemberArea/PendingApplications.js | Updated to use avatar URL directly instead of helper function |
| src/components/MemberArea/MemberArea.js | Added avatar error handling with fallback to auth0Picture |
| src/components/MemberArea/EditProfile.js | Integrated avatar toggle functionality with new AvatarField component |
| src/components/MemberArea/AvatarField.tsx | New component providing avatar toggle UI for Google OAuth users |
| src/components/Card/Card.tsx | Removed getAvatarUrl usage, minor whitespace cleanup |
| src/api/index.ts | Added toggleAvatar API method |
| src/Me/Routes/Home/Avatar/Avatar.tsx | Integrated avatar toggle with Switch component and user feedback |
| src/Me/MentorshipRequests/UsersList.tsx | Updated to use avatar URL directly |
| netlify/functions-src/functions/users.ts | Added route for avatar toggle endpoint |
| netlify/functions-src/functions/modules/users/userInfo.ts | Improved error handling type safety |
| netlify/functions-src/functions/modules/users/toggleAvatar.ts | New handler implementing avatar toggle logic |
| netlify/functions-src/functions/modules/users/current.ts | Updated to return auth0Picture and prefer stored avatar |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| user: fromMtoVM(this.context.currentUser), | ||
| errors: [], | ||
| agree: false, | ||
| isUsingGravatar: this.context.currentUser?.avatar?.includes('gravatar.com') || false, |
There was a problem hiding this comment.
The logic for determining whether a user is using Gravatar relies on checking if the avatar URL contains 'gravatar.com'. However, this approach may not work correctly if the avatar URL is undefined or if a user switches from Google to Gravatar and the URL hasn't been updated yet. Consider checking the avatar URL against both the auth0Picture to determine which one is currently active, or storing the avatar preference explicitly in the database.
| return null; | ||
| } | ||
|
|
||
| const isUsingGravatar = currentUser.avatar?.includes('gravatar.com') || false; |
There was a problem hiding this comment.
The logic for determining whether a user is using Gravatar relies on checking if the avatar URL contains 'gravatar.com'. However, this approach may not work correctly if the avatar URL is undefined or if a user switches from Google to Gravatar and the URL hasn't been updated yet. Consider checking the avatar URL against both the auth0Picture to determine which one is currently active, or storing the avatar preference explicitly in the database.
| const isUsingGravatar = currentUser.avatar?.includes('gravatar.com') || false; | |
| const isUsingGravatar = | |
| !!currentUser.avatar && | |
| !!(currentUser as User).auth0Picture && | |
| currentUser.avatar !== (currentUser as User).auth0Picture; |
| return error('User not found', 404); | ||
| } | ||
|
|
||
| const avatarUrl = useGravatar ? getGravatarUrl(currentUser.email) : context.user?.picture; |
There was a problem hiding this comment.
When useGravatar is false, the avatar URL is set to context.user?.picture, which could potentially be undefined. This could result in setting the avatar to undefined in the database. Consider adding validation to ensure context.user?.picture exists before allowing the switch, or falling back to generating a Gravatar URL if it's missing.
| const avatarUrl = useGravatar ? getGravatarUrl(currentUser.email) : context.user?.picture; | |
| const avatarUrl = | |
| useGravatar || !context.user?.picture | |
| ? getGravatarUrl(currentUser.email) | |
| : context.user.picture; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const avatarUrl = avatarError && currentUser?.auth0Picture | ||
| ? currentUser.auth0Picture | ||
| : currentUser?.avatar || currentUser?.auth0Picture; | ||
|
|
There was a problem hiding this comment.
avatarUrl selection is based on avatarError, but avatarError is never reset. Once an image load fails, the component will keep preferring auth0Picture even after currentUser.avatar changes (e.g., user switches back to Gravatar), which can make the UI appear stuck. Consider resetting avatarError to false when the avatar source changes (e.g., via useEffect on currentUser.avatar) or on successful image load (onLoad).
| handleToggleGravatar = async (newValue) => { | ||
| const { updateCurrentUser } = this.context; | ||
| const { api } = this.props; | ||
|
|
||
| this.setState({ disabled: true }); | ||
| try { | ||
| report('Avatar', newValue ? 'use gravatar' : 'use google profile picture'); | ||
| const updatedUser = await api.toggleAvatar(newValue); | ||
| if (updatedUser) { |
There was a problem hiding this comment.
handleToggleGravatar can be triggered repeatedly while a previous request is in-flight (the disabled state is set, but the Switch component isn’t disabled and there’s no early return). This can send concurrent /users/current/avatar requests and leave isUsingGravatar out of sync with the server. Add a guard at the start (e.g., if this.state.disabled return) and/or wire the disabled state into the UI so the toggle can’t be spammed while saving.
| const avatarUrl = useGravatar ? getGravatarUrl(currentUser.email) : context.user?.picture; | ||
|
|
||
| const userDto: UserDto = new UserDto({ | ||
| _id: currentUser._id, | ||
| avatar: avatarUrl, | ||
| }); |
There was a problem hiding this comment.
avatarUrl can become undefined when useGravatar is false and context.user.picture is missing. That would overwrite the stored avatar with an empty value and may cause clients to render a broken/blank avatar. Handle the no-picture case explicitly (e.g., return 400, fall back to the current stored avatar, or fall back to a Gravatar URL) before calling upsertUser.
| import styled from 'styled-components/macro'; | ||
| import { useUser } from '../../../../context/userContext/UserContext'; | ||
| import type { User } from '../../../../types/models'; | ||
| import Camera from '../../../../assets/me/camera.svg'; |
There was a problem hiding this comment.
There are unused imports here (useEffect and User). In a TS build with noUnusedLocals/noUnusedParameters enabled, this will fail the build/lint. Remove the unused imports or use them.
| import Camera from '../../../../assets/me/camera.svg'; |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Changes
This pull request introduces a new feature that allows users to toggle between using their Google profile picture and a Gravatar image as their avatar.
Backend changes:
/users/current/avatar(POST) to allow users to switch between their Google profile picture and Gravatar avatar. The endpoint updates the user's avatar URL in the database and returns the updated user object. [1] [2] [3]Frontend changes:
AvatarFieldand updates toAvatar.tsxandEditProfile.js) to let users toggle between Gravatar and Google profile avatars, including a switch and contextual help links. [1] [2] [3] [4] [5] [6] [7]getAvatarUrlhelper, updating all avatar references to use the direct avatar URL from the user object. [1] [2] [3] [4] [5] [6]UI consistency and fallback improvements:
Demo
Screen.Recording.2026-01-29.at.0.52.52.mov