Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d6a2ce6
feat: add backgroundUrl and isTrending to DiscoverData type and API m…
CassioMG Mar 31, 2026
3b805d1
feat: add recentProtocols helper for browser.storage.local
CassioMG Mar 31, 2026
5746f87
feat: add useDiscoverData hook with trending, recent, and dapps lists
CassioMG Mar 31, 2026
a8c4866
feat: add ProtocolRow component for Discover sections
CassioMG Mar 31, 2026
95ea090
feat: add ProtocolDetailsPanel component for SlideupModal content
CassioMG Mar 31, 2026
9890836
feat: add ExpandedRecent and ExpandedDapps sub-view components
CassioMG Mar 31, 2026
c180149
feat: rewrite Discover orchestrator with sub-views, modals, and data …
CassioMG Mar 31, 2026
1099777
feat: wire up Discover Sheet in Account view and update AccountHeader
CassioMG Mar 31, 2026
5a35340
test: rewrite Discover tests for new Sheet-based component structure
CassioMG Mar 31, 2026
50b3c08
refactor: remove /discover route, now handled via Sheet overlay
CassioMG Mar 31, 2026
8a17902
fix: save recent protocol before opening tab to prevent data loss in …
CassioMG Mar 31, 2026
18b7a0f
fix: Discover Sheet fills full viewport and removes white border arti…
CassioMG Mar 31, 2026
e5b314d
fix carousel margins and paddings
CassioMG Mar 31, 2026
0ecf85b
fix discover header spacing
CassioMG Mar 31, 2026
ab5f8c0
remove duplicate padding
CassioMG Mar 31, 2026
aea1ad2
update legal disclaimer
CassioMG Mar 31, 2026
6ee8491
Merge branch 'master' into feature/discover-v2
CassioMG Mar 31, 2026
cb2339d
clean-up !important values
CassioMG Mar 31, 2026
d9e7c37
fix carousel card size
CassioMG Mar 31, 2026
2509c15
tweak "Open" button
CassioMG Mar 31, 2026
7e02554
use consistent "X" icon throughout screens
CassioMG Apr 1, 2026
3bcaf62
figma tweaks
CassioMG Apr 1, 2026
b30a972
figma tweak
CassioMG Apr 1, 2026
e999c53
remove superpowers folder
CassioMG Apr 1, 2026
f59150c
globally hides the scrollbar
CassioMG Apr 1, 2026
8321cc4
add translation wrapper
CassioMG Apr 1, 2026
3cc3cfc
add comment
CassioMG Apr 1, 2026
6dc2d5d
pt translations
CassioMG Apr 1, 2026
ccd20d8
more missing pt translations
CassioMG Apr 1, 2026
6f2f8cd
remove unused keys
CassioMG Apr 1, 2026
1df31a3
safe access empty array
CassioMG Apr 1, 2026
a1f53b3
allow dismissing popover menu by tapping on the outside
CassioMG Apr 1, 2026
670d0c2
cleaner ProtocolEntry interface importing/exporting
CassioMG Apr 1, 2026
865a685
move style between files
CassioMG Apr 1, 2026
44981ce
remove unused output
CassioMG Apr 1, 2026
e7b6dae
display fallback image when carousel card image fails to load
CassioMG Apr 1, 2026
06df44a
polish "Welcome to Discover!" modal
CassioMG Apr 1, 2026
f8b7df1
add unit tests
CassioMG Apr 1, 2026
544dc77
Use SDS Text component to render text
CassioMG Apr 1, 2026
c388362
smaller button
CassioMG Apr 2, 2026
de7a612
fix white-border bug
CassioMG Apr 2, 2026
7891c21
Radix handles toggling natively via onOpenChange
CassioMG Apr 2, 2026
010c6fb
discover error state handling
CassioMG Apr 2, 2026
1fc3b41
pt translations
CassioMG Apr 2, 2026
5717f5d
add metrics
CassioMG Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion @shared/api/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
HorizonOperation,
UserNotification,
CollectibleContract,
DiscoverData,
} from "./types";
import {
AccountBalancesInterface,
Expand Down Expand Up @@ -621,7 +622,7 @@ export const getTokenPrices = async (tokens: string[]) => {
return parsedResponse.data;
};

export const getDiscoverData = async () => {
export const getDiscoverData = async (): Promise<DiscoverData> => {
const url = new URL(`${INDEXER_V2_URL}/protocols`);
const response = await fetch(url.href);
const parsedResponse = (await response.json()) as {
Expand All @@ -633,6 +634,8 @@ export const getDiscoverData = async () => {
website_url: string;
tags: string[];
is_blacklisted: boolean;
background_url?: string;
is_trending: boolean;
}[];
};
};
Expand All @@ -652,6 +655,8 @@ export const getDiscoverData = async () => {
websiteUrl: entry.website_url,
tags: entry.tags,
isBlacklisted: entry.is_blacklisted,
backgroundUrl: entry.background_url,
isTrending: entry.is_trending,
}));
};

Expand Down
8 changes: 6 additions & 2 deletions @shared/api/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,14 +396,18 @@ export interface ApiTokenPrices {
[key: string]: ApiTokenPrice | null;
}

export type DiscoverData = {
export interface ProtocolEntry {
description: string;
iconUrl: string;
name: string;
websiteUrl: string;
tags: string[];
isBlacklisted: boolean;
}[];
backgroundUrl?: string;
isTrending: boolean;
}

export type DiscoverData = ProtocolEntry[];

export interface LedgerKeyAccount {
account_id: string;
Expand Down
2 changes: 0 additions & 2 deletions extension/src/popup/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ import { ManageNetwork } from "popup/views/ManageNetwork";
import { LeaveFeedback } from "popup/views/LeaveFeedback";
import { AccountMigration } from "popup/views/AccountMigration";
import { AddFunds } from "popup/views/AddFunds";
import { Discover } from "popup/views/Discover";
import { Wallets } from "popup/views/Wallets";

import { DEV_SERVER } from "@shared/constants/services";
Expand Down Expand Up @@ -275,7 +274,6 @@ export const Router = () => (
element={<AdvancedSettings />}
></Route>
<Route path={ROUTES.addFunds} element={<AddFunds />} />
<Route path={ROUTES.discover} element={<Discover />} />
<Route path={ROUTES.wallets} element={<Wallets />} />

{DEV_SERVER && (
Expand Down
3 changes: 2 additions & 1 deletion extension/src/popup/basics/layout/View/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ const ViewAppHeader: React.FC<ViewAppHeaderProps> = ({
<div className="View__header__box View__header__box--center">
<Text
as="h2"
size="md"
size="sm"
weight="medium"
role="heading"
aria-level={2}
data-testid="AppHeaderPageTitle"
Expand Down
2 changes: 1 addition & 1 deletion extension/src/popup/basics/shadcn/Sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function SheetContent({
<SheetPrimitive.Content
data-slot="sheet-content"
className={classNames(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col shadow-lg outline-none transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
side === "right" &&
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
side === "left" &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface AccountHeaderProps {
roundedTotalBalanceUsd: string;
refreshHiddenCollectibles: () => Promise<void>;
isCollectibleHidden: (collectionAddress: string, tokenId: string) => boolean;
onDiscoverClick: () => void;
}

export const AccountHeader = ({
Expand All @@ -55,6 +56,7 @@ export const AccountHeader = ({
roundedTotalBalanceUsd,
refreshHiddenCollectibles,
isCollectibleHidden,
onDiscoverClick,
}: AccountHeaderProps) => {
const { t } = useTranslation();
const networkDetails = useSelector(settingsNetworkDetailsSelector);
Expand Down Expand Up @@ -309,7 +311,7 @@ export const AccountHeader = ({
rightContent={
<div
className="AccountHeader__right-button AccountHeader__right-button--with-label"
onClick={() => navigateTo(ROUTES.discover, navigate)}
onClick={onDiscoverClick}
>
<Icon.Compass03 /> {t("Discover")}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const HardwareSign = ({
onCancel();
}
}}
customBackIcon={<Icon.XClose />}
customBackIcon={<Icon.X />}
title={t("Connect {walletType}", { walletType })}
/>
<div className="HardwareSign__content">
Expand Down Expand Up @@ -199,7 +199,7 @@ export const HardwareSign = ({
<div className="HardwareSign__wrapper" ref={hardwareConnectRef}>
<SubviewHeader
customBackAction={closeOverlay}
customBackIcon={<Icon.XClose />}
customBackIcon={<Icon.X />}
title={t("Connect {walletType}", { walletType })}
/>
<div className="HardwareSign__content">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const ChooseAsset = ({
<SubviewHeader
title={t("Your assets")}
customBackIcon={
!domainState.data?.isManagingAssets ? <Icon.XClose /> : undefined
!domainState.data?.isManagingAssets ? <Icon.X /> : undefined
}
customBackAction={goBack}
rightButton={
Expand Down
7 changes: 6 additions & 1 deletion extension/src/popup/constants/metricsNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@ export const METRIC_NAMES = {
viewEditNetwork: "loaded screen: edit network",
viewNetworkSettings: "loaded screen: network settings",
viewAddFunds: "loaded screen: add fund",
discover: "loaded screen: discover",

viewDiscover: "loaded screen: discover",
discoverProtocolOpened: "discover: protocol opened",
discoverProtocolDetailsViewed: "discover: protocol details viewed",
discoverProtocolOpenedFromDetails: "discover: protocol opened from details",
discoverWelcomeModalViewed: "discover: welcome modal viewed",

manageAssetAddAsset: "manage asset: add asset",
manageAssetAddToken: "manage asset: add token",
Expand Down
1 change: 0 additions & 1 deletion extension/src/popup/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,5 @@ export enum ROUTES {
accountMigrationConfirmMigration = "/account-migration/confirm-migration",
accountMigrationMigrationComplete = "/account-migration/migration-complete",

discover = "/discover",
wallets = "/wallets",
}
28 changes: 28 additions & 0 deletions extension/src/popup/helpers/recentProtocols.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import browser from "webextension-polyfill";
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.

If you start the extension in dev mode and load the extension in your browser at http://localhost:9000/#/, this code won't be able to run. That's because browser.storage is only available in:

  1. inside the background script

or

  1. a completely bundled extension that you're loading by clicking the little Freighter icon in your toolbar.

Because of this, we keep all interactions with browser.storage in the background script and then transmit them to the UI. For ex: https://github.com/stellar/freighter/blob/master/extension/src/background/messageListener/popupMessageListener.ts#L353

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.

There's also a dual purpose here. By keeping all storage interactions in the background, it makes it easier to track where storage changes were made

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.

It's certainly up for debate if we want to keep using this pattern, but IMO, it's useful to keep this hot reloading dev experience and it's also good practice to keep storage concerns away from the UI


const STORAGE_KEY = "recentProtocols";
const MAX_RECENT = 5;

export interface RecentProtocolEntry {
websiteUrl: string;
lastAccessed: number;
}

export const getRecentProtocols = async (): Promise<RecentProtocolEntry[]> => {
const result = await browser.storage.local.get(STORAGE_KEY);
return (result[STORAGE_KEY] as RecentProtocolEntry[]) || [];
};

export const addRecentProtocol = async (websiteUrl: string): Promise<void> => {
const existing = await getRecentProtocols();
const filtered = existing.filter((entry) => entry.websiteUrl !== websiteUrl);
const updated = [{ websiteUrl, lastAccessed: Date.now() }, ...filtered].slice(
0,
MAX_RECENT,
);
await browser.storage.local.set({ [STORAGE_KEY]: updated });
};

export const clearRecentProtocols = async (): Promise<void> => {
await browser.storage.local.remove(STORAGE_KEY);
};
13 changes: 13 additions & 0 deletions extension/src/popup/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"Clear": "Clear",
"Clear Flags": "Clear Flags",
"Clear Override": "Clear Override",
"Clear recents": "Clear recents",
"Close": "Close",
"Coinbase Logo": "Coinbase Logo",
"Collectible": "Collectible",
Expand Down Expand Up @@ -146,6 +147,7 @@
"Create new wallet": "Create new wallet",
"Current Network": "Current Network",
"Custom": "Custom",
"dApps": "dApps",
"Dapps": "Dapps",
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Both "dApps" and "Dapps" translation keys exist with effectively the same meaning, but there are no code references to t("Dapps") in the repo. Keeping both increases translation/maintenance overhead and can lead to inconsistent UI text. Consider consolidating to a single key (e.g., keep dApps and remove the unused Dapps).

Suggested change
"Dapps": "Dapps",

Copilot uses AI. Check for mistakes.
"data entries": "data entries",
"Debug": "Debug",
Expand All @@ -165,6 +167,7 @@
"Discover": "Discover",
"Do not proceed": "Do not proceed",
"Do this later": "Do this later",
"Domain": "Domain",
"Don’t share this phrase with anyone": "Don’t share this phrase with anyone",
"Done": "Done",
"Download on iOS or Android today": "Download on iOS or Android today",
Expand Down Expand Up @@ -311,6 +314,7 @@
"Leave Feedback": "Leave Feedback",
"Leave feedback about Blockaid warnings and messages": "Leave feedback about Blockaid warnings and messages",
"Ledger will not display the transaction details in the device display prior to signing so make sure you only interact with applications you know and trust.": "Ledger will not display the transaction details in the device display prior to signing so make sure you only interact with applications you know and trust.",
"Let's go": "Let's go",
"Limit": "Limit",
"Links": "Links",
"Liquidity Pool ID": "Liquidity Pool ID",
Expand Down Expand Up @@ -397,6 +401,7 @@
"Order is incorrect, try again": "Order is incorrect, try again",
"Overridden response": "Overridden response",
"Override Blockaid security responses for testing different security states (DEV only)": "Override Blockaid security responses for testing different security states (DEV only)",
"Overview": "Overview",
"Parameters": "Parameters",
"Passphrase": "Passphrase",
"Password": "Password",
Expand Down Expand Up @@ -429,6 +434,7 @@
"Ready to migrate": "Ready to migrate",
"Receive": "Receive",
"Received": "Received",
"Recent": "Recent",
"Recents": "Recents",
"Recovery Phrase": "Recovery Phrase",
"Refresh": "Refresh",
Expand Down Expand Up @@ -534,6 +540,7 @@
"Swapped!": "Swapped!",
"Swapping": "Swapping",
"Switch to this network": "Switch to this network",
"Tags": "Tags",
"Terms of Service": "Terms of Service",
"Terms of Use": "Terms of Use",
"The authorization entry is for": "The authorization entry is for",
Expand All @@ -550,7 +557,9 @@
"The transaction you’re trying to sign is on": "The transaction you’re trying to sign is on",
"The website <1>{url}</1> does not use an SSL certificate.": "The website <1>{url}</1> does not use an SSL certificate.",
"There are no sites to display at this moment.": "There are no sites to display at this moment.",
"There was an error fetching protocols. Please refresh and try again.": "There was an error fetching protocols. Please refresh and try again.",
"These assets are not on any of your lists. Proceed with caution before adding.": "These assets are not on any of your lists. Proceed with caution before adding.",
"These services are operated by independent third parties, not by Freighter or SDF. Inclusion here is not an endorsement. DeFi carries risk, including loss of funds. Use at your own risk.": "These services are operated by independent third parties, not by Freighter or SDF. Inclusion here is not an endorsement. DeFi carries risk, including loss of funds. Use at your own risk.",
"These words are your wallet’s keys—store them securely to keep your funds safe.": "These words are your wallet’s keys—store them securely to keep your funds safe.",
"This asset does not appear safe for the following reasons.": "This asset does not appear safe for the following reasons.",
"This asset has a balance": "This asset has a balance",
Expand Down Expand Up @@ -609,11 +618,13 @@
"Transaction Timeout": "Transaction Timeout",
"Transfer from another account": "Transfer from another account",
"Transfer from Coinbase, buy with debit and credit cards or bank transfer *": "Transfer from Coinbase, buy with debit and credit cards or bank transfer *",
"Trending": "Trending",
"trustlines": "trustlines",
"Trustor": "Trustor",
"Type": "Type",
"Type your memo": "Type your memo",
"Unable to connect to": "Unable to connect to",
"Unable to fetch protocols": "Unable to fetch protocols",
"Unable to find your asset.": "Unable to find your asset.",
"Unable to load network details": "Unable to load network details",
"Unable to migrate": "Unable to migrate",
Expand Down Expand Up @@ -667,6 +678,7 @@
"We were unable to scan this transaction for security threats": "We were unable to scan this transaction for security threats",
"WEBSITE CONNECTION IS NOT SECURE": "WEBSITE CONNECTION IS NOT SECURE",
"Welcome back": "Welcome back",
"Welcome to Discover!": "Welcome to Discover!",
"What is this transaction for? (optional)": "What is this transaction for? (optional)",
"What’s new": "What’s new",
"Wrong simulation result": "Wrong simulation result",
Expand Down Expand Up @@ -703,6 +715,7 @@
"Your account data could not be fetched at this time.": "Your account data could not be fetched at this time.",
"Your assets": "Your assets",
"Your available XLM balance is not enough to pay for the transaction fee.": "Your available XLM balance is not enough to pay for the transaction fee.",
"Your gateway to the Stellar ecosystem. Browse and connect to decentralized applications built on Stellar.": "Your gateway to the Stellar ecosystem. Browse and connect to decentralized applications built on Stellar.",
"Your recovery phrase": "Your recovery phrase",
"Your Recovery Phrase": "Your Recovery Phrase",
"Your recovery phrase gives you access to your account and is the only way to access it in a new browser.": "Your recovery phrase gives you access to your account and is the only way to access it in a new browser.",
Expand Down
Loading
Loading