From 574b5ff6932bb4b346b958de10df39fa2a2abe6e Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 23 May 2025 16:27:50 +0200 Subject: [PATCH] Add generic ThemedContainer component (#846) --- .../app/(tabs)/credentials/[id].tsx | 50 ++++++++++--------- .../app/(tabs)/credentials/_layout.tsx | 4 +- .../app/(tabs)/credentials/add-edit.tsx | 14 ++---- .../app/(tabs)/credentials/index.tsx | 15 ++---- apps/mobile-app/app/(tabs)/emails/index.tsx | 16 ++---- .../app/(tabs)/settings/auto-lock.tsx | 14 ++---- apps/mobile-app/app/(tabs)/settings/index.tsx | 16 ++---- .../app/(tabs)/settings/ios-autofill.tsx | 14 ++---- .../settings/security/active-sessions.tsx | 16 ++---- .../(tabs)/settings/security/auth-logs.tsx | 16 ++---- .../settings/security/change-password.tsx | 16 ++---- .../settings/security/delete-account.tsx | 16 ++---- .../app/(tabs)/settings/security/index.tsx | 18 ++----- .../app/(tabs)/settings/vault-unlock.tsx | 14 ++---- .../credentials/details/AliasDetails.tsx | 3 +- .../credentials/details/EmailPreview.tsx | 3 +- .../credentials/details/LoginCredentials.tsx | 3 +- .../credentials/details/NotesSection.tsx | 3 +- .../credentials/details/TotpSection.tsx | 3 +- .../components/themed/ThemedContainer.tsx | 24 +++++++++ .../components/themed/ThemedHeader.tsx | 9 ++-- .../components/themed/ThemedScrollView.tsx | 2 +- .../components/ui/IconSymbol.ios.tsx | 1 + 23 files changed, 119 insertions(+), 171 deletions(-) create mode 100644 apps/mobile-app/components/themed/ThemedContainer.tsx diff --git a/apps/mobile-app/app/(tabs)/credentials/[id].tsx b/apps/mobile-app/app/(tabs)/credentials/[id].tsx index 06ecbc22a..5199ef640 100644 --- a/apps/mobile-app/app/(tabs)/credentials/[id].tsx +++ b/apps/mobile-app/app/(tabs)/credentials/[id].tsx @@ -17,6 +17,7 @@ import { EmailPreview } from '@/components/credentials/details/EmailPreview'; import { TotpSection } from '@/components/credentials/details/TotpSection'; import { useColors } from '@/hooks/useColorScheme'; import emitter from '@/utils/EventEmitter'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Credential details screen. @@ -107,28 +108,30 @@ export default function CredentialDetailsScreen() : React.ReactNode { } return ( - - - - - - {credential.ServiceName} - - {credential.ServiceUrl && ( - Linking.openURL(credential.ServiceUrl!)}> - - {credential.ServiceUrl} - - - )} - - - - - - - - + + + + + + + {credential.ServiceName} + + {credential.ServiceUrl && ( + Linking.openURL(credential.ServiceUrl!)}> + + {credential.ServiceUrl} + + + )} + + + + + + + + + ); } @@ -137,8 +140,7 @@ const styles = StyleSheet.create({ alignItems: 'center', flexDirection: 'row', gap: 12, - marginTop: 6, - padding: 16, + paddingTop: 16, }, headerRightButton: { padding: 10, diff --git a/apps/mobile-app/app/(tabs)/credentials/_layout.tsx b/apps/mobile-app/app/(tabs)/credentials/_layout.tsx index 20efd4a48..2c9eb60b8 100644 --- a/apps/mobile-app/app/(tabs)/credentials/_layout.tsx +++ b/apps/mobile-app/app/(tabs)/credentials/_layout.tsx @@ -1,9 +1,7 @@ import { Stack } from 'expo-router'; -import { Platform, Text } from 'react-native'; -import { useRouter } from 'expo-router'; +import { Platform } from 'react-native'; import { defaultHeaderOptions } from '@/components/themed/ThemedHeader'; -import { AndroidHeader } from '@/components/ui/AndroidHeader'; /** * Credentials layout. diff --git a/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx b/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx index cc7d5bc15..7a304e18a 100644 --- a/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx +++ b/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx @@ -9,7 +9,6 @@ import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view import * as Haptics from 'expo-haptics'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { useDb } from '@/context/DbContext'; import { useWebApi } from '@/context/WebApiContext'; @@ -24,6 +23,7 @@ import { ValidatedFormField, ValidatedFormFieldRef } from '@/components/form/Val import { credentialSchema } from '@/utils/validationSchema'; import LoadingOverlay from '@/components/LoadingOverlay'; import { useAuth } from '@/context/AuthContext'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; type CredentialMode = 'random' | 'manual'; @@ -422,14 +422,6 @@ export default function AddEditCredentialScreen() : React.ReactNode { }; const styles = StyleSheet.create({ - container: { - flex: 1, - }, - content: { - flex: 1, - padding: 16, - paddingTop: 0, - }, contentContainer: { paddingBottom: 40, paddingTop: Platform.OS === 'ios' ? 76 : 56, @@ -547,7 +539,7 @@ export default function AddEditCredentialScreen() : React.ReactNode { style={styles.container} behavior={Platform.OS === 'ios' ? 'padding' : 'height'} > - + )} - + diff --git a/apps/mobile-app/app/(tabs)/credentials/index.tsx b/apps/mobile-app/app/(tabs)/credentials/index.tsx index 59c6d65c4..e2da89b27 100644 --- a/apps/mobile-app/app/(tabs)/credentials/index.tsx +++ b/apps/mobile-app/app/(tabs)/credentials/index.tsx @@ -4,7 +4,6 @@ import { useNavigation } from '@react-navigation/native'; import { useRouter } from 'expo-router'; import Toast from 'react-native-toast-message'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; import * as Haptics from 'expo-haptics'; import { ThemedText } from '@/components/themed/ThemedText'; @@ -22,6 +21,7 @@ import { AndroidHeader } from '@/components/ui/AndroidHeader'; import emitter from '@/utils/EventEmitter'; import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; import { useWebApi } from '@/context/WebApiContext'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Credentials screen. @@ -39,7 +39,6 @@ export default function CredentialsScreen() : React.ReactNode { const [credentialsList, setCredentialsList] = useState([]); const [isLoadingCredentials, setIsLoadingCredentials] = useMinDurationLoading(false, 200); const [refreshing, setRefreshing] = useMinDurationLoading(false, 200); - const insets = useSafeAreaInsets(); const authContext = useAuth(); const dbContext = useDb(); @@ -219,15 +218,9 @@ export default function CredentialsScreen() : React.ReactNode { color: colors.textMuted, fontSize: 20, }, - container: { - flex: 1, - paddingBottom: insets.bottom, - paddingHorizontal: 14, - paddingTop: Platform.OS === 'android' ? insets.top + 24 : insets.top, - }, contentContainer: { paddingBottom: 40, - paddingTop: 42, + paddingTop: Platform.OS === 'ios' ? 42 : 0, }, emptyText: { color: colors.textMuted, @@ -274,7 +267,7 @@ export default function CredentialsScreen() : React.ReactNode { }, [navigation, headerButtons]); return ( - + - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/emails/index.tsx b/apps/mobile-app/app/(tabs)/emails/index.tsx index afc6d4fac..576ad9bc0 100644 --- a/apps/mobile-app/app/(tabs)/emails/index.tsx +++ b/apps/mobile-app/app/(tabs)/emails/index.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useState, useCallback, useRef } from 'react'; import { StyleSheet, View, ScrollView, RefreshControl, Animated , Platform } from 'react-native'; import { useNavigation } from 'expo-router'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Toast from 'react-native-toast-message'; import * as Haptics from 'expo-haptics'; @@ -14,12 +13,12 @@ import { CollapsibleHeader } from '@/components/ui/CollapsibleHeader'; import { MailboxBulkRequest, MailboxBulkResponse } from '@/utils/types/webapi/MailboxBulk'; import EncryptionUtility from '@/utils/EncryptionUtility'; import { useColors } from '@/hooks/useColorScheme'; -import { ThemedView } from '@/components/themed/ThemedView'; import { EmailCard } from '@/components/EmailCard'; import { SkeletonLoader } from '@/components/ui/SkeletonLoader'; import emitter from '@/utils/EventEmitter'; import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; import { useAuth } from '@/context/AuthContext'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Emails screen. */ @@ -36,7 +35,6 @@ export default function EmailsScreen() : React.ReactNode { const [isLoading, setIsLoading] = useMinDurationLoading(true, 200); const [isRefreshing, setIsRefreshing] = useMinDurationLoading(false, 200); const [isTabFocused, setIsTabFocused] = useState(false); - const insets = useSafeAreaInsets(); /** * Load emails. @@ -155,15 +153,9 @@ export default function EmailsScreen() : React.ReactNode { justifyContent: 'center', padding: 20, }, - container: { - flex: 1, - paddingBottom: insets.bottom, - paddingHorizontal: 14, - paddingTop: Platform.OS === 'android' ? insets.top + 24 : insets.top, - }, contentContainer: { paddingBottom: 40, - paddingTop: 42, + paddingTop: Platform.OS === 'ios' ? 42 : 0, }, emptyText: { color: colors.textMuted, @@ -223,7 +215,7 @@ export default function EmailsScreen() : React.ReactNode { }; return ( - + {renderContent()} - + ); } diff --git a/apps/mobile-app/app/(tabs)/settings/auto-lock.tsx b/apps/mobile-app/app/(tabs)/settings/auto-lock.tsx index c6400905b..66dcee38f 100644 --- a/apps/mobile-app/app/(tabs)/settings/auto-lock.tsx +++ b/apps/mobile-app/app/(tabs)/settings/auto-lock.tsx @@ -3,10 +3,10 @@ import { Ionicons } from '@expo/vector-icons'; import { useEffect, useState } from 'react'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { useAuth } from '@/context/AuthContext'; import { ThemedScrollView } from '@/components/themed/ThemedScrollView'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Auto-lock screen. @@ -40,12 +40,8 @@ export default function AutoLockScreen() : React.ReactNode { ]; const styles = StyleSheet.create({ - container: { - flex: 1, - }, header: { - padding: 16, - paddingBottom: 0, + paddingTop: 12, }, headerText: { color: colors.textMuted, @@ -62,7 +58,7 @@ export default function AutoLockScreen() : React.ReactNode { optionContainer: { backgroundColor: colors.accentBackground, borderRadius: 10, - margin: 16, + marginTop: 16, }, optionLast: { borderBottomWidth: 0, @@ -79,7 +75,7 @@ export default function AutoLockScreen() : React.ReactNode { }); return ( - + @@ -107,6 +103,6 @@ export default function AutoLockScreen() : React.ReactNode { })} - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/settings/index.tsx b/apps/mobile-app/app/(tabs)/settings/index.tsx index 056d2b454..f0741ec92 100644 --- a/apps/mobile-app/app/(tabs)/settings/index.tsx +++ b/apps/mobile-app/app/(tabs)/settings/index.tsx @@ -2,10 +2,8 @@ import { StyleSheet, View, ScrollView, TouchableOpacity, Animated, Platform, Ale import { router, useFocusEffect } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { useRef, useState, useCallback } from 'react'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useWebApi } from '@/context/WebApiContext'; import { AppInfo } from '@/utils/AppInfo'; import { useColors } from '@/hooks/useColorScheme'; @@ -15,6 +13,7 @@ import { CollapsibleHeader } from '@/components/ui/CollapsibleHeader'; import { InlineSkeletonLoader } from '@/components/ui/InlineSkeletonLoader'; import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; import { UsernameDisplay } from '@/components/ui/UsernameDisplay'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Settings screen. @@ -29,7 +28,6 @@ export default function SettingsScreen() : React.ReactNode { const [autoLockDisplay, setAutoLockDisplay] = useState(''); const [authMethodDisplay, setAuthMethodDisplay] = useState(''); const [isFirstLoad, setIsFirstLoad] = useMinDurationLoading(true, 100); - const insets = useSafeAreaInsets(); useFocusEffect( useCallback(() => { @@ -126,15 +124,9 @@ export default function SettingsScreen() : React.ReactNode { }; const styles = StyleSheet.create({ - container: { - flex: 1, - paddingBottom: insets.bottom, - paddingHorizontal: 14, - paddingTop: Platform.OS === 'android' ? insets.top + 24 : insets.top, - }, scrollContent: { paddingBottom: 40, - paddingTop: 42, + paddingTop: Platform.OS === 'ios' ? 42 : 0, }, scrollView: { flex: 1, @@ -212,7 +204,7 @@ export default function SettingsScreen() : React.ReactNode { }); return ( - + App version {AppInfo.VERSION} - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/settings/ios-autofill.tsx b/apps/mobile-app/app/(tabs)/settings/ios-autofill.tsx index 72d31a87b..b5888c469 100644 --- a/apps/mobile-app/app/(tabs)/settings/ios-autofill.tsx +++ b/apps/mobile-app/app/(tabs)/settings/ios-autofill.tsx @@ -2,10 +2,10 @@ import { StyleSheet, View, TouchableOpacity, Linking } from 'react-native'; import { router } from 'expo-router'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { useAuth } from '@/context/AuthContext'; import { ThemedScrollView } from '@/components/themed/ThemedScrollView'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * iOS autofill screen. @@ -47,19 +47,15 @@ export default function IosAutofillScreen() : React.ReactNode { fontSize: 16, fontWeight: '600', }, - container: { - flex: 1, - }, header: { - padding: 16, - paddingBottom: 0, + paddingTop: 12, }, headerText: { color: colors.textMuted, fontSize: 13, }, instructionContainer: { - padding: 16, + paddingTop: 16, }, instructionStep: { color: colors.text, @@ -94,7 +90,7 @@ export default function IosAutofillScreen() : React.ReactNode { }); return ( - + @@ -146,6 +142,6 @@ export default function IosAutofillScreen() : React.ReactNode { - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/settings/security/active-sessions.tsx b/apps/mobile-app/app/(tabs)/settings/security/active-sessions.tsx index b6c3b2d5a..e2fb98096 100644 --- a/apps/mobile-app/app/(tabs)/settings/security/active-sessions.tsx +++ b/apps/mobile-app/app/(tabs)/settings/security/active-sessions.tsx @@ -5,12 +5,12 @@ import * as Haptics from 'expo-haptics'; import Toast from 'react-native-toast-message'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { useWebApi } from '@/context/WebApiContext'; import { RefreshToken } from '@/utils/types/webapi/RefreshToken'; import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; import { SkeletonLoader } from '@/components/ui/SkeletonLoader'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Active sessions screen. */ @@ -24,15 +24,9 @@ export default function ActiveSessionsScreen() : React.ReactNode { const [isRefreshing, setIsRefreshing] = useMinDurationLoading(false, 200); const styles = StyleSheet.create({ - container: { - flex: 1, - marginTop: 42, - paddingBottom: insets.bottom, - paddingHorizontal: 14, - paddingTop: insets.top, - }, contentContainer: { paddingBottom: 40, + paddingTop: Platform.OS === 'ios' ? insets.top : 0, }, detailText: { color: colors.textMuted, @@ -57,7 +51,7 @@ export default function ActiveSessionsScreen() : React.ReactNode { textAlign: 'center', }, header: { - paddingTop: 16 + paddingTop: 12 }, headerText: { color: colors.textMuted, @@ -175,7 +169,7 @@ export default function ActiveSessionsScreen() : React.ReactNode { }; return ( - + - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/settings/security/auth-logs.tsx b/apps/mobile-app/app/(tabs)/settings/security/auth-logs.tsx index 3d1821390..c66632f27 100644 --- a/apps/mobile-app/app/(tabs)/settings/security/auth-logs.tsx +++ b/apps/mobile-app/app/(tabs)/settings/security/auth-logs.tsx @@ -5,13 +5,13 @@ import * as Haptics from 'expo-haptics'; import Toast from 'react-native-toast-message'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { useWebApi } from '@/context/WebApiContext'; import { SkeletonLoader } from '@/components/ui/SkeletonLoader'; import { AuthLogModel } from '@/utils/types/webapi/AuthLog'; import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; import { AuthEventType } from '@/utils/types/webapi/AuthEventType'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Auth logs screen. @@ -26,15 +26,9 @@ export default function AuthLogsScreen() : React.ReactNode { const [isRefreshing, setIsRefreshing] = useMinDurationLoading(false, 200); const styles = StyleSheet.create({ - container: { - flex: 1, - marginTop: 42, - paddingBottom: insets.bottom, - paddingHorizontal: 14, - paddingTop: insets.top, - }, contentContainer: { paddingBottom: 40, + paddingTop: Platform.OS === 'ios' ? insets.top : 0, }, detailText: { color: colors.textMuted, @@ -57,7 +51,7 @@ export default function AuthLogsScreen() : React.ReactNode { fontWeight: '600', }, header: { - paddingTop: 16, + paddingTop: 12, }, headerText: { color: colors.textMuted, @@ -188,7 +182,7 @@ export default function AuthLogsScreen() : React.ReactNode { }; return ( - + - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/settings/security/change-password.tsx b/apps/mobile-app/app/(tabs)/settings/security/change-password.tsx index 325ff7798..914bd06bd 100644 --- a/apps/mobile-app/app/(tabs)/settings/security/change-password.tsx +++ b/apps/mobile-app/app/(tabs)/settings/security/change-password.tsx @@ -4,7 +4,6 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { router } from 'expo-router'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { ThemedTextInput } from '@/components/themed/ThemedTextInput'; import { ThemedButton } from '@/components/themed/ThemedButton'; @@ -12,6 +11,7 @@ import { useAuth } from '@/context/AuthContext'; import { useVaultMutate } from '@/hooks/useVaultMutate'; import LoadingOverlay from '@/components/LoadingOverlay'; import { UsernameDisplay } from '@/components/ui/UsernameDisplay'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Change password screen. @@ -33,15 +33,9 @@ export default function ChangePasswordScreen(): React.ReactNode { button: { marginTop: 8, }, - container: { - flex: 1, - marginTop: 42, - paddingBottom: insets.bottom, - paddingHorizontal: 14, - paddingTop: insets.top, - }, contentContainer: { paddingBottom: 40, + paddingTop: Platform.OS === 'ios' ? insets.top : 0, }, form: { backgroundColor: colors.accentBackground, @@ -50,7 +44,7 @@ export default function ChangePasswordScreen(): React.ReactNode { padding: 16, }, header: { - paddingTop: 16, + paddingTop: 12, }, headerText: { color: colors.textMuted, @@ -132,7 +126,7 @@ export default function ChangePasswordScreen(): React.ReactNode { behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.keyboardAvoidingView} > - + - + ); diff --git a/apps/mobile-app/app/(tabs)/settings/security/delete-account.tsx b/apps/mobile-app/app/(tabs)/settings/security/delete-account.tsx index 11490d813..61b6698d8 100644 --- a/apps/mobile-app/app/(tabs)/settings/security/delete-account.tsx +++ b/apps/mobile-app/app/(tabs)/settings/security/delete-account.tsx @@ -5,7 +5,6 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import srp from 'secure-remote-password/client'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { ThemedTextInput } from '@/components/themed/ThemedTextInput'; import { ThemedButton } from '@/components/themed/ThemedButton'; @@ -15,6 +14,7 @@ import { DeleteAccountInitiateRequest, DeleteAccountInitiateResponse } from '@/u import { DeleteAccountRequest } from '@/utils/types/webapi/DeleteAccountRequest'; import { UsernameDisplay } from '@/components/ui/UsernameDisplay'; import LoadingOverlay from '@/components/LoadingOverlay'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Delete account screen. @@ -35,15 +35,9 @@ export default function DeleteAccountScreen(): React.ReactNode { button: { marginTop: 16, }, - container: { - flex: 1, - marginTop: 42, - paddingBottom: insets.bottom, - paddingHorizontal: 16, - paddingTop: insets.top, - }, contentContainer: { paddingBottom: 40, + paddingTop: Platform.OS === 'ios' ? insets.top : 0, }, form: { backgroundColor: colors.accentBackground, @@ -54,7 +48,7 @@ export default function DeleteAccountScreen(): React.ReactNode { padding: 20, }, header: { - paddingTop: 16, + paddingTop: 12, }, headerText: { color: colors.textMuted, @@ -238,7 +232,7 @@ export default function DeleteAccountScreen(): React.ReactNode { behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.keyboardAvoidingView} > - + - + ); diff --git a/apps/mobile-app/app/(tabs)/settings/security/index.tsx b/apps/mobile-app/app/(tabs)/settings/security/index.tsx index c7737adcb..e78f456a0 100644 --- a/apps/mobile-app/app/(tabs)/settings/security/index.tsx +++ b/apps/mobile-app/app/(tabs)/settings/security/index.tsx @@ -1,33 +1,25 @@ -import { StyleSheet, View, TouchableOpacity, Animated, ScrollView } from 'react-native'; +import { StyleSheet, View, TouchableOpacity, Animated, ScrollView, Platform } from 'react-native'; import { router } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { useRef } from 'react'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { ThemedText } from '@/components/themed/ThemedText'; -import { ThemedView } from '@/components/themed/ThemedView'; import { useColors } from '@/hooks/useColorScheme'; import { SettingsHeader } from '@/components/ui/SettingsHeader'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Security settings screen. */ export default function SecuritySettingsScreen() : React.ReactNode { const colors = useColors(); - const insets = useSafeAreaInsets(); const scrollY = useRef(new Animated.Value(0)).current; const scrollViewRef = useRef(null); const styles = StyleSheet.create({ - container: { - flex: 1, - paddingBottom: insets.bottom, - paddingHorizontal: 14, - paddingTop: insets.top, - }, scrollContent: { paddingBottom: 40, - paddingTop: 42, + paddingTop: Platform.OS === 'ios' ? 42 : 0, }, scrollView: { flex: 1, @@ -71,7 +63,7 @@ export default function SecuritySettingsScreen() : React.ReactNode { }); return ( - + - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx b/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx index e37d0b43c..5c1552c78 100644 --- a/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx +++ b/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx @@ -7,7 +7,7 @@ import { ThemedText } from '@/components/themed/ThemedText'; import { useColors } from '@/hooks/useColorScheme'; import { AuthMethod, useAuth } from '@/context/AuthContext'; import { ThemedScrollView } from '@/components/themed/ThemedScrollView'; -import { ThemedView } from '@/components/themed/ThemedView'; +import { ThemedContainer } from '@/components/themed/ThemedContainer'; /** * Vault unlock settings screen. @@ -134,16 +134,12 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { }, [hasBiometrics, setAuthMethods, biometricDisplayName]); const styles = StyleSheet.create({ - container: { - flex: 1, - }, disabledText: { color: colors.textMuted, opacity: 0.5, }, header: { - padding: 16, - paddingBottom: 0, + paddingTop: 12, }, headerText: { color: colors.textMuted, @@ -163,7 +159,7 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { optionContainer: { backgroundColor: colors.accentBackground, borderRadius: 10, - margin: 16, + marginTop: 16, }, optionHeader: { alignItems: 'center', @@ -181,7 +177,7 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { }); return ( - + @@ -229,6 +225,6 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { - + ); } \ No newline at end of file diff --git a/apps/mobile-app/components/credentials/details/AliasDetails.tsx b/apps/mobile-app/components/credentials/details/AliasDetails.tsx index c9547ecb4..12aa41d4b 100644 --- a/apps/mobile-app/components/credentials/details/AliasDetails.tsx +++ b/apps/mobile-app/components/credentials/details/AliasDetails.tsx @@ -58,8 +58,7 @@ export const AliasDetails: React.FC = ({ credential }) : Reac const styles = { section: { - padding: 16, - paddingBottom: 0, + paddingTop: 16, gap: 8, }, }; \ No newline at end of file diff --git a/apps/mobile-app/components/credentials/details/EmailPreview.tsx b/apps/mobile-app/components/credentials/details/EmailPreview.tsx index db9fe5530..2779ef025 100644 --- a/apps/mobile-app/components/credentials/details/EmailPreview.tsx +++ b/apps/mobile-app/components/credentials/details/EmailPreview.tsx @@ -181,8 +181,7 @@ export const EmailPreview: React.FC = ({ email }) : React.Rea marginBottom: 8, }, section: { - padding: 16, - paddingBottom: 0, + paddingTop: 16, }, subject: { color: colors.text, diff --git a/apps/mobile-app/components/credentials/details/LoginCredentials.tsx b/apps/mobile-app/components/credentials/details/LoginCredentials.tsx index c2da79e1f..331ae2caf 100644 --- a/apps/mobile-app/components/credentials/details/LoginCredentials.tsx +++ b/apps/mobile-app/components/credentials/details/LoginCredentials.tsx @@ -50,8 +50,7 @@ export const LoginCredentials: React.FC = ({ credential } const styles = { section: { - padding: 16, - paddingBottom: 0, + paddingTop: 16, gap: 8, }, }; \ No newline at end of file diff --git a/apps/mobile-app/components/credentials/details/NotesSection.tsx b/apps/mobile-app/components/credentials/details/NotesSection.tsx index 436923a9f..eaef90d34 100644 --- a/apps/mobile-app/components/credentials/details/NotesSection.tsx +++ b/apps/mobile-app/components/credentials/details/NotesSection.tsx @@ -93,8 +93,7 @@ export const NotesSection: React.FC = ({ credential }) : Reac }, section: { gap: 8, - padding: 16, - paddingBottom: 8, + paddingTop: 16, }, }); diff --git a/apps/mobile-app/components/credentials/details/TotpSection.tsx b/apps/mobile-app/components/credentials/details/TotpSection.tsx index 89f121869..750dab3fe 100644 --- a/apps/mobile-app/components/credentials/details/TotpSection.tsx +++ b/apps/mobile-app/components/credentials/details/TotpSection.tsx @@ -146,8 +146,7 @@ export const TotpSection: React.FC = ({ credential }) : React. justifyContent: 'space-between', }, container: { - marginTop: 16, - padding: 16, + paddingTop: 16, }, content: { backgroundColor: colors.accentBackground, diff --git a/apps/mobile-app/components/themed/ThemedContainer.tsx b/apps/mobile-app/components/themed/ThemedContainer.tsx new file mode 100644 index 000000000..a54b478e8 --- /dev/null +++ b/apps/mobile-app/components/themed/ThemedContainer.tsx @@ -0,0 +1,24 @@ +import { Platform, StyleSheet, type ViewProps } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { ThemedView } from '@/components/themed/ThemedView'; + +export type ThemedContainerProps = ViewProps; + +/** + * Themed container component which should be the outermost component of a screen. + * It handles the safe area insets and the padding for the screen. + */ +export function ThemedContainer({ style, ...otherProps }: ThemedContainerProps): React.ReactNode { + const insets = useSafeAreaInsets(); + + const styles = StyleSheet.create({ + container: { + flex: 1, + paddingBottom: insets.bottom, + paddingHorizontal: 14, + paddingTop: Platform.OS === 'ios' ? insets.top : 0, + }, + }); + return ; +} diff --git a/apps/mobile-app/components/themed/ThemedHeader.tsx b/apps/mobile-app/components/themed/ThemedHeader.tsx index 2b2aa4f4f..5d8752fe4 100644 --- a/apps/mobile-app/components/themed/ThemedHeader.tsx +++ b/apps/mobile-app/components/themed/ThemedHeader.tsx @@ -41,8 +41,7 @@ export function ThemedHeader(): React.ReactNode { ); - } - else if (Platform.OS === 'android') { + } else if (Platform.OS === 'android') { return ( @@ -59,7 +58,11 @@ export function ThemedHeader(): React.ReactNode { * @returns {Object} The default header options */ export const defaultHeaderOptions = { - headerTransparent: true, + /** + * On iOS, we want the header to be transparent. + * On Android, we want the header to be opaque. + */ + headerTransparent: Platform.OS === 'ios' ? true : false, /** * Header background component that provides consistent styling. * @returns {React.ReactNode} The themed header background component diff --git a/apps/mobile-app/components/themed/ThemedScrollView.tsx b/apps/mobile-app/components/themed/ThemedScrollView.tsx index 7eebf2a66..9db33833a 100644 --- a/apps/mobile-app/components/themed/ThemedScrollView.tsx +++ b/apps/mobile-app/components/themed/ThemedScrollView.tsx @@ -16,7 +16,7 @@ type ThemedScrollViewProps = { export function ThemedScrollView({ style, lightColor, darkColor, ...otherProps }: ThemedScrollViewProps): React.ReactNode { const insets = useSafeAreaInsets(); - const paddingTop = Platform.OS === 'ios' ? insets.top + 36 : 64; + const paddingTop = Platform.OS === 'ios' ? insets.top : 0; const paddingBottom = Platform.OS === 'ios' ? insets.bottom + 60 : 0; return ( diff --git a/apps/mobile-app/components/ui/IconSymbol.ios.tsx b/apps/mobile-app/components/ui/IconSymbol.ios.tsx index 5df07a46b..b87900e2e 100644 --- a/apps/mobile-app/components/ui/IconSymbol.ios.tsx +++ b/apps/mobile-app/components/ui/IconSymbol.ios.tsx @@ -1,5 +1,6 @@ import { SymbolView, SymbolWeight } from 'expo-symbols'; import { StyleProp, ViewStyle } from 'react-native'; + import { IconSymbolName } from './IconSymbolName'; /**