fix(desktop): give the shared emoji picker a panel, and open the inbox on unread - #27
Merged
Merged
Conversation
…x on unread Two independent user reports, both in the desktop app. **The emoji picker had no background and swallowed the wheel.** `EmojiPicker` renders emoji-mart into a bare unclassed `<div>`. Three things that make the picker usable live elsewhere and were never applied here: the `buzz-emoji-mart` surface class, `useEmojiMartThemeVars()` (which supplies `--rgb-background`), and `useEmojiMartStyles()` (which injects the shadow-root CSS *and* installs the wheel-scroll handler). Only `ProfileAvatarEditor` and `AgentCreationPreview` had them, because those render emoji-mart directly — so the component that exists to be "the one emoji picker" was the only one without. Measured, not assumed: unset `--rgb-background` does not make the panel transparent, it drops emoji-mart onto its own `theme="auto"` palette, which follows `prefers-color-scheme` instead of Buzz's theme class — a white panel on a white app, inside popovers that carry `bg-transparent border-0 shadow-none` precisely because they assume the picker paints itself. And a wheel event over a shadow-root list is retargeted to the host, so without the handler the list is stuck on its first category. `className` is now an override rather than the whole style, so the two callers that already size the picker keep working. **The inbox reset to "all" on every launch.** `unreadOnly` was `React.useState(false)`. The inbox is a queue: it should open on what is still owed. New `useInboxUnreadOnly` defaults to unread-only and remembers the last choice in `localStorage` — not `sessionStorage`, because which mail you consider outstanding is not a per-window layout preference. Anything but the exact string `"false"` reads as the default, so a corrupt value can never silently hide unread mail. Flipping the default broke six specs that assert on specific fixture rows (most of which are read). Rather than rewrite them or fake a different default, the bridge helper now seeds the preference as explicitly off — the state of a user who already switched it — with `seedInboxUnreadOnly: false` to opt out. One new spec opts out and covers the shipped default. Tests: `useInboxUnreadOnly.test.mjs` (6), `emoji-picker-surface.spec.ts` (3, two of which were proven red before the fix), `inbox-unread-default.spec.ts` (3).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two unrelated desktop reports: "the emoji/reactions panel has no background and doesn't scroll", and "the inbox should open on unread".
The emoji picker
EmojiPickerrenders emoji-mart into a bare unclassed<div>. Three things that make the picker usable live in.buzz-emoji-mart+useEmojiMartThemeVars+useEmojiMartStyles, and onlyProfileAvatarEditorandAgentCreationPreviewapplied them — those render emoji-mart directly. So the component that exists to be the emoji picker was the only one without a surface.Measured before the fix rather than assumed:
--rgb-backgroundunset does not make the panel transparent. It drops emoji-mart onto its owntheme="auto"palette, which followsprefers-color-schemeand not Buzz's theme class. Probed live:rgb(255, 255, 255)on a white app, inside popovers that carrybg-transparent border-0 shadow-nonebecause they assume the picker paints itself. That is the "no background"..scrollon its own.useEmojiMartStylesinstalls a non-passive handler on it; without that the list is stuck on its first category. This is the half the original report called "doesn't scroll".w-[352px]on the host was already dead code — unlayered.buzz-emoji-mart { width: 100% }beats a layered Tailwind utility regardless of specificity. Removed withdynamicWidth; re-probed identical.classNamebecame an override on top of the surface classes, so the two call sites that size the picker keep working.The inbox
unreadOnlywasReact.useState(false)— every launch reset to "all", so the one unhandled thing had to be found by reading past everything already handled.New
useInboxUnreadOnly: defaults to unread-only, remembers the last choice inlocalStorage. NotsessionStorage(the list width next door is a per-window layout preference; which mail is outstanding is not). Anything but the exact string"false"reads as the default, so a corrupt value can never silently hide unread mail.Flipping the default broke six specs that assert on specific fixture rows, most of which are read. Instead of rewriting them or faking a different default in tests,
installMockBridgenow seeds the preference as explicitly off — the state of a user who already switched it — withseedInboxUnreadOnly: falseto opt out. One new spec opts out and covers the shipped default.Test plan
node --test desktop/src/features/home/useInboxUnreadOnly.test.mjs— 6/6desktop/tests/e2e/emoji-picker-surface.spec.ts— 3/3, two proven red before the fixdesktop/tests/e2e/inbox-unread-default.spec.ts— 3/3npx tsc --noEmitclean;biome checkclean on the changed files