Skip to content

Discover V2#2671

Merged
CassioMG merged 100 commits intomasterfrom
feature/discover-v2
Apr 22, 2026
Merged

Discover V2#2671
CassioMG merged 100 commits intomasterfrom
feature/discover-v2

Conversation

@CassioMG
Copy link
Copy Markdown
Contributor

@CassioMG CassioMG commented Apr 1, 2026

Summary

Rewrites the Discover feature as a rich, Sheet-based overlay with a Trending carousel, Recent protocols, dApps sections, protocol detail panels, and a first-time welcome modal. This is the extension counterpart to the mobile Discover V2 implementation (stellar/freighter-mobile#780).

Key changes

  • Sheet overlay instead of route: Discover is now a bottom Sheet opened from the Account header (like AssetDetail), replacing the old /discover route
  • Trending carousel: Horizontal scroll with protocol background images and fallback placeholder icons
  • Recent protocols: Tracks the last 5 opened protocols, shown on the home screen and in an expanded view with a "Clear recents" option
  • Protocol details panel: SlideupModal showing domain, tags, description, and an "Open" button
  • Welcome modal: Shown once on first visit
  • Background-routed storage: All Discover storage access (recents list, welcome-seen flag) goes through sendMessageToBackground handlers under background/messageListener/handlers/, matching the established codebase convention (addRecentAddress, getMobileAppBannerDismissed) rather than touching browser.storage.local from the popup
  • Data layer: New useDiscoverData hook with useReducer for trending/recent/dApps categorization; new ProtocolEntry type extracted from DiscoverData
  • 9 new components, 2 new hooks, 5 new background handlers, 5 new @shared/api/internal wrappers, 15 unit tests

Known issues

  • White border on first Sheet open: A white border shows around Sheet overlays (AssetDetail, Discover) and only goes away after the XLM asset is opened. If you press "shift" on your keyboard the white border comes back. We're still investigating how to properly fix it. This is a pre-existing production bug, not introduced by this PR. See video below as reference.
    UPDATE: this issue has just been fixed here 🎉 with a 1 liner change (after 8 failed attempts) ✅

  • Global scrollbar hiding: The browser's default scrollbar kept appearing over Sheet overlays (AssetDetail, Discover), breaking the visual presentation. Scoping the hiding to individual components did not work — hiding it globally on html, body was the only approach that reliably fixed it. Pending team discussion on whether this trade-off is acceptable. See video below as reference.
    UPDATE: we've decided on keeping the scrollbar hidden for now and further investigate it as part of this task. ✅

Analytics

Metrics match the mobile Discover V2 events (stellar/freighter-mobile#780) for consistent Amplitude dashboards.

Event Properties Triggered when
loaded screen: discover Discover Sheet opens
discover: protocol opened protocolName, url, source, isKnownProtocol User taps "Open" on a protocol row or from a details panel
discover: protocol details viewed protocolName, tags Protocol details panel renders
discover: protocol opened from details protocolName, url User taps "Open" from the details panel
discover: welcome modal viewed Welcome modal renders

Sources: trending_carousel, recent_list, dapps_list, expanded_recent_list, expanded_dapps_list

isKnownProtocol is always true on extension (no search bar for unknown URLs).

Other improvements

  • ViewAppHeader title size changed from size="md" to size="sm" weight="medium" to match updated Figma specs (affects all screens using View.AppHeader)
  • Consistent "X" icon usage and size (Icon.XClose -> Icon.X) throughout the codebase
  • Added explicit ProtocolEntry interface to @shared/api/types
  • Global scrollbar hiding for cleaner UI
  • Portuguese translations for all new strings + 30 previously untranslated strings
  • Removed 2 legacy translation keys

Follow-up work (after initial team review)

  • Analytics/metrics: Add Amplitude events for Discover interactions (open, protocol click)
  • E2E tests: Playwright tests for the full Discover flow (open Sheet, navigate sub-views, open protocol, welcome modal dismissal)

Reference

Videos

Popup experience

pop-up.experience.720p.mov

Fullscreen experience

fullscreen.experience.720p.mov

Scrollbar removal

With scrollbar

with.scrollbar.720p.mov

Without scrollbar

without.scrollbar.720p.mov

### White border bug (known Production issue) fixed here

white.border.bug.720p.mov

CassioMG and others added 26 commits March 30, 2026 20:51
…apping

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hooks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…popup mode

The popup closes immediately when a new tab opens, so the storage write
must complete before calling openTab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…facts

Use height: 100% instead of fixed popup height so the Sheet covers the
full screen in both popup and fullscreen mode. Override Tailwind defaults
that caused a visible white border around the overlay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@CassioMG CassioMG marked this pull request as ready for review April 1, 2026 01:54
Copilot AI review requested due to automatic review settings April 1, 2026 01:55
@CassioMG CassioMG added the don't review yet wip / tests in progress / missing videos or screenshots / pending self code-review label Apr 1, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 64 out of 68 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread @shared/api/internal.ts Outdated
Comment thread extension/src/popup/locales/pt/translation.json
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 64 out of 68 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread extension/src/popup/views/Account/index.tsx
Comment thread extension/src/popup/views/Discover/index.tsx
Exports SLIDEUP_MODAL_TRANSITION_MS from SlideupModal and uses it in
handleDetailsOpen instead of duplicating the 200ms literal. Keeps the
consumer's setTimeout in sync with the modal's dismiss animation.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 65 out of 69 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@CassioMG CassioMG requested a review from piyalbasu April 21, 2026 00:20
<Text
as="h2"
size="md"
size="sm"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is likely fine - but just want to confirm that we're okay with changing the font-size on all views that use this AppHeader

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yep, it was intended. I noticed there were some inconsistencies between old and new Figma screens in general so I updated to match the most recent designs for consistency. I also did an overall UI check and seems like it's looking good.

try {
await openTab(protocol.websiteUrl);
} catch (error) {
captureException(`Error opening Discover tab - ${error}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we should surface some sort of user-facing error in this case

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great suggestion.

I've surfaced user-facing error here using the existing sonner's toast + Notification component as it provides a smooth built-in experience with a very simple API. This is how it's looking like:

Using toast + Notification component

using-toast.mov

Using just Notification component

using-notification.mov

When using just the Notification component the code looks much more complex as we need to manually handle useState + useEffect locally for it to appear/dismiss.

await clearRecentProtocols();
await refreshRecent();
} catch (error) {
captureException(`Error clearing Discover recent protocols - ${error}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we should also surface a user-face error in the unlikely event this fails

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

see comment here

@piyalbasu
Copy link
Copy Markdown
Contributor

Code review

Found 1 issue:

  1. <Sheet open={isDiscoverOpen}> is rendered without an onOpenChange handler. Since Sheet wraps Radix Dialog, Escape-key and overlay/outside-click dismiss gestures fire onOpenChange(false) on the primitive but the parent's isDiscoverOpen state never flips, so those native dismiss paths are broken. Every other Sheet usage in the codebase (e.g. HiddenCollectibles, AccountCollectibles, AccountAssets) wires onOpenChange={(open) => !open && onClose()}. Users can still close via the in-sheet back button, but Escape and overlay-click are no-ops.

)}
<Sheet open={isDiscoverOpen}>
<SheetContent
onOpenAutoFocus={(e) => e.preventDefault()}
aria-describedby={undefined}
side="bottom"
className="AccountView__discover-sheet"
>
<ScreenReaderOnly>
<SheetTitle>{t("Discover")}</SheetTitle>
</ScreenReaderOnly>
<Discover onClose={() => setIsDiscoverOpen(false)} />
</SheetContent>
</Sheet>
</>

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

Copy link
Copy Markdown
Contributor

@piyalbasu piyalbasu left a comment

Choose a reason for hiding this comment

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

This is looking solid! I just left a couple of small comments and Claude has a comment, as well

@CassioMG
Copy link
Copy Markdown
Contributor Author

@piyalbasu thanks for the help reviewing! About the Claude comment, I noticed that as well but as I was using AssetDetails as reference for the Sheet implementation I ended up not adding the onOpenChange behavior for consistency since AssetDetails doesn't have it as well. But I see now that CollectibleDetails (which is a more recent component) has it so users can close it by tapping "esc" on keyboard. I'll add onOpenChange for both Discover and AssetDetails so we have both covered.

CassioMG and others added 3 commits April 21, 2026 10:40
After setItem resolves we already know the value is true; returning it
directly eliminates the storage round-trip and removes the duplication
flagged against getDiscoverWelcomeSeen. If setItem fails it throws, so
callers never get a false-positive { hasSeenDiscoverWelcome: true }.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s failures

Previously both failures were swallowed into Sentry only. Add toast.custom
notifications rendered with the SDS Notification error variant so the user
gets immediate feedback when opening a dApp or clearing recent dApps fails.
Includes en/pt-BR copy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without onOpenChange, Radix Dialog's native Escape-key and overlay-click
dismiss paths fire on the primitive but the parent state never flips,
making those gestures no-ops. Matches the pattern already used by
HiddenCollectibles and AccountCollectibles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CassioMG
Copy link
Copy Markdown
Contributor Author

CassioMG commented Apr 21, 2026

fixed the missing onOpenChange places here

Screen.Recording.2026-04-21.at.12.00.05.mov

CassioMG and others added 2 commits April 21, 2026 12:06
Spies on sonner's toast.custom to verify the Notification element emitted
when openTab rejects (from row-level Open button) and when
clearRecentProtocols rejects (from ExpandedRecent kebab clear).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Exercises the onOpenChange wiring added in the previous sheet fix by
pressing Escape after the trending carousel is visible and asserting
the sheet closes back to the account view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CassioMG CassioMG merged commit 325eb7f into master Apr 22, 2026
9 checks passed
@CassioMG CassioMG deleted the feature/discover-v2 branch April 22, 2026 01:14
@github-actions github-actions Bot mentioned this pull request Apr 24, 2026
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.

5 participants