Skip to content
Open
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
16 changes: 8 additions & 8 deletions ext/src/pages/Popup/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { HDSegwitBech32Wallet } from '@shared/class/wallets/hd-segwit-bech32-wal
import { SparkWallet } from '@shared/class/wallets/spark-wallet';
import { AccountNumberContext } from '@shared/hooks/AccountNumberContext';
import { EStep, InitializationContext } from '@shared/hooks/InitializationContext';
import { SETTINGS_CONFIG } from '@shared/hooks/SettingsContext';
import { AppSettings, SETTINGS_CONFIG } from '@shared/hooks/SettingsContext';
import { useSettings } from '@shared/hooks/useSettings';
import { Csprng } from '../../class/rng';
import { ThemedText } from '../../components/ThemedText';
Expand Down Expand Up @@ -39,9 +39,9 @@ const SettingsPage: React.FC = () => {
setAccountNumber(parseInt(value));
};

const handleSettingChange = async (key: string, value: string | boolean) => {
const handleSettingChange = async <K extends keyof AppSettings>(key: K, value: AppSettings[K]) => {
try {
await updateSetting(key as any, value);
await updateSetting(key, value);
} catch (error) {
console.error('Error updating setting:', error);
}
Expand All @@ -66,18 +66,18 @@ const SettingsPage: React.FC = () => {

{/* App Settings Section */}
<div style={{ textAlign: 'left', fontSize: '16px', marginBottom: '20px' }}>
{Object.keys(SETTINGS_CONFIG).map((key) => {
const config = SETTINGS_CONFIG[key as keyof typeof SETTINGS_CONFIG];
const currentValue = settings[key as keyof typeof SETTINGS_CONFIG];
{(Object.keys(SETTINGS_CONFIG) as (keyof AppSettings)[]).map((key) => {
const config = SETTINGS_CONFIG[key];
const currentValue = settings[key];

return (
<div key={key} style={{ marginBottom: '15px' }}>
<ThemedText>{key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1')}:</ThemedText>
<div style={{ marginTop: '5px' }}>
<Select id={`setting-${key}`} value={currentValue} onChange={(e) => handleSettingChange(key, e.target.value)}>
<Select id={`setting-${key}`} value={currentValue} onChange={(e) => handleSettingChange(key, e.target.value as AppSettings[typeof key])}>
{config.options.map((option: string) => (
<option key={option} value={option}>
{option === 'never' ? 'Never' : option === 'ON' ? 'On' : option === 'OFF' ? 'Off' : option.length === 2 ? option.toUpperCase() : option.charAt(0).toUpperCase() + option.slice(1)}
{option === 'never' ? 'Never' : option === 'ON' ? 'On' : option === 'OFF' ? 'Off' : option === option.toUpperCase() ? option : option.charAt(0).toUpperCase() + option.slice(1)}
</option>
))}
</Select>
Expand Down
9 changes: 6 additions & 3 deletions ext/src/pages/Popup/SwapDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { ArrowLeftIcon, Copy, ExternalLink } from 'lucide-react';

import { NetworkContext } from '@shared/hooks/NetworkContext';
import { useExchangeRate } from '@shared/hooks/useExchangeRate';
import { useSelectedFiat } from '@shared/hooks/useSelectedFiat';
import { getDecimalsByNetwork, getTickerByNetwork } from '@shared/models/network-getters';
import { formatFiatDisplay } from '@shared/modules/fiat-utils';
import { capitalizeFirstLetter, formatBalance, formatFiatBalance } from '@shared/modules/string-utils';
import { CommonSwap } from '@shared/types/common-swap';
import { NETWORK_ARK, NETWORK_ARK_MUTINYNET, NETWORK_SPARK } from '@shared/types/networks';
Expand All @@ -21,10 +23,11 @@ const SwapDetails: React.FC = () => {
const location = useLocation();
const { network } = useContext(NetworkContext);
const { swap } = location.state as SwapDetailsParams;
const fiat = useSelectedFiat();

const ticker = getTickerByNetwork(network);
const decimals = getDecimalsByNetwork(network);
const { exchangeRate } = useExchangeRate(network, 'USD');
const { exchangeRate } = useExchangeRate(network);

const [formattedDate, formattedDateWithTime] = useMemo(() => {
if (!swap.timestamp) return ['—', '—'];
Expand All @@ -48,8 +51,8 @@ const SwapDetails: React.FC = () => {

const amountUsd = useMemo(() => {
if (!exchangeRate) return '';
return `${formatFiatBalance(Math.abs(swap.amount).toString(), decimals, exchangeRate)} USD`;
}, [swap.amount, decimals, exchangeRate]);
return formatFiatDisplay(formatFiatBalance(Math.abs(swap.amount).toString(), decimals, exchangeRate), fiat);
}, [swap.amount, decimals, exchangeRate, fiat]);

const statusText = useMemo(() => {
switch (swap.status) {
Expand Down
23 changes: 15 additions & 8 deletions ext/src/pages/Popup/components/Balance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { useAccountBalance } from '@shared/hooks/useAccountBalance';
import { useAvailableNetworks } from '@shared/hooks/useAvailableNetworks';
import { useBalance } from '@shared/hooks/useBalance';
import { useExchangeRate } from '@shared/hooks/useExchangeRate';
import { useSelectedFiat } from '@shared/hooks/useSelectedFiat';
import { formatFiatDisplay } from '@shared/modules/fiat-utils';
import { useTokenBalance } from '@shared/hooks/useTokenBalance';
import { useTokenDiscovery } from '@shared/hooks/useTokenDiscovery';
import { fiatOnRamp } from '@shared/models/fiat-on-ramp';
Expand Down Expand Up @@ -48,7 +50,8 @@ const BalanceDefault = forwardRef<{ refresh: () => void }, BalanceProps>(({ netw
mutate();
},
}));
const { exchangeRate } = useExchangeRate(network, 'USD');
const fiat = useSelectedFiat();
const { exchangeRate } = useExchangeRate(network);
const availableNetworks = useAvailableNetworks();
const { accountBalance } = useAccountBalance(accountNumber, availableNetworks);
const ticker = getTickerByNetwork(network);
Expand Down Expand Up @@ -81,7 +84,7 @@ const BalanceDefault = forwardRef<{ refresh: () => void }, BalanceProps>(({ netw

<h1>
<span id="home-balance">{displayBalance}</span> {ticker}
&nbsp;<span style={{ fontSize: 14 }}>{displaySubBalance !== '—' ? `$${displaySubBalance}` : ''}</span>
&nbsp;<span style={{ fontSize: 14 }}>{displaySubBalance !== '—' ? formatFiatDisplay(displaySubBalance, fiat) : ''}</span>
{canBuyWithFiat ? (
<span style={{ paddingLeft: '15px' }}>
<Button onClick={handleBuyClick}>
Expand All @@ -103,6 +106,7 @@ BalanceDefault.displayName = 'BalanceDefault';
const BalanceLightning = forwardRef<{ refresh: () => void }, BalanceProps>(({ network, accountNumber, BackgroundCaller }, ref) => {
const availableNetworks = useAvailableNetworks();
const { accountBalance } = useAccountBalance(accountNumber, availableNetworks);
const fiat = useSelectedFiat();

// Lightning network aggregates balances from Spark, Ark, and Liquid networks
// Each underlying network has its own balance and exchange rate
Expand All @@ -119,9 +123,9 @@ const BalanceLightning = forwardRef<{ refresh: () => void }, BalanceProps>(({ ne
mutateLiquid();
},
}));
const { exchangeRate: sparkExchangeRate } = useExchangeRate(NETWORK_SPARK, 'USD');
const { exchangeRate: arkExchangeRate } = useExchangeRate(NETWORK_ARK, 'USD');
const { exchangeRate: liquidExchangeRate } = useExchangeRate(liquidNetwork, 'USD');
const { exchangeRate: sparkExchangeRate } = useExchangeRate(NETWORK_SPARK);
const { exchangeRate: arkExchangeRate } = useExchangeRate(NETWORK_ARK);
const { exchangeRate: liquidExchangeRate } = useExchangeRate(liquidNetwork);

const ticker = getTickerByNetwork(network);
const decimals = getDecimalsByNetwork(network);
Expand Down Expand Up @@ -168,7 +172,10 @@ const BalanceLightning = forwardRef<{ refresh: () => void }, BalanceProps>(({ ne
}

const formattedBalance = balance !== undefined ? formatBalance(balance, Number(getDecimalsByNetwork(net)), 8) : '—';
const formattedFiatBalance = exchangeRate !== undefined && balance !== undefined ? '$' + formatFiatBalance(balance, Number(getDecimalsByNetwork(net)), Number(exchangeRate)) : '$—';
const formattedFiatBalance =
exchangeRate !== undefined && balance !== undefined
? formatFiatDisplay(formatFiatBalance(balance, Number(getDecimalsByNetwork(net)), Number(exchangeRate)), fiat)
: formatFiatDisplay('—', fiat);
const networkTicker = getTickerByNetwork(net);
const networkName = capitalizeFirstLetter(net);

Expand All @@ -178,7 +185,7 @@ const BalanceLightning = forwardRef<{ refresh: () => void }, BalanceProps>(({ ne
<td style={{ textAlign: 'right', padding: '8px', fontSize: '14px' }}>
{formattedBalance} {networkTicker}
</td>
<td style={{ textAlign: 'right', padding: '8px', fontSize: '14px' }}>{formattedFiatBalance} USD</td>
<td style={{ textAlign: 'right', padding: '8px', fontSize: '14px' }}>{formattedFiatBalance}</td>
</tr>
);
});
Expand All @@ -203,7 +210,7 @@ const BalanceLightning = forwardRef<{ refresh: () => void }, BalanceProps>(({ ne
<h1>
<span id="home-balance">{displayBalance}</span> {ticker}
<div style={{ width: '100%', marginBottom: '15px' }}>
<span style={{ fontSize: 14 }}>{displaySubBalance !== '—' ? `$${displaySubBalance}` : ''}</span>
<span style={{ fontSize: 14 }}>{displaySubBalance !== '—' ? formatFiatDisplay(displaySubBalance, fiat) : ''}</span>
</div>
</h1>

Expand Down
11 changes: 11 additions & 0 deletions mobile/.eas/workflows/e2e-test-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ jobs:
flow_path:
- .maestro/send-spark.yml

settings:
needs: [build_android_for_e2e]
type: maestro
runs_on: linux-large-nested-virtualization
params:
retries: 5
build_id: ${{ needs.build_android_for_e2e.outputs.build_id }}
device_identifier: 'pixel_9'
flow_path:
- .maestro/settings.yml

send_token:
needs: [build_android_for_e2e]
type: maestro
Expand Down
9 changes: 9 additions & 0 deletions mobile/.eas/workflows/e2e-test-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ jobs:
flow_path:
- .maestro/send-spark.yml

settings:
needs: [build_ios_for_e2e]
type: maestro
params:
retries: 5
build_id: ${{ needs.build_ios_for_e2e.outputs.build_id }}
flow_path:
- .maestro/settings.yml

send_token:
needs: [build_ios_for_e2e]
type: maestro
Expand Down
87 changes: 87 additions & 0 deletions mobile/.maestro/settings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
appId: com.layerzwallet.mobile
---
# Create wallet first
- runFlow:
file: subflows/wallet-creation.yml

# --- Currency Selection Test ---

# Navigate to Settings
- tapOn:
id: SettingsButton
- assertVisible:
id: SettingsScreenTitle
- waitForAnimationToEnd:
timeout: 2000

# Open Currency screen
- tapOn:
id: CurrencyButton
- assertVisible:
id: CurrencyScreenTitle
- waitForAnimationToEnd:
timeout: 2000

# USD should be selected by default
- assertVisible:
id: CurrencyOption-USD

# Select EUR
- tapOn:
id: CurrencyOption-EUR

# Go back to Home and verify balance is rendered in EUR (€ symbol)
- runFlow: subflows/back.yml
- runFlow: subflows/back.yml
- waitForAnimationToEnd:
timeout: 2000
- assertVisible:
id: LayerActualBalance
- assertVisible:
text: '€.*'

# Go back to Settings > Currency and switch back to USD
- tapOn:
id: SettingsButton
- waitForAnimationToEnd:
timeout: 2000
- tapOn:
id: CurrencyButton
- waitForAnimationToEnd:
timeout: 2000
- tapOn:
id: CurrencyOption-USD

# Go back to Home and verify balance is rendered in USD ($ symbol)
- runFlow: subflows/back.yml
- runFlow: subflows/back.yml
- waitForAnimationToEnd:
timeout: 2000
- assertVisible:
id: LayerActualBalance
- assertVisible:
text: '\$.*'

# --- About Screen Test ---

# Navigate to Settings > About
- tapOn:
id: SettingsButton
- assertVisible:
id: SettingsScreenTitle
- waitForAnimationToEnd:
timeout: 2000

# Open About screen
- tapOn:
id: AboutButton
- waitForAnimationToEnd:
timeout: 2000

# Verify About screen content
- assertVisible:
text: Blog
- assertVisible:
text: Terms of Service
- assertVisible:
text: Privacy Policy
89 changes: 89 additions & 0 deletions mobile/app/Currency.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import { SafeAreaView } from 'react-native-safe-area-context';
import { ScrollView, StyleSheet, View } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Stack } from 'expo-router';

import ScreenHeader from '@/components/navigation/ScreenHeader';
import SettingsRow from '@/components/SettingsRow';
import { globalDarkBackground } from '@shared/constants/Colors';
import { useSettings } from '@shared/hooks/useSettings';
import { SUPPORTED_FIAT_CURRENCIES, TFiat } from '@shared/types/fiat';

const CURRENCY_OPTIONS = SUPPORTED_FIAT_CURRENCIES;

export default function CurrencyScreen() {
const { settings, updateSetting } = useSettings();
const selectedCurrency = settings.currency;

return (
<View style={[styles.container, { backgroundColor: globalDarkBackground }]}>
<Stack.Screen options={{ headerShown: false }} />
<SafeAreaView style={styles.safeArea} edges={['top', 'left', 'right', 'bottom']}>
<ScreenHeader title="Currency" testID="CurrencyScreenTitle" />

<ScrollView style={styles.scrollContainer} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator>
<View style={styles.settingsGroup}>
{CURRENCY_OPTIONS.map((item: TFiat, index: number) => {
const isSelected = selectedCurrency === item;
const isLastItem = index === CURRENCY_OPTIONS.length - 1;

return (
<View key={item}>
<View style={styles.rowContainer}>
<View style={styles.rowContent}>
<SettingsRow title={item} onPress={() => updateSetting('currency', item)} hideChevron testID={`CurrencyOption-${item}`} />
</View>
{isSelected && (
<View style={styles.selectedIconContainer}>
<Ionicons name="checkmark-circle" size={20} color="white" />
</View>
)}
</View>
{!isLastItem && <View style={styles.divider} />}
</View>
);
})}
</View>
</ScrollView>
</SafeAreaView>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
safeArea: {
flex: 1,
},
scrollContainer: {
flex: 1,
},
scrollContent: {
paddingHorizontal: 16,
paddingTop: 24,
paddingBottom: 0,
},
settingsGroup: {
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: 12,
overflow: 'hidden',
},
rowContainer: {
flexDirection: 'row',
alignItems: 'center',
},
rowContent: {
flex: 1,
},
selectedIconContainer: {
marginRight: 16,
},
divider: {
height: 1,
backgroundColor: 'rgba(255, 255, 255, 0.1)',
marginLeft: 16,
},
});
Loading
Loading