mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-09 15:56:11 -04:00
Add generic ThemedContainer component (#846)
This commit is contained in:
@@ -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 (
|
||||
<ThemedScrollView>
|
||||
<ThemedView style={styles.header}>
|
||||
<CredentialIcon logo={credential.Logo} style={styles.logo} />
|
||||
<View style={styles.headerText}>
|
||||
<ThemedText type="title" style={styles.serviceName}>
|
||||
{credential.ServiceName}
|
||||
</ThemedText>
|
||||
{credential.ServiceUrl && (
|
||||
<TouchableOpacity onPress={() => Linking.openURL(credential.ServiceUrl!)}>
|
||||
<Text style={[styles.serviceUrl, { color: colors.primary }]}>
|
||||
{credential.ServiceUrl}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</ThemedView>
|
||||
<EmailPreview email={credential.Alias.Email} />
|
||||
<TotpSection credential={credential} />
|
||||
<NotesSection credential={credential} />
|
||||
<LoginCredentials credential={credential} />
|
||||
<AliasDetails credential={credential} />
|
||||
</ThemedScrollView>
|
||||
<ThemedContainer>
|
||||
<ThemedScrollView>
|
||||
<ThemedView style={styles.header}>
|
||||
<CredentialIcon logo={credential.Logo} style={styles.logo} />
|
||||
<View style={styles.headerText}>
|
||||
<ThemedText type="title" style={styles.serviceName}>
|
||||
{credential.ServiceName}
|
||||
</ThemedText>
|
||||
{credential.ServiceUrl && (
|
||||
<TouchableOpacity onPress={() => Linking.openURL(credential.ServiceUrl!)}>
|
||||
<Text style={[styles.serviceUrl, { color: colors.primary }]}>
|
||||
{credential.ServiceUrl}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</ThemedView>
|
||||
<EmailPreview email={credential.Alias.Email} />
|
||||
<TotpSection credential={credential} />
|
||||
<NotesSection credential={credential} />
|
||||
<LoginCredentials credential={credential} />
|
||||
<AliasDetails credential={credential} />
|
||||
</ThemedScrollView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -137,8 +140,7 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
marginTop: 6,
|
||||
padding: 16,
|
||||
paddingTop: 16,
|
||||
},
|
||||
headerRightButton: {
|
||||
padding: 10,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'}
|
||||
>
|
||||
<ThemedView style={styles.content}>
|
||||
<ThemedContainer>
|
||||
<KeyboardAwareScrollView
|
||||
enableOnAndroid={true}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
@@ -691,7 +683,7 @@ export default function AddEditCredentialScreen() : React.ReactNode {
|
||||
</>
|
||||
)}
|
||||
</KeyboardAwareScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
<AliasVaultToast />
|
||||
</KeyboardAvoidingView>
|
||||
</>
|
||||
|
||||
@@ -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<Credential[]>([]);
|
||||
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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<CollapsibleHeader
|
||||
title="Credentials"
|
||||
scrollY={scrollY}
|
||||
@@ -354,6 +347,6 @@ export default function CredentialsScreen() : React.ReactNode {
|
||||
}
|
||||
/>
|
||||
</ThemedView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<CollapsibleHeader
|
||||
title="Emails"
|
||||
scrollY={scrollY}
|
||||
@@ -250,6 +242,6 @@ export default function EmailsScreen() : React.ReactNode {
|
||||
<TitleContainer title="Emails" />
|
||||
{renderContent()}
|
||||
</Animated.ScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<ThemedScrollView>
|
||||
<View style={styles.header}>
|
||||
<ThemedText style={styles.headerText}>
|
||||
@@ -107,6 +103,6 @@ export default function AutoLockScreen() : React.ReactNode {
|
||||
})}
|
||||
</View>
|
||||
</ThemedScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -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<string>('');
|
||||
const [authMethodDisplay, setAuthMethodDisplay] = useState<string>('');
|
||||
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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<CollapsibleHeader
|
||||
title="Settings"
|
||||
scrollY={scrollY}
|
||||
@@ -324,6 +316,6 @@ export default function SettingsScreen() : React.ReactNode {
|
||||
<ThemedText style={styles.versionText}>App version {AppInfo.VERSION}</ThemedText>
|
||||
</View>
|
||||
</Animated.ScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<ThemedScrollView>
|
||||
<View style={styles.header}>
|
||||
<ThemedText style={styles.headerText}>
|
||||
@@ -146,6 +142,6 @@ export default function IosAutofillScreen() : React.ReactNode {
|
||||
</View>
|
||||
</View>
|
||||
</ThemedScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
refreshControl={
|
||||
@@ -217,6 +211,6 @@ export default function ActiveSessionsScreen() : React.ReactNode {
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
refreshControl={
|
||||
@@ -209,6 +203,6 @@ export default function AuthLogsScreen() : React.ReactNode {
|
||||
{renderContent()}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -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}
|
||||
>
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
@@ -182,7 +176,7 @@ export default function ChangePasswordScreen(): React.ReactNode {
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
</KeyboardAvoidingView>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
@@ -298,7 +292,7 @@ export default function DeleteAccountScreen(): React.ReactNode {
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
</KeyboardAvoidingView>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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<ScrollView>(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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<Animated.ScrollView
|
||||
ref={scrollViewRef}
|
||||
onScroll={Animated.event(
|
||||
@@ -141,6 +133,6 @@ export default function SecuritySettingsScreen() : React.ReactNode {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</Animated.ScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedContainer>
|
||||
<ThemedScrollView>
|
||||
<View style={styles.header}>
|
||||
<ThemedText style={styles.headerText}>
|
||||
@@ -229,6 +225,6 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode {
|
||||
</View>
|
||||
</View>
|
||||
</ThemedScrollView>
|
||||
</ThemedView>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
@@ -58,8 +58,7 @@ export const AliasDetails: React.FC<AliasDetailsProps> = ({ credential }) : Reac
|
||||
|
||||
const styles = {
|
||||
section: {
|
||||
padding: 16,
|
||||
paddingBottom: 0,
|
||||
paddingTop: 16,
|
||||
gap: 8,
|
||||
},
|
||||
};
|
||||
@@ -181,8 +181,7 @@ export const EmailPreview: React.FC<EmailPreviewProps> = ({ email }) : React.Rea
|
||||
marginBottom: 8,
|
||||
},
|
||||
section: {
|
||||
padding: 16,
|
||||
paddingBottom: 0,
|
||||
paddingTop: 16,
|
||||
},
|
||||
subject: {
|
||||
color: colors.text,
|
||||
|
||||
@@ -50,8 +50,7 @@ export const LoginCredentials: React.FC<LoginCredentialsProps> = ({ credential }
|
||||
|
||||
const styles = {
|
||||
section: {
|
||||
padding: 16,
|
||||
paddingBottom: 0,
|
||||
paddingTop: 16,
|
||||
gap: 8,
|
||||
},
|
||||
};
|
||||
@@ -93,8 +93,7 @@ export const NotesSection: React.FC<NotesSectionProps> = ({ credential }) : Reac
|
||||
},
|
||||
section: {
|
||||
gap: 8,
|
||||
padding: 16,
|
||||
paddingBottom: 8,
|
||||
paddingTop: 16,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -146,8 +146,7 @@ export const TotpSection: React.FC<TotpSectionProps> = ({ credential }) : React.
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
container: {
|
||||
marginTop: 16,
|
||||
padding: 16,
|
||||
paddingTop: 16,
|
||||
},
|
||||
content: {
|
||||
backgroundColor: colors.accentBackground,
|
||||
|
||||
24
apps/mobile-app/components/themed/ThemedContainer.tsx
Normal file
24
apps/mobile-app/components/themed/ThemedContainer.tsx
Normal file
@@ -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 <ThemedView style={[styles.container, style]} {...otherProps} />;
|
||||
}
|
||||
@@ -41,8 +41,7 @@ export function ThemedHeader(): React.ReactNode {
|
||||
<View style={[styles.headerBorder, { backgroundColor: colors.headerBorder }]} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
else if (Platform.OS === 'android') {
|
||||
} else if (Platform.OS === 'android') {
|
||||
return (
|
||||
<View style={[styles.header, styles.androidHeader]}>
|
||||
<View style={[styles.headerBorder, { backgroundColor: colors.headerBorder }]} />
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SymbolView, SymbolWeight } from 'expo-symbols';
|
||||
import { StyleProp, ViewStyle } from 'react-native';
|
||||
|
||||
import { IconSymbolName } from './IconSymbolName';
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user