diff --git a/veilend-mobile/App.tsx b/veilend-mobile/App.tsx
index 8d938be..afbe5e7 100644
--- a/veilend-mobile/App.tsx
+++ b/veilend-mobile/App.tsx
@@ -25,8 +25,15 @@ export default function App() {
{anyLoading && (
-
-
+
+
)}
diff --git a/veilend-mobile/src/components/ProtocolStatusBanners.tsx b/veilend-mobile/src/components/ProtocolStatusBanners.tsx
index b808ac9..85d3711 100644
--- a/veilend-mobile/src/components/ProtocolStatusBanners.tsx
+++ b/veilend-mobile/src/components/ProtocolStatusBanners.tsx
@@ -39,6 +39,7 @@ export default function ProtocolStatusBanners({
const isWalletBanner = banner.id === 'wallet-disconnected';
const onPress = isWalletBanner ? onReconnect : onRetrySync;
const iconName = isWalletBanner ? 'wallet-outline' : 'warning-outline';
+ const actionLabel = isRefreshing && !isWalletBanner ? 'Checking...' : banner.actionLabel;
return (
-
+
- {banner.title}
- {banner.message}
+ {/* Text is included in the parent's accessibilityLabel; hide from individual focus */}
+ {banner.title}
+ {banner.message}
-
- {isRefreshing && !isWalletBanner ? 'Checking...' : banner.actionLabel}
-
+ {actionLabel}
);
@@ -113,6 +119,11 @@ const styles = StyleSheet.create({
borderRadius: 999,
paddingHorizontal: 10,
paddingVertical: 7,
+ // Ensure minimum touch target
+ minWidth: 44,
+ minHeight: 44,
+ alignItems: 'center',
+ justifyContent: 'center',
},
actionText: {
color: '#FFFFFF',
diff --git a/veilend-mobile/src/hooks/useResponsive.ts b/veilend-mobile/src/hooks/useResponsive.ts
new file mode 100644
index 0000000..4e68f80
--- /dev/null
+++ b/veilend-mobile/src/hooks/useResponsive.ts
@@ -0,0 +1,29 @@
+/**
+ * useResponsive
+ *
+ * Returns responsive helpers derived from the current window dimensions.
+ * Re-renders when the device rotates or the window size changes (e.g. on
+ * foldables or when running in a split-screen context).
+ *
+ * Usage:
+ * const { isSmallScreen, width } = useResponsive();
+ */
+import { useWindowDimensions } from 'react-native';
+
+export type ResponsiveValues = {
+ /** Current window width in dp */
+ width: number;
+ /** Current window height in dp */
+ height: number;
+ /** True when the screen is narrower than 380 dp */
+ isSmallScreen: boolean;
+};
+
+export function useResponsive(): ResponsiveValues {
+ const { width, height } = useWindowDimensions();
+ return {
+ width,
+ height,
+ isSmallScreen: width < 380,
+ };
+}
diff --git a/veilend-mobile/src/navigation/index.tsx b/veilend-mobile/src/navigation/index.tsx
index f583173..b9834d1 100644
--- a/veilend-mobile/src/navigation/index.tsx
+++ b/veilend-mobile/src/navigation/index.tsx
@@ -31,14 +31,38 @@ function MainTabs() {
else if (route.name === 'Deposit') iconName = focused ? 'arrow-down' : 'arrow-down-outline';
else if (route.name === 'Borrow') iconName = focused ? 'cash' : 'cash-outline';
else if (route.name === 'Repay') iconName = focused ? 'arrow-up' : 'arrow-up-outline';
- return ;
+ return ;
},
})}
>
-
-
-
-
+
+
+
+
);
}
@@ -46,8 +70,14 @@ function MainTabs() {
/** Splash shown while session is being restored from SecureStore */
function SessionRestoreSplash() {
return (
-
-
+
+
);
}
diff --git a/veilend-mobile/src/screens/BorrowScreen.tsx b/veilend-mobile/src/screens/BorrowScreen.tsx
index 4dcbf05..ad0a739 100644
--- a/veilend-mobile/src/screens/BorrowScreen.tsx
+++ b/veilend-mobile/src/screens/BorrowScreen.tsx
@@ -5,11 +5,14 @@ import { MOCK_ASSETS } from '../data/mockData';
import { useStore } from '../store/store';
import { ActivityIndicator } from 'react-native';
import Toast from '../utils/toast';
+import { useResponsive } from '../hooks/useResponsive';
type SelectedAsset = { id: string; name: string; symbol: string } | null;
-
export default function BorrowScreen() {
+ const { isSmallScreen } = useResponsive();
+ const styles = makeStyles(isSmallScreen);
+
const [modalVisible, setModalVisible] = useState(false);
const [selectedAsset, setSelectedAsset] = useState(null);
const [amount, setAmount] = useState('1');
@@ -33,19 +36,25 @@ export default function BorrowScreen() {
setModalVisible(false);
}
};
+
return (
<>
Borrow Market
{/* Borrow Stats */}
-
-
+
+
Total Borrowed
$1,250
-
-
+
+
Borrow Limit
$8,500
@@ -59,10 +68,13 @@ export default function BorrowScreen() {
key={asset.id}
style={styles.assetCard}
onPress={() => openBorrowModal(asset)}
+ accessibilityRole="button"
+ accessibilityLabel={`Borrow ${asset.name} (${asset.symbol}), ${(asset.apy + 2).toFixed(2)}% APR, 10 million available`}
+ accessibilityHint="Double tap to open borrow amount dialog"
>
-
+
{asset.name}
@@ -84,186 +96,210 @@ export default function BorrowScreen() {
+
{/* Amount Modal */}
- setModalVisible(false)}
- >
-
-
- Borrow {selectedAsset?.symbol}
-
-
- setModalVisible(false)} style={[styles.modalBtn, { backgroundColor: '#333' }]}>
- Cancel
-
-
- {useStore.getState().lendingLoading ? : Confirm}
-
-
+ setModalVisible(false)}
+ accessibilityViewIsModal={true}
+ >
+
+
+ Borrow {selectedAsset?.symbol}
+
+
+ setModalVisible(false)}
+ style={[styles.modalBtn, { backgroundColor: '#333' }]}
+ accessibilityRole="button"
+ accessibilityLabel="Cancel borrow"
+ >
+ Cancel
+
+
+ {useStore.getState().lendingLoading
+ ?
+ : Confirm}
+
-
-
- >
- );
+
+
+
+ >
+ );
}
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#0A0A0A',
- padding: 24,
- paddingTop: 60,
- },
- headerTitle: {
- fontSize: 28,
- fontWeight: 'bold',
- color: '#FFFFFF',
- marginBottom: 24,
- },
- statsContainer: {
- flexDirection: 'row',
- backgroundColor: '#121212',
- borderRadius: 16,
- padding: 20,
- marginBottom: 32,
- borderWidth: 1,
- borderColor: '#222',
- },
- statBox: {
- flex: 1,
- alignItems: 'center',
- },
- statDivider: {
- width: 1,
- backgroundColor: '#333',
- marginHorizontal: 16,
- },
- statLabel: {
- color: '#A1A1A1',
- marginBottom: 8,
- fontSize: 14,
- },
- statValue: {
- color: '#FFFFFF',
- fontSize: 24,
- fontWeight: 'bold',
- },
- sectionTitle: {
- color: '#FFFFFF',
- fontSize: 18,
- fontWeight: 'bold',
- marginBottom: 16,
- },
- assetsList: {
- gap: 16,
- },
- assetCard: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- backgroundColor: '#121212',
- padding: 16,
- borderRadius: 16,
- borderWidth: 1,
- borderColor: '#222',
- },
- assetLeft: {
- flexDirection: 'row',
- alignItems: 'center',
- gap: 16,
- },
- iconContainer: {
- width: 48,
- height: 48,
- backgroundColor: '#1A1A1A',
- borderRadius: 24,
- justifyContent: 'center',
- alignItems: 'center',
- borderWidth: 1,
- borderColor: '#333',
- },
- assetName: {
- color: '#FFFFFF',
- fontSize: 16,
- fontWeight: '600',
- marginBottom: 4,
- },
- assetSymbol: {
- color: '#666',
- fontSize: 14,
- },
- assetRight: {
- alignItems: 'flex-end',
- },
- aprBadge: {
- backgroundColor: 'rgba(255, 99, 99, 0.15)',
- paddingHorizontal: 10,
- paddingVertical: 4,
- borderRadius: 8,
- marginBottom: 4,
- },
- aprText: {
- color: '#FF6363',
- fontWeight: 'bold',
- fontSize: 12,
- },
- available: {
- color: '#A1A1A1',
- fontSize: 14,
- },
- modalOverlay: {
- flex: 1,
- backgroundColor: 'rgba(0, 0, 0, 0.7)',
- justifyContent: 'flex-end',
- },
- modalContent: {
- backgroundColor: '#121212',
- padding: 24,
- borderTopLeftRadius: 24,
- borderTopRightRadius: 24,
- borderWidth: 1,
- borderColor: '#222',
- gap: 16,
- },
- modalTitle: {
- color: '#FFFFFF',
- fontSize: 22,
- fontWeight: 'bold',
- },
- amountInput: {
- backgroundColor: '#1A1A1A',
- borderWidth: 1,
- borderColor: '#333',
- borderRadius: 16,
- paddingHorizontal: 16,
- paddingVertical: 14,
- color: '#FFFFFF',
- fontSize: 16,
- },
- modalButtons: {
- flexDirection: 'row',
- gap: 12,
- },
- modalBtn: {
- flex: 1,
- paddingVertical: 16,
- borderRadius: 16,
- alignItems: 'center',
- justifyContent: 'center',
- },
- buttonText: {
- color: '#FFFFFF',
- fontWeight: 'bold',
- fontSize: 16,
- },
-});
+function makeStyles(isSmallScreen: boolean) {
+ return StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#0A0A0A',
+ padding: isSmallScreen ? 16 : 24,
+ paddingTop: 60,
+ },
+ headerTitle: {
+ fontSize: isSmallScreen ? 22 : 28,
+ fontWeight: 'bold',
+ color: '#FFFFFF',
+ marginBottom: 24,
+ },
+ statsContainer: {
+ flexDirection: 'row',
+ backgroundColor: '#121212',
+ borderRadius: 16,
+ padding: isSmallScreen ? 14 : 20,
+ marginBottom: 32,
+ borderWidth: 1,
+ borderColor: '#222',
+ },
+ statBox: {
+ flex: 1,
+ alignItems: 'center',
+ },
+ statDivider: {
+ width: 1,
+ backgroundColor: '#333',
+ marginHorizontal: 16,
+ },
+ statLabel: {
+ color: '#A1A1A1',
+ marginBottom: 8,
+ fontSize: isSmallScreen ? 12 : 14,
+ },
+ statValue: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 18 : 24,
+ fontWeight: 'bold',
+ },
+ sectionTitle: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 16 : 18,
+ fontWeight: 'bold',
+ marginBottom: 16,
+ },
+ assetsList: {
+ gap: 16,
+ },
+ assetCard: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ backgroundColor: '#121212',
+ padding: isSmallScreen ? 12 : 16,
+ borderRadius: 16,
+ borderWidth: 1,
+ borderColor: '#222',
+ minHeight: 72,
+ },
+ assetLeft: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: isSmallScreen ? 10 : 16,
+ flexShrink: 1,
+ },
+ iconContainer: {
+ width: isSmallScreen ? 40 : 48,
+ height: isSmallScreen ? 40 : 48,
+ backgroundColor: '#1A1A1A',
+ borderRadius: 24,
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderWidth: 1,
+ borderColor: '#333',
+ },
+ assetName: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 14 : 16,
+ fontWeight: '600',
+ marginBottom: 4,
+ },
+ assetSymbol: {
+ color: '#666',
+ fontSize: isSmallScreen ? 12 : 14,
+ },
+ assetRight: {
+ alignItems: 'flex-end',
+ flexShrink: 0,
+ },
+ aprBadge: {
+ backgroundColor: 'rgba(255, 99, 99, 0.15)',
+ paddingHorizontal: 10,
+ paddingVertical: 4,
+ borderRadius: 8,
+ marginBottom: 4,
+ },
+ aprText: {
+ color: '#FF6363',
+ fontWeight: 'bold',
+ fontSize: 12,
+ },
+ available: {
+ color: '#A1A1A1',
+ fontSize: isSmallScreen ? 12 : 14,
+ },
+ modalOverlay: {
+ flex: 1,
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
+ justifyContent: 'flex-end',
+ },
+ modalContent: {
+ backgroundColor: '#121212',
+ padding: isSmallScreen ? 16 : 24,
+ borderTopLeftRadius: 24,
+ borderTopRightRadius: 24,
+ borderWidth: 1,
+ borderColor: '#222',
+ gap: 16,
+ },
+ modalTitle: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 18 : 22,
+ fontWeight: 'bold',
+ },
+ amountInput: {
+ backgroundColor: '#1A1A1A',
+ borderWidth: 1,
+ borderColor: '#333',
+ borderRadius: 16,
+ paddingHorizontal: 16,
+ paddingVertical: 14,
+ color: '#FFFFFF',
+ fontSize: 16,
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ gap: 12,
+ },
+ modalBtn: {
+ flex: 1,
+ paddingVertical: isSmallScreen ? 14 : 16,
+ borderRadius: 16,
+ alignItems: 'center',
+ justifyContent: 'center',
+ minHeight: 48,
+ },
+ buttonText: {
+ color: '#FFFFFF',
+ fontWeight: 'bold',
+ fontSize: isSmallScreen ? 14 : 16,
+ },
+ });
+}
diff --git a/veilend-mobile/src/screens/ConnectWalletScreen.tsx b/veilend-mobile/src/screens/ConnectWalletScreen.tsx
index 31245e7..07249b8 100644
--- a/veilend-mobile/src/screens/ConnectWalletScreen.tsx
+++ b/veilend-mobile/src/screens/ConnectWalletScreen.tsx
@@ -112,6 +112,10 @@ export default function ConnectWalletScreen() {
activeOpacity={0.8}
onPress={generateWallet}
disabled={loading}
+ accessibilityRole="button"
+ accessibilityLabel="Generate new wallet"
+ accessibilityHint="Creates a new Stellar wallet and connects to the app"
+ accessibilityState={{ disabled: loading }}
>
setMode('import')}
disabled={loading}
+ accessibilityRole="button"
+ accessibilityLabel="Import existing wallet using secret key"
+ accessibilityHint="Switch to the import wallet form"
+ accessibilityState={{ disabled: loading }}
>
Already have a wallet?{' '}
@@ -151,17 +159,27 @@ export default function ConnectWalletScreen() {
autoCapitalize="characters"
autoCorrect={false}
secureTextEntry={false}
+ accessibilityLabel="Stellar secret key"
+ accessibilityHint="Enter your Stellar secret key starting with S to import your wallet"
/>
{error ? (
- {error}
+ {error}
) : null}
importWallet(secretKey)}
disabled={loading || !secretKey.trim()}
+ accessibilityRole="button"
+ accessibilityLabel="Connect wallet"
+ accessibilityHint="Imports and connects your Stellar wallet using the provided secret key"
+ accessibilityState={{ disabled: loading || !secretKey.trim() }}
>
{ setMode('choose'); setSecretKey(''); }}
disabled={loading}
+ accessibilityRole="button"
+ accessibilityLabel="Back to wallet options"
+ accessibilityHint="Returns to the generate or import wallet choice"
+ accessibilityState={{ disabled: loading }}
>
← Back
diff --git a/veilend-mobile/src/screens/DashboardScreen.tsx b/veilend-mobile/src/screens/DashboardScreen.tsx
index 65e3828..5185ca7 100644
--- a/veilend-mobile/src/screens/DashboardScreen.tsx
+++ b/veilend-mobile/src/screens/DashboardScreen.tsx
@@ -1,6 +1,5 @@
import React, { useEffect, useState, useRef } from 'react';
-import { View, Text, ScrollView, StyleSheet, TouchableOpacity, Image, Modal, TextInput, TouchableWithoutFeedback, Keyboard, KeyboardAvoidingView, Platform, Dimensions, FlatList, ActivityIndicator } from 'react-native';
-import api from '../utils/api';
+import { View, Text, ScrollView, StyleSheet, TouchableOpacity, Image, Modal, TextInput, TouchableWithoutFeedback, Keyboard, FlatList, ActivityIndicator } from 'react-native';
import { useStore, TransactionRecord } from '../store/store';
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
@@ -17,6 +16,10 @@ const DEFAULT_PROFILE_IMAGE = 'https://i.pravatar.cc/100?img=5';
export default function DashboardScreen({ navigation }: any) {
+ const { width, isSmallScreen } = useResponsive();
+ const CARD_WIDTH = width - 48; // Padding 24 * 2
+ const styles = makeStyles(isSmallScreen);
+
const {
address,
authToken,
@@ -68,8 +71,13 @@ export default function DashboardScreen({ navigation }: any) {
if (portfolioError) {
return (
- {portfolioError}
-
+ {portfolioError}
+
Retry
@@ -141,16 +149,35 @@ export default function DashboardScreen({ navigation }: any) {
};
const ServiceButton = ({ icon, label, onPress }: any) => (
-
+
-
+
- {label}
+ {label}
);
- const renderCard = ({ item }: { item: any }) => (
-
+ const renderCard = ({ item }: { item: any }) => {
+ const displayValue = isPrivacyMode
+ ? '****'
+ : `$${Number(item.value).toLocaleString('en-US', { minimumFractionDigits: 2 })}`;
+ const cardA11yLabel = isPrivacyMode
+ ? `${item.label}: hidden in privacy mode`
+ : `${item.label}: ${displayValue}`;
+
+ return (
+
- {item.label}
+ {item.label}
-
+
{isPrivacyMode
? '****'
: `$${item.value.toLocaleString('en-US', { minimumFractionDigits: 2 })}`
}
- {/* Masked number eye icon */}
-
+ {/* Masked number eye icon - decorative */}
+
{/* Added privacy shield top-right */}
-
-
- ZK Shielded
+
+
+ ZK Shielded
- {/* Bottom dots styled as small privacy "veils" with teal glow */}
-
+ {/* Bottom dots styled as small privacy "veils" with teal glow - decorative */}
+
@@ -199,7 +226,7 @@ export default function DashboardScreen({ navigation }: any) {
- );
+ );};
const getGreeting = () => {
const hour = new Date().getHours();
@@ -212,15 +239,31 @@ export default function DashboardScreen({ navigation }: any) {
{/* Header */}
-
- {getGreeting()}
- {username}
+
+ {getGreeting()}
+ {username}
-
-
+
+
- setProfileVisible(true)}>
+ setProfileVisible(true)}
+ accessibilityRole="button"
+ accessibilityLabel={`Profile menu for ${username}`}
+ accessibilityHint="Opens profile and settings"
+ >
setProfileVisible(false)}
+ accessibilityViewIsModal={true}
>
- setProfileVisible(false)}>
+ setProfileVisible(false)} accessibilityLabel="Close profile menu">
Profile
- setProfileVisible(false)}>
-
+ setProfileVisible(false)}
+ accessibilityRole="button"
+ accessibilityLabel="Close profile menu"
+ >
+
{/* Profile Setup / Username */}
-
+
Username
@@ -272,19 +320,32 @@ export default function DashboardScreen({ navigation }: any) {
onChangeText={setTempName}
autoFocus
placeholderTextColor="#555"
+ accessibilityLabel="Edit username"
+ accessibilityHint="Enter your new display name"
/>
-
-
+
+
) : (
{username}
- {
- setTempName(username);
- setIsEditingName(true);
- }}>
-
+ {
+ setTempName(username);
+ setIsEditingName(true);
+ }}
+ accessibilityRole="button"
+ accessibilityLabel={`Edit username, current name: ${username}`}
+ accessibilityHint="Opens an editable field to change your display name"
+ >
+
)}
@@ -292,46 +353,46 @@ export default function DashboardScreen({ navigation }: any) {
{/* Change Profile Picture */}
-
+
-
+
Change Profile Picture
Update your avatar
-
+
{/* Account Preference */}
-
+
-
+
Account Preferences
Currency, Notifications, Privacy
-
+
{/* Profile Setup (Generic) */}
-
+
-
+
Profile Setup
Complete your KYC/Verification
-
+
{/* Log Out */}
-
-
+
+
Log Out
@@ -352,18 +413,26 @@ export default function DashboardScreen({ navigation }: any) {
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
extraData={isPrivacyMode}
+ accessibilityLabel="Balance cards carousel"
+ accessibilityHint={`Swipe left or right to view ${CARDS.length} balance cards`}
/>
{/* Pagination Dots */}
-
- {CARDS.map((_, index) => (
+
+ {CARDS.map((card, index) => (
))}
@@ -394,26 +463,54 @@ export default function DashboardScreen({ navigation }: any) {
{/* Transactions List */}
- Transactions
+ Transactions
- {transactions.map((tx: TransactionRecord) => (
-
-
-
-
-
-
- {tx.type} — {tx.asset}
- {tx.timestamp}
+ {transactions.length === 0 && (
+
+ No transactions yet.
+
+ )}
+ {transactions.map((tx: TransactionRecord) => {
+ const txTypeLabel =
+ tx.type === 'deposit' ? 'Deposited' :
+ tx.type === 'withdraw' ? 'Withdrew' :
+ tx.type === 'borrow' ? 'Borrowed' :
+ tx.type === 'repay' ? 'Repaid' : tx.type;
+ const txA11yLabel = isPrivacyMode
+ ? `${txTypeLabel} ${tx.asset}, amount hidden in privacy mode, ${tx.timestamp}, status: ${tx.status}`
+ : `${txTypeLabel} ${tx.amount} ${tx.asset}, ${tx.timestamp}, status: ${tx.status}`;
+
+ return (
+
+
+
+
+
+
+ {tx.type} — {tx.asset}
+ {tx.timestamp}
+
+
+ {isPrivacyMode ? '****' : `${tx.amount} ${tx.asset}`}
+
- {tx.amount} {tx.asset}
-
- ))}
+ );
+ })}
{/* Spacer for bottom tab bar */}
@@ -422,7 +519,8 @@ export default function DashboardScreen({ navigation }: any) {
);
}
-const styles = StyleSheet.create({
+function makeStyles(s: boolean) {
+ return StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#0A0A0A',
@@ -509,8 +607,8 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.2)',
- paddingHorizontal: isSmallScreen ? 8 : 10,
- paddingVertical: isSmallScreen ? 4 : 6,
+ paddingHorizontal: s ? 8 : 10,
+ paddingVertical: s ? 4 : 6,
borderRadius: 20,
borderWidth: 1,
borderColor: 'rgba(0, 209, 255, 0.3)', // Teal border
@@ -521,9 +619,9 @@ const styles = StyleSheet.create({
},
privacyText: {
color: '#D1D1D1',
- fontSize: isSmallScreen ? 10 : 12,
+ fontSize: s ? 10 : 12,
fontWeight: '600',
- marginLeft: isSmallScreen ? 4 : 6,
+ marginLeft: s ? 4 : 6,
},
cardNumberContainer: {
flexDirection: 'row',
@@ -581,10 +679,12 @@ const styles = StyleSheet.create({
},
serviceBtn: {
alignItems: 'center',
+ minWidth: 64,
+ padding: 4,
},
serviceIconBox: {
- width: 56,
- height: 56,
+ width: s ? 52 : 56,
+ height: s ? 52 : 56,
backgroundColor: '#1A1A1A',
borderRadius: 16,
justifyContent: 'center',
diff --git a/veilend-mobile/src/screens/DepositScreen.tsx b/veilend-mobile/src/screens/DepositScreen.tsx
index a34ef3e..d275e18 100644
--- a/veilend-mobile/src/screens/DepositScreen.tsx
+++ b/veilend-mobile/src/screens/DepositScreen.tsx
@@ -5,10 +5,14 @@ import { MOCK_ASSETS } from '../data/mockData';
import { useStore } from '../store/store';
import { ActivityIndicator } from 'react-native';
import Toast from '../utils/toast';
+import { useResponsive } from '../hooks/useResponsive';
type SelectedAsset = { id: string; name: string; symbol: string; balance?: number } | null;
export default function DepositScreen() {
+ const { isSmallScreen } = useResponsive();
+ const styles = makeStyles(isSmallScreen);
+
const [modalVisible, setModalVisible] = useState(false);
const [selectedAsset, setSelectedAsset] = useState(null);
const [amount, setAmount] = useState('');
@@ -33,19 +37,25 @@ export default function DepositScreen() {
setModalVisible(false);
}
};
+
return (
<>
Supply Market
{/* Stats Header */}
-
-
+
+
Net APY
4.25%
-
-
+
+
Supply Balance
$12,450
@@ -59,10 +69,13 @@ export default function DepositScreen() {
key={asset.id}
style={styles.assetCard}
onPress={() => openDepositModal(asset)}
+ accessibilityRole="button"
+ accessibilityLabel={`Deposit ${asset.name} (${asset.symbol}), ${asset.apy}% APY, wallet balance: ${asset.balance} ${asset.symbol}`}
+ accessibilityHint="Double tap to open deposit amount dialog"
>
-
+
{asset.name}
@@ -84,186 +97,210 @@ export default function DepositScreen() {
+
{/* Amount Modal */}
- setModalVisible(false)}
- >
-
-
- Deposit {selectedAsset?.symbol}
-
-
- setModalVisible(false)} style={[styles.modalBtn, { backgroundColor: '#333' }]}>
- Cancel
-
-
- {useStore.getState().lendingLoading ? : Confirm}
-
-
+ setModalVisible(false)}
+ accessibilityViewIsModal={true}
+ >
+
+
+ Deposit {selectedAsset?.symbol}
+
+
+ setModalVisible(false)}
+ style={[styles.modalBtn, { backgroundColor: '#333' }]}
+ accessibilityRole="button"
+ accessibilityLabel="Cancel deposit"
+ >
+ Cancel
+
+
+ {useStore.getState().lendingLoading
+ ?
+ : Confirm}
+
-
-
- >
- );
+
+
+
+ >
+ );
}
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#0A0A0A',
- padding: 24,
- paddingTop: 60,
- },
- headerTitle: {
- fontSize: 28,
- fontWeight: 'bold',
- color: '#FFFFFF',
- marginBottom: 24,
- },
- statsContainer: {
- flexDirection: 'row',
- backgroundColor: '#121212',
- borderRadius: 16,
- padding: 20,
- marginBottom: 32,
- borderWidth: 1,
- borderColor: '#222',
- },
- statBox: {
- flex: 1,
- alignItems: 'center',
- },
- statDivider: {
- width: 1,
- backgroundColor: '#333',
- marginHorizontal: 16,
- },
- statLabel: {
- color: '#A1A1A1',
- marginBottom: 8,
- fontSize: 14,
- },
- statValue: {
- color: '#FFFFFF',
- fontSize: 24,
- fontWeight: 'bold',
- },
- sectionTitle: {
- color: '#FFFFFF',
- fontSize: 18,
- fontWeight: 'bold',
- marginBottom: 16,
- },
- assetsList: {
- gap: 16,
- },
- assetCard: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- backgroundColor: '#121212',
- padding: 16,
- borderRadius: 16,
- borderWidth: 1,
- borderColor: '#222',
- },
- assetLeft: {
- flexDirection: 'row',
- alignItems: 'center',
- gap: 16,
- },
- iconContainer: {
- width: 48,
- height: 48,
- backgroundColor: '#1A1A1A',
- borderRadius: 24,
- justifyContent: 'center',
- alignItems: 'center',
- borderWidth: 1,
- borderColor: '#333',
- },
- assetName: {
- color: '#FFFFFF',
- fontSize: 16,
- fontWeight: '600',
- marginBottom: 4,
- },
- assetSymbol: {
- color: '#666',
- fontSize: 14,
- },
- assetRight: {
- alignItems: 'flex-end',
- },
- apyBadge: {
- backgroundColor: 'rgba(168, 85, 247, 0.15)',
- paddingHorizontal: 10,
- paddingVertical: 4,
- borderRadius: 8,
- marginBottom: 4,
- },
- apyText: {
- color: '#A855F7',
- fontWeight: 'bold',
- fontSize: 12,
- },
- walletBalance: {
- color: '#A1A1A1',
- fontSize: 14,
- },
- modalOverlay: {
- flex: 1,
- backgroundColor: 'rgba(0, 0, 0, 0.7)',
- justifyContent: 'flex-end',
- },
- modalContent: {
- backgroundColor: '#121212',
- padding: 24,
- borderTopLeftRadius: 24,
- borderTopRightRadius: 24,
- borderWidth: 1,
- borderColor: '#222',
- gap: 16,
- },
- modalTitle: {
- color: '#FFFFFF',
- fontSize: 22,
- fontWeight: 'bold',
- },
- amountInput: {
- backgroundColor: '#1A1A1A',
- borderWidth: 1,
- borderColor: '#333',
- borderRadius: 16,
- paddingHorizontal: 16,
- paddingVertical: 14,
- color: '#FFFFFF',
- fontSize: 16,
- },
- modalButtons: {
- flexDirection: 'row',
- gap: 12,
- },
- modalBtn: {
- flex: 1,
- paddingVertical: 16,
- borderRadius: 16,
- alignItems: 'center',
- justifyContent: 'center',
- },
- buttonText: {
- color: '#FFFFFF',
- fontWeight: 'bold',
- fontSize: 16,
- },
-});
+function makeStyles(isSmallScreen: boolean) {
+ return StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#0A0A0A',
+ padding: isSmallScreen ? 16 : 24,
+ paddingTop: 60,
+ },
+ headerTitle: {
+ fontSize: isSmallScreen ? 22 : 28,
+ fontWeight: 'bold',
+ color: '#FFFFFF',
+ marginBottom: 24,
+ },
+ statsContainer: {
+ flexDirection: 'row',
+ backgroundColor: '#121212',
+ borderRadius: 16,
+ padding: isSmallScreen ? 14 : 20,
+ marginBottom: 32,
+ borderWidth: 1,
+ borderColor: '#222',
+ },
+ statBox: {
+ flex: 1,
+ alignItems: 'center',
+ },
+ statDivider: {
+ width: 1,
+ backgroundColor: '#333',
+ marginHorizontal: 16,
+ },
+ statLabel: {
+ color: '#A1A1A1',
+ marginBottom: 8,
+ fontSize: isSmallScreen ? 12 : 14,
+ },
+ statValue: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 18 : 24,
+ fontWeight: 'bold',
+ },
+ sectionTitle: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 16 : 18,
+ fontWeight: 'bold',
+ marginBottom: 16,
+ },
+ assetsList: {
+ gap: 16,
+ },
+ assetCard: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ backgroundColor: '#121212',
+ padding: isSmallScreen ? 12 : 16,
+ borderRadius: 16,
+ borderWidth: 1,
+ borderColor: '#222',
+ minHeight: 72,
+ },
+ assetLeft: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: isSmallScreen ? 10 : 16,
+ flexShrink: 1,
+ },
+ iconContainer: {
+ width: isSmallScreen ? 40 : 48,
+ height: isSmallScreen ? 40 : 48,
+ backgroundColor: '#1A1A1A',
+ borderRadius: 24,
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderWidth: 1,
+ borderColor: '#333',
+ },
+ assetName: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 14 : 16,
+ fontWeight: '600',
+ marginBottom: 4,
+ },
+ assetSymbol: {
+ color: '#666',
+ fontSize: isSmallScreen ? 12 : 14,
+ },
+ assetRight: {
+ alignItems: 'flex-end',
+ flexShrink: 0,
+ },
+ apyBadge: {
+ backgroundColor: 'rgba(168, 85, 247, 0.15)',
+ paddingHorizontal: 10,
+ paddingVertical: 4,
+ borderRadius: 8,
+ marginBottom: 4,
+ },
+ apyText: {
+ color: '#A855F7',
+ fontWeight: 'bold',
+ fontSize: 12,
+ },
+ walletBalance: {
+ color: '#A1A1A1',
+ fontSize: isSmallScreen ? 12 : 14,
+ },
+ modalOverlay: {
+ flex: 1,
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
+ justifyContent: 'flex-end',
+ },
+ modalContent: {
+ backgroundColor: '#121212',
+ padding: isSmallScreen ? 16 : 24,
+ borderTopLeftRadius: 24,
+ borderTopRightRadius: 24,
+ borderWidth: 1,
+ borderColor: '#222',
+ gap: 16,
+ },
+ modalTitle: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 18 : 22,
+ fontWeight: 'bold',
+ },
+ amountInput: {
+ backgroundColor: '#1A1A1A',
+ borderWidth: 1,
+ borderColor: '#333',
+ borderRadius: 16,
+ paddingHorizontal: 16,
+ paddingVertical: 14,
+ color: '#FFFFFF',
+ fontSize: 16,
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ gap: 12,
+ },
+ modalBtn: {
+ flex: 1,
+ paddingVertical: isSmallScreen ? 14 : 16,
+ borderRadius: 16,
+ alignItems: 'center',
+ justifyContent: 'center',
+ minHeight: 48,
+ },
+ buttonText: {
+ color: '#FFFFFF',
+ fontWeight: 'bold',
+ fontSize: isSmallScreen ? 14 : 16,
+ },
+ });
+}
diff --git a/veilend-mobile/src/screens/RepayScreen.tsx b/veilend-mobile/src/screens/RepayScreen.tsx
index bc7359f..936ab68 100644
--- a/veilend-mobile/src/screens/RepayScreen.tsx
+++ b/veilend-mobile/src/screens/RepayScreen.tsx
@@ -5,8 +5,12 @@ import { MOCK_POSITIONS } from '../data/mockData';
import { useStore } from '../store/store';
import { ActivityIndicator } from 'react-native';
import Toast from '../utils/toast';
+import { useResponsive } from '../hooks/useResponsive';
export default function RepayScreen() {
+ const { isSmallScreen } = useResponsive();
+ const styles = makeStyles(isSmallScreen);
+
const activeLoans = MOCK_POSITIONS.filter(p => p.type === 'Borrowed');
const [modalVisible, setModalVisible] = useState(false);
const [selectedLoan, setSelectedLoan] = useState(null);
@@ -44,30 +48,51 @@ export default function RepayScreen() {
-
+
{loan.asset}
Debt
-
- Health: {loan.healthFactor}
+
+
+ Health: {loan.healthFactor}
+
-
- Amount Owed
- {loan.amount} {loan.asset}
+
+ Amount Owed
+ {loan.amount} {loan.asset}
-
- Value
- ${loan.value.toLocaleString()}
+
+ Value
+ ${loan.value.toLocaleString()}
-
- Interest Accrued
- $12.50
+
+ Interest Accrued
+ $12.50
@@ -81,6 +106,9 @@ export default function RepayScreen() {
}
openRepayModal(loan);
}}
+ accessibilityRole="button"
+ accessibilityLabel={`Repay ${loan.amount} ${loan.asset}`}
+ accessibilityHint="Double tap to open repayment amount dialog"
>
Repay Now
@@ -88,194 +116,225 @@ export default function RepayScreen() {
))}
) : (
-
-
- No active loans
- You don't have any borrowed assets to repay.
+
+
+ No active loans
+
+ You don't have any borrowed assets to repay.
+
)}
+
{/* Amount Modal */}
- setModalVisible(false)}
- >
-
-
- Repay {selectedLoan?.asset}
-
-
- setModalVisible(false)} style={[styles.modalBtn, { backgroundColor: '#333' }]}>
- Cancel
-
-
- {useStore.getState().lendingLoading ? : Confirm}
-
-
+ setModalVisible(false)}
+ accessibilityViewIsModal={true}
+ >
+
+
+ Repay {selectedLoan?.asset}
+
+
+ setModalVisible(false)}
+ style={[styles.modalBtn, { backgroundColor: '#333' }]}
+ accessibilityRole="button"
+ accessibilityLabel="Cancel repayment"
+ >
+ Cancel
+
+
+ {useStore.getState().lendingLoading
+ ?
+ : Confirm}
+
-
-
- >
- );
+
+
+
+ >
+ );
}
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#0A0A0A',
- padding: 24,
- paddingTop: 60,
- },
- headerTitle: {
- fontSize: 28,
- fontWeight: 'bold',
- color: '#FFFFFF',
- marginBottom: 24,
- },
- loansList: {
- gap: 16,
- },
- loanCard: {
- backgroundColor: '#121212',
- borderRadius: 24,
- padding: 20,
- borderWidth: 1,
- borderColor: '#222',
- },
- cardHeader: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- marginBottom: 24,
- },
- assetInfo: {
- flexDirection: 'row',
- alignItems: 'center',
- gap: 12,
- },
- iconContainer: {
- width: 48,
- height: 48,
- backgroundColor: '#1A1A1A',
- borderRadius: 16,
- justifyContent: 'center',
- alignItems: 'center',
- borderWidth: 1,
- borderColor: '#333',
- },
- assetName: {
- color: '#FFFFFF',
- fontSize: 18,
- fontWeight: 'bold',
- },
- loanLabel: {
- color: '#666',
- fontSize: 12,
- },
- healthBadge: {
- backgroundColor: 'rgba(74, 222, 128, 0.1)',
- paddingHorizontal: 12,
- paddingVertical: 6,
- borderRadius: 20,
- borderWidth: 1,
- borderColor: 'rgba(74, 222, 128, 0.2)',
- },
- healthText: {
- color: '#4ade80',
- fontSize: 12,
- fontWeight: 'bold',
- },
- loanDetails: {
- gap: 12,
- marginBottom: 24,
- },
- detailRow: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- },
- detailLabel: {
- color: '#A1A1A1',
- fontSize: 14,
- },
- detailValue: {
- color: '#FFFFFF',
- fontSize: 14,
- fontWeight: '600',
- },
- repayButton: {
- backgroundColor: '#A855F7',
- paddingVertical: 16,
- borderRadius: 16,
- alignItems: 'center',
- },
- buttonText: {
- color: '#FFFFFF',
- fontWeight: 'bold',
- fontSize: 16,
- },
- emptyState: {
- alignItems: 'center',
- marginTop: 60,
- },
- emptyText: {
- color: '#FFFFFF',
- fontSize: 20,
- fontWeight: 'bold',
- marginTop: 16,
- },
- emptySubtext: {
- color: '#666',
- fontSize: 14,
- marginTop: 8,
- textAlign: 'center',
- },
- modalOverlay: {
- flex: 1,
- backgroundColor: 'rgba(0, 0, 0, 0.7)',
- justifyContent: 'flex-end',
- },
- modalContent: {
- backgroundColor: '#121212',
- padding: 24,
- borderTopLeftRadius: 24,
- borderTopRightRadius: 24,
- borderWidth: 1,
- borderColor: '#222',
- gap: 16,
- },
- modalTitle: {
- color: '#FFFFFF',
- fontSize: 22,
- fontWeight: 'bold',
- },
- amountInput: {
- backgroundColor: '#1A1A1A',
- borderWidth: 1,
- borderColor: '#333',
- borderRadius: 16,
- paddingHorizontal: 16,
- paddingVertical: 14,
- color: '#FFFFFF',
- fontSize: 16,
- },
- modalButtons: {
- flexDirection: 'row',
- gap: 12,
- },
- modalBtn: {
- flex: 1,
- paddingVertical: 16,
- borderRadius: 16,
- alignItems: 'center',
- justifyContent: 'center',
- },
-});
+function makeStyles(isSmallScreen: boolean) {
+ return StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#0A0A0A',
+ padding: isSmallScreen ? 16 : 24,
+ paddingTop: 60,
+ },
+ headerTitle: {
+ fontSize: isSmallScreen ? 22 : 28,
+ fontWeight: 'bold',
+ color: '#FFFFFF',
+ marginBottom: 24,
+ },
+ loansList: {
+ gap: 16,
+ },
+ loanCard: {
+ backgroundColor: '#121212',
+ borderRadius: 24,
+ padding: isSmallScreen ? 14 : 20,
+ borderWidth: 1,
+ borderColor: '#222',
+ },
+ cardHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ flexWrap: 'wrap',
+ gap: 8,
+ },
+ assetInfo: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 12,
+ },
+ iconContainer: {
+ width: isSmallScreen ? 40 : 48,
+ height: isSmallScreen ? 40 : 48,
+ backgroundColor: '#1A1A1A',
+ borderRadius: 16,
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderWidth: 1,
+ borderColor: '#333',
+ },
+ assetName: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 15 : 18,
+ fontWeight: 'bold',
+ },
+ loanLabel: {
+ color: '#666',
+ fontSize: 12,
+ },
+ healthBadge: {
+ backgroundColor: 'rgba(74, 222, 128, 0.1)',
+ paddingHorizontal: 12,
+ paddingVertical: 6,
+ borderRadius: 20,
+ borderWidth: 1,
+ borderColor: 'rgba(74, 222, 128, 0.2)',
+ },
+ healthText: {
+ color: '#4ade80',
+ fontSize: 12,
+ fontWeight: 'bold',
+ },
+ loanDetails: {
+ gap: 12,
+ marginBottom: 24,
+ },
+ detailRow: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ detailLabel: {
+ color: '#A1A1A1',
+ fontSize: isSmallScreen ? 12 : 14,
+ },
+ detailValue: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 12 : 14,
+ fontWeight: '600',
+ },
+ repayButton: {
+ backgroundColor: '#A855F7',
+ paddingVertical: isSmallScreen ? 14 : 16,
+ borderRadius: 16,
+ alignItems: 'center',
+ minHeight: 48,
+ },
+ buttonText: {
+ color: '#FFFFFF',
+ fontWeight: 'bold',
+ fontSize: isSmallScreen ? 14 : 16,
+ },
+ emptyState: {
+ alignItems: 'center',
+ marginTop: 60,
+ },
+ emptyText: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 17 : 20,
+ fontWeight: 'bold',
+ marginTop: 16,
+ },
+ emptySubtext: {
+ color: '#666',
+ fontSize: isSmallScreen ? 12 : 14,
+ marginTop: 8,
+ textAlign: 'center',
+ },
+ modalOverlay: {
+ flex: 1,
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
+ justifyContent: 'flex-end',
+ },
+ modalContent: {
+ backgroundColor: '#121212',
+ padding: isSmallScreen ? 16 : 24,
+ borderTopLeftRadius: 24,
+ borderTopRightRadius: 24,
+ borderWidth: 1,
+ borderColor: '#222',
+ gap: 16,
+ },
+ modalTitle: {
+ color: '#FFFFFF',
+ fontSize: isSmallScreen ? 18 : 22,
+ fontWeight: 'bold',
+ },
+ amountInput: {
+ backgroundColor: '#1A1A1A',
+ borderWidth: 1,
+ borderColor: '#333',
+ borderRadius: 16,
+ paddingHorizontal: 16,
+ paddingVertical: 14,
+ color: '#FFFFFF',
+ fontSize: 16,
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ gap: 12,
+ },
+ modalBtn: {
+ flex: 1,
+ paddingVertical: isSmallScreen ? 14 : 16,
+ borderRadius: 16,
+ alignItems: 'center',
+ justifyContent: 'center',
+ minHeight: 48,
+ },
+ });
+}