Gravatar avatars with colored initials in channel views#3657
Conversation
Channel/thread/activity message avatars were initials-only in a single neutral color. Add a shared UserAvatar that resolves a Gravatar from the author's email (SHA-256 hash, d=404 so a missing Gravatar falls through) and falls back to initials tinted with a deterministic per-user color, so people are visually distinguishable at a glance. - avatarColorClass: deterministic seed -> Radix palette class (core); reviewerAvatarToneClass now delegates to it. - useGravatarUrl: Web Crypto SHA-256, no new hashing dependency. - UserAvatar: quill Avatar + AvatarImage(gravatar) + colored AvatarFallback. - Swap human-author call sites in ActivityView, ThreadPanel, ChannelFeedView; agent/system rows keep the robot icon. Generated-By: PostHog Code Task-Id: 03a1a20f-7597-4267-bf73-0ef6224e71e7
|
😎 Merged successfully - details. |
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
| const hash = Array.from(new Uint8Array(digest)) | ||
| .map((b) => b.toString(16).padStart(2, "0")) | ||
| .join(""); | ||
| return `https://www.gravatar.com/avatar/${hash}?s=96&d=404`; |
There was a problem hiding this comment.
Medium: User data disclosed to Gravatar
Opening a channel or thread now sends Gravatar an unsalted, deterministic hash for every displayed author's email, along with the viewer's IP and request timing. Gravatar can match likely organization addresses by hashing them, and d=404 still makes the request for users without an avatar; use an authenticated first-party image proxy/cache or require explicit opt-in before contacting Gravatar.
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
packages/ui/src/features/auth/useGravatarUrl.ts:26-30
**Previous User Avatar Persists**
When a mounted avatar changes from one user to another, `url` still contains the first user's Gravatar while the second email is being hashed. React can therefore show the wrong person's photo until the new digest finishes; clear the URL when each new email starts processing so initials remain visible during the transition.
```suggestion
let cancelled = false;
setUrl(undefined);
gravatarUrlForEmail(email)
.then((next) => {
if (!cancelled) setUrl(next);
})
```
Reviews (1): Last reviewed commit: "Add Gravatar avatars with colored initia..." | Re-trigger Greptile |
PR overviewThis pull request adds avatar rendering in channel and thread views using Gravatar, with colored initials as a fallback for users without an avatar image. There is one open privacy issue: viewing channels or threads can cause requests to Gravatar for displayed authors, exposing deterministic email-derived identifiers along with the viewer’s IP address and request timing to a third party. The concern is still unresolved, so the PR would benefit from a first-party proxy/cache or explicit opt-in before contacting Gravatar. Open issues (1)
Fixed/addressed: 0 · PR risk: 6/10 |
The per-user color was applied as a Tailwind `bg-(--x-9)` class built in @posthog/core, but the app's Tailwind content globs only scan packages/ui and apps/*, not packages/core — so those utilities were never generated and every bubble fell back to the neutral gray default. Return a CSS variable (var(--x-9)) instead and apply it via inline style on the AvatarFallback, which doesn't depend on Tailwind class generation. Revert the reviewer-avatar helper to its original standalone form (it has the same latent scanning limitation but is out of scope here). Generated-By: PostHog Code Task-Id: 03a1a20f-7597-4267-bf73-0ef6224e71e7
Port the 16 background/text color pairs and the index%16 selection from
PostHog's Lettermark. Each background ships with a paired text color chosen
for contrast, and the values are theme-independent, so initials stay legible
in both light and dark mode. avatarColor(seed) now returns {bg, text}, applied
together as inline styles on the fallback.
Generated-By: PostHog Code
Task-Id: 03a1a20f-7597-4267-bf73-0ef6224e71e7
A reused UserAvatar whose email prop changed kept the previous person's Gravatar URL until the new email's hash resolved, briefly showing the wrong photo. Clear the URL at the start of each hash so initials show during the transition. Adds a regression test. Generated-By: PostHog Code Task-Id: 03a1a20f-7597-4267-bf73-0ef6224e71e7
There was a problem hiding this comment.
Gates denied this PR because it touches auth and is classified as a complex change requiring human review. Additionally, the Gravatar integration sends unsalted SHA-256 hashes of user emails to an external third party (gravatar.com) on every channel/thread render without user opt-in — a real privacy concern that needs a deliberate product decision before shipping.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
A suggestion applied to the branch left two `let cancelled = false;` declarations in the same block, which fails to compile and broke the useGravatarUrl tests. Drop the duplicate. Generated-By: PostHog Code Task-Id: 03a1a20f-7597-4267-bf73-0ef6224e71e7
Problem
In the Channels / Website space (project-bluebird), every person's message showed a plain initials bubble in one neutral color — people didn't stand out from each other, and there was no photo at all.
Why: avatars are a bigger deal in a multiplayer surface. This is the first step (Gravatar + colored initials); uploaded profile photos are a planned follow-up.
I researched how the main PostHog app does it: a single
ProfilePictureresolves hedgehog → Gravatar → initials. We mirror the Gravatar + initials part here, frontend-only (no backend changes — the message author already carries an email).Changes
UserAvatar(packages/ui/src/features/auth): one shared component that shows a Gravatar when the author has one and otherwise a colored initials bubble. Replaces the repeated inlineAvatar/AvatarFallbackpattern inActivityView,ThreadPanel, andChannelFeedView. Agent/system rows keep the robot icon.useGravatarUrl: builds the Gravatar URL by hashing the email with the built-in Web CryptoSHA-256(Gravatar supports it), requested withd=404so a missing Gravatar cleanly falls back to initials. No new dependency.avatarColorClass(packages/core/src/auth): a deterministic seed→color helper so each person gets a stable, distinct hue everywhere. Generalized from the existing reviewer-avatar palette, which now delegates to it.How did you test this?
pnpm --filter @posthog/core exec tsc --noEmitandpnpm --filter @posthog/ui exec tsc --noEmit— both clean.avatarColorClass(3) anduseGravatarUrl(3) tests pass; existingThreadPaneltests (7) still pass.Automatic notifications
Created with PostHog Code