Skip to content

Gravatar avatars with colored initials in channel views#3657

Merged
trunk-io[bot] merged 6 commits into
mainfrom
posthog-code/channel-gravatar-avatars
Jul 22, 2026
Merged

Gravatar avatars with colored initials in channel views#3657
trunk-io[bot] merged 6 commits into
mainfrom
posthog-code/channel-gravatar-avatars

Conversation

@raquelmsmith

@raquelmsmith raquelmsmith commented Jul 21, 2026

Copy link
Copy Markdown
Member

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 ProfilePicture resolves hedgehog → Gravatar → initials. We mirror the Gravatar + initials part here, frontend-only (no backend changes — the message author already carries an email).

PostHog Code (Development) 2026-07-21 17 11 31

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 inline Avatar/AvatarFallback pattern in ActivityView, ThreadPanel, and ChannelFeedView. Agent/system rows keep the robot icon.
  • useGravatarUrl: builds the Gravatar URL by hashing the email with the built-in Web Crypto SHA-256 (Gravatar supports it), requested with d=404 so 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 --noEmit and pnpm --filter @posthog/ui exec tsc --noEmit — both clean.
  • Vitest: new avatarColorClass (3) and useGravatarUrl (3) tests pass; existing ThreadPanel tests (7) still pass.
  • Biome check clean on all changed files.
  • Not yet exercised in the running app — please pull down and try a channel with real teammate emails.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code

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
@trunk-io

trunk-io Bot commented Jul 21, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 1be46bd.

@raquelmsmith
raquelmsmith marked this pull request as ready for review July 21, 2026 19:43
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`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix 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

@veria-ai

veria-ai Bot commented Jul 21, 2026

Copy link
Copy Markdown

PR overview

This 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

Comment thread packages/ui/src/features/auth/useGravatarUrl.ts
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
@raquelmsmith raquelmsmith added the Stamphog This will request an autostamp by stamphog on small changes label Jul 22, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@stamphog stamphog Bot removed the Stamphog This will request an autostamp by stamphog on small changes label Jul 22, 2026
@raquelmsmith
raquelmsmith requested a review from a team July 22, 2026 00:11
Comment thread packages/ui/src/features/auth/useGravatarUrl.ts
raquelmsmith and others added 2 commits July 21, 2026 19:50
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
@trunk-io
trunk-io Bot merged commit ecbae80 into main Jul 22, 2026
32 checks passed
@trunk-io
trunk-io Bot deleted the posthog-code/channel-gravatar-avatars branch July 22, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants