Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 50 additions & 0 deletions .yarn/patches/expo-web-browser-npm-14.0.2-98d00ce880.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
diff --git a/ios/WebAuthSession.swift b/ios/WebAuthSession.swift
index 0d8101b01d7c6cd803acf6a359ceaa026993bdd0..c1beeabd962e561bf48392d58c084272247a95cc 100644
--- a/ios/WebAuthSession.swift
+++ b/ios/WebAuthSession.swift
@@ -20,17 +20,34 @@ final internal class WebAuthSession {
private var presentationContextProvider = PresentationContextProvider()

init(authUrl: URL, redirectUrl: URL?, options: AuthSessionOptions) {
- self.authSession = ASWebAuthenticationSession(
- url: authUrl,
- callbackURLScheme: redirectUrl?.scheme,
- completionHandler: { callbackUrl, error in
- self.finish(with: [
- "type": callbackUrl != nil ? "success" : "cancel",
- "url": callbackUrl?.absoluteString,
- "error": error?.localizedDescription
- ])
- }
- )
+ let completionHandler: (URL?, Error?) -> Void = { callbackUrl, error in
+ self.finish(with: [
+ "type": callbackUrl != nil ? "success" : "cancel",
+ "url": callbackUrl?.absoluteString,
+ "error": error?.localizedDescription
+ ])
+ }
+
+ // iOS 17.4+/macOS 14.4+ supports HTTPS callbacks with host/path matching
+ if #available(iOS 17.4, macOS 14.4, *),
+ let redirectUrl,
+ redirectUrl.scheme?.lowercased() == "https",
+ let host = redirectUrl.host(percentEncoded: false),
+ !host.isEmpty {
+ let rawPath = redirectUrl.path
+ let path = (rawPath.isEmpty || rawPath == "/") ? "" : rawPath
+ self.authSession = ASWebAuthenticationSession(
+ url: authUrl,
+ callback: .https(host: host, path: path),
+ completionHandler: completionHandler
+ )
+ } else {
+ self.authSession = ASWebAuthenticationSession(
+ url: authUrl,
+ callbackURLScheme: redirectUrl?.scheme,
+ completionHandler: completionHandler
+ )
+ }
self.authSession?.prefersEphemeralWebBrowserSession = options.preferEphemeralSession
}

10 changes: 8 additions & 2 deletions app/components/UI/AssetOverview/Price/Price.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,14 @@ const Price = ({
variant={TextVariant.BodyMDMedium}
allowFontScaling={false}
>
{diff > 0 ? '+' : ''}
{addCurrencySymbol(diff, currentCurrency, true)} (
{diff > 0 ? '+' : diff < 0 ? '-' : ''}
{diff !== 0
? formatPriceWithSubscriptNotation(
Math.abs(diff),
currentCurrency,
)
: addCurrencySymbol(0, currentCurrency, true)}{' '}
{'('}
{diff > 0 ? '+' : ''}
{diff === 0 ? '0' : ((diff / comparePrice) * 100).toFixed(2)}
%){' '}
Expand Down
25 changes: 0 additions & 25 deletions app/components/UI/Card/Views/CardHome/CardHome.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2278,31 +2278,6 @@ describe('CardHome Component', () => {
expect(screen.queryByText('Spending Limit')).not.toBeOnTheScreen();
});

it('hides manage spending limit button for Solana chain', () => {
// Given: authenticated with Solana chain
setupMockSelectors({ isAuthenticated: true });
mockIsSolanaChainId.mockReturnValue(true);
const solanaToken = {
...mockPriorityToken,
caipChainId: 'solana:mainnet',
allowanceState: AllowanceState.Limited,
};
setupLoadCardDataMock({
priorityToken: solanaToken,
allTokens: [solanaToken],
isAuthenticated: true,
warning: null,
});

// When: component renders
render();

// Then: should not display manage spending limit button
expect(
screen.queryByTestId(CardHomeSelectors.MANAGE_SPENDING_LIMIT_ITEM),
).not.toBeOnTheScreen();
});

it('hides close spending limit warning for Solana chain', () => {
// Given: authenticated with Solana chain and close to limit (15% remaining)
setupMockSelectors({ isAuthenticated: true });
Expand Down
32 changes: 15 additions & 17 deletions app/components/UI/Card/Views/CardHome/CardHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1571,23 +1571,21 @@ const CardHome = () => {
testID="freeze-card-list-item"
/>
)}
{isBaanxLoginEnabled &&
!isLoading &&
!isSolanaChainId(priorityToken?.caipChainId ?? '') && (
<ManageCardListItem
title={strings(
'card.card_home.manage_card_options.manage_spending_limit',
)}
description={strings(
priorityToken?.allowanceState === AllowanceState.Enabled
? 'card.card_home.manage_card_options.manage_spending_limit_description_full'
: 'card.card_home.manage_card_options.manage_spending_limit_description_restricted',
)}
rightIcon={IconName.ArrowRight}
onPress={manageSpendingLimitAction}
testID={CardHomeSelectors.MANAGE_SPENDING_LIMIT_ITEM}
/>
)}
{isBaanxLoginEnabled && !isLoading && (
<ManageCardListItem
title={strings(
'card.card_home.manage_card_options.manage_spending_limit',
)}
description={strings(
priorityToken?.allowanceState === AllowanceState.Enabled
? 'card.card_home.manage_card_options.manage_spending_limit_description_full'
: 'card.card_home.manage_card_options.manage_spending_limit_description_restricted',
)}
rightIcon={IconName.ArrowRight}
onPress={manageSpendingLimitAction}
testID={CardHomeSelectors.MANAGE_SPENDING_LIMIT_ITEM}
/>
)}
</Box>
{!isLoading &&
!cardSetupState.isKYCPending &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ describe('SpendingLimit Component', () => {
cancel: mockCancel,
skip: mockSkip,
isValid: true,
isSolanaSelected: false,
needsFaucet: false,
isFaucetCheckLoading: false,
});
Expand Down Expand Up @@ -576,7 +575,6 @@ describe('SpendingLimit Component', () => {
cancel: mockCancel,
skip: mockSkip,
isValid: true,
isSolanaSelected: false,
needsFaucet: false,
isFaucetCheckLoading: false,
});
Expand Down Expand Up @@ -692,7 +690,6 @@ describe('SpendingLimit Component', () => {
cancel: mockCancel,
skip: mockSkip,
isValid: true,
isSolanaSelected: false,
needsFaucet: false,
isFaucetCheckLoading: false,
});
Expand Down Expand Up @@ -736,7 +733,6 @@ describe('SpendingLimit Component', () => {
cancel: mockCancel,
skip: mockSkip,
isValid: true,
isSolanaSelected: false,
needsFaucet: false,
isFaucetCheckLoading: false,
});
Expand Down Expand Up @@ -858,7 +854,6 @@ describe('SpendingLimit Component', () => {
cancel: mockCancel,
skip: mockSkip,
isValid: true,
isSolanaSelected: false,
needsFaucet: false,
isFaucetCheckLoading: false,
});
Expand Down Expand Up @@ -1071,7 +1066,6 @@ describe('SpendingLimit Component', () => {
cancel: mockCancel,
skip: mockSkip,
isValid: true,
isSolanaSelected: false,
needsFaucet: false,
isFaucetCheckLoading: false,
});
Expand Down
21 changes: 0 additions & 21 deletions app/components/UI/Card/Views/SpendingLimit/SpendingLimit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ const SpendingLimit: React.FC<SpendingLimitProps> = ({ route }) => {
cancel,
skip,
isValid,
isSolanaSelected,
} = useSpendingLimit({
flow,
initialToken: selectedTokenFromRoute,
Expand Down Expand Up @@ -328,26 +327,6 @@ const SpendingLimit: React.FC<SpendingLimitProps> = ({ route }) => {

{/* Footer Buttons */}
<Box twClassName="gap-3 mt-6">
{/* Solana Warning */}
{isSolanaSelected && (
<Box
flexDirection={BoxFlexDirection.Row}
twClassName="p-3 bg-warning-muted rounded-lg items-center"
>
<Icon
name={IconName.Info}
size={IconSize.Sm}
color={IconColor.WarningDefault}
/>
<Text
variant={TextVariant.BodySm}
twClassName="flex-1 ml-2 text-warning-default"
>
{strings('card.card_spending_limit.solana_not_supported')}
</Text>
</Box>
)}

<Box flexDirection={BoxFlexDirection.Row} twClassName="gap-3">
<Box twClassName="flex-1">
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import {
TextVariant,
BoxAlignItems,
BoxJustifyContent,
BoxFlexDirection,
Icon,
IconName,
IconSize,
IconColor,
} from '@metamask/design-system-react-native';
import { useTailwind } from '@metamask/design-system-twrnc-preset';
import AvatarToken from '../../../../../../component-library/components/Avatars/Avatar/variants/AvatarToken';
import AvatarNetwork from '../../../../../../component-library/components/Avatars/Avatar/variants/AvatarNetwork';
import { AvatarSize } from '../../../../../../component-library/components/Avatars/Avatar';
import BadgeWrapper, {
BadgePosition,
Expand All @@ -24,6 +26,8 @@ import { NetworkBadgeSource } from '../../../../AssetOverview/Balance/Balance';
import { buildTokenIconUrl } from '../../../util/buildTokenIconUrl';
import { LINEA_CAIP_CHAIN_ID } from '../../../util/buildTokenList';
import { safeFormatChainIdToHex } from '../../../util/safeFormatChainIdToHex';
import { getNetworkImageSource } from '../../../../../../util/networks';
import { cardNetworkInfos } from '../../../constants';

export interface AssetCardProps {
/** Token symbol (e.g., 'mUSD', 'USDC') or 'Other' */
Expand Down Expand Up @@ -102,11 +106,40 @@ const AssetCard: React.FC<AssetCardProps> = ({
</BadgeWrapper>
)}
{isOther && (
<Icon
name={IconName.MoreHorizontal}
size={IconSize.Lg}
color={isSelected ? IconColor.IconMuted : IconColor.IconDefault}
/>
<Box
flexDirection={BoxFlexDirection.Row}
alignItems={BoxAlignItems.Center}
>
<AvatarNetwork
size={AvatarSize.Sm}
name="Base"
imageSource={getNetworkImageSource({
chainId: cardNetworkInfos.base.caipChainId,
})}
style={tw.style('rounded-full overflow-hidden')}
/>
<AvatarNetwork
size={AvatarSize.Sm}
name="Solana"
imageSource={getNetworkImageSource({
chainId: cardNetworkInfos.solana.caipChainId,
})}
style={tw.style('-ml-2 rounded-full overflow-hidden')}
/>
<Box
alignItems={BoxAlignItems.Center}
justifyContent={BoxJustifyContent.Center}
style={tw.style(
'w-6 h-6 -ml-2 rounded-full bg-background-default',
)}
>
<Icon
name={IconName.MoreHorizontal}
size={IconSize.Xs}
color={IconColor.PrimaryDefault}
/>
</Box>
</Box>
)}

<Text
Expand Down
Loading
Loading