mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-24 16:32:20 -04:00
Add translucent headers (#771)
This commit is contained in:
@@ -1,14 +1,12 @@
|
||||
import { Stack } from 'expo-router';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { defaultHeaderOptions } from '@/components/themed/ThemedHeader';
|
||||
|
||||
/**
|
||||
* Credentials layout.
|
||||
*/
|
||||
export default function CredentialsLayout() : React.ReactNode {
|
||||
const colors = useColors();
|
||||
|
||||
export default function CredentialsLayout(): React.ReactNode {
|
||||
return (
|
||||
<Stack>
|
||||
<Stack.Screen
|
||||
@@ -23,10 +21,8 @@ export default function CredentialsLayout() : React.ReactNode {
|
||||
title: 'Add Credential',
|
||||
presentation: Platform.OS === 'ios' ? 'modal' : 'card',
|
||||
headerShown: true,
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
gestureEnabled: true,
|
||||
...defaultHeaderOptions,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -35,9 +31,7 @@ export default function CredentialsLayout() : React.ReactNode {
|
||||
title: 'Credential Created',
|
||||
presentation: Platform.OS === 'ios' ? 'modal' : 'card',
|
||||
headerShown: true,
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
...defaultHeaderOptions,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -45,9 +39,7 @@ export default function CredentialsLayout() : React.ReactNode {
|
||||
options={{
|
||||
title: 'Credential Details',
|
||||
headerShown: true,
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
...defaultHeaderOptions,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -55,9 +47,6 @@ export default function CredentialsLayout() : React.ReactNode {
|
||||
options={{
|
||||
title: 'Email Preview',
|
||||
headerShown: true,
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { StyleSheet, View, TouchableOpacity, Alert, Keyboard, ScrollView } from 'react-native';
|
||||
import { StyleSheet, View, TouchableOpacity, Alert, Keyboard, KeyboardAvoidingView, Platform } from 'react-native';
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useLocalSearchParams, useNavigation, useRouter } from 'expo-router';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
import Toast from 'react-native-toast-message';
|
||||
import { Resolver, useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
||||
|
||||
import { ThemedText } from '@/components/themed/ThemedText';
|
||||
import { ThemedView } from '@/components/themed/ThemedView';
|
||||
import { ThemedSafeAreaView } from '@/components/themed/ThemedSafeAreaView';
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { useDb } from '@/context/DbContext';
|
||||
import { useWebApi } from '@/context/WebApiContext';
|
||||
@@ -404,10 +404,13 @@ export default function AddEditCredentialScreen() : React.ReactNode {
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
marginTop: 36,
|
||||
padding: 16,
|
||||
paddingTop: 0,
|
||||
},
|
||||
contentContainer: {
|
||||
paddingBottom: 40,
|
||||
paddingTop: Platform.OS === 'ios' ? 76 : 56,
|
||||
},
|
||||
deleteButton: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.errorBackground,
|
||||
@@ -441,7 +444,6 @@ export default function AddEditCredentialScreen() : React.ReactNode {
|
||||
},
|
||||
headerLeftButtonText: {
|
||||
color: colors.primary,
|
||||
fontSize: 20,
|
||||
},
|
||||
headerRightButton: {
|
||||
padding: 10,
|
||||
@@ -518,9 +520,17 @@ export default function AddEditCredentialScreen() : React.ReactNode {
|
||||
{(isLoading) && (
|
||||
<LoadingOverlay status={syncStatus} />
|
||||
)}
|
||||
<ThemedSafeAreaView style={styles.container}>
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
keyboardVerticalOffset={Platform.OS === 'ios' ? 140 : 0} // adjust offset as needed
|
||||
>
|
||||
<ThemedView style={styles.content}>
|
||||
<ScrollView
|
||||
<KeyboardAwareScrollView
|
||||
enableOnAndroid={true}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
extraScrollHeight={0}
|
||||
>
|
||||
{!isEditMode && (
|
||||
<View style={styles.modeSelector}>
|
||||
@@ -658,10 +668,10 @@ export default function AddEditCredentialScreen() : React.ReactNode {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
</KeyboardAwareScrollView>
|
||||
</ThemedView>
|
||||
<AliasVaultToast />
|
||||
</ThemedSafeAreaView>
|
||||
</KeyboardAvoidingView>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import EncryptionUtility from '@/utils/EncryptionUtility';
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { IconSymbol } from '@/components/ui/IconSymbol';
|
||||
import emitter from '@/utils/EventEmitter';
|
||||
import { ThemedView } from '@/components/themed/ThemedView';
|
||||
|
||||
/**
|
||||
* Email details screen.
|
||||
@@ -297,12 +298,6 @@ export default function EmailDetailsScreen() : React.ReactNode {
|
||||
flexDirection: 'row',
|
||||
padding: 2,
|
||||
},
|
||||
viewDark: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
viewLight: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
webView: {
|
||||
flex: 1,
|
||||
},
|
||||
@@ -323,7 +318,7 @@ export default function EmailDetailsScreen() : React.ReactNode {
|
||||
<Ionicons
|
||||
name={isHtmlView ? 'text-outline' : 'document-outline'}
|
||||
size={22}
|
||||
color="#FFA500"
|
||||
color={colors.primary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
@@ -335,30 +330,30 @@ export default function EmailDetailsScreen() : React.ReactNode {
|
||||
</View>
|
||||
),
|
||||
});
|
||||
}, [isHtmlView, navigation, handleDelete, styles.headerRightButton, styles.headerRightContainer]);
|
||||
}, [isHtmlView, navigation, handleDelete, colors.primary, styles.headerRightButton, styles.headerRightContainer]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<View style={[styles.centerContainer, isDarkMode ? styles.viewDark : styles.viewLight]}>
|
||||
<ThemedView style={styles.centerContainer}>
|
||||
<Stack.Screen options={{ title: 'Email Details' }} />
|
||||
<ActivityIndicator size="large" />
|
||||
</View>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<View style={[styles.centerContainer, isDarkMode ? styles.viewDark : styles.viewLight]}>
|
||||
<ThemedView style={styles.centerContainer}>
|
||||
<ThemedText style={styles.errorText}>Error: {error}</ThemedText>
|
||||
</View>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
return (
|
||||
<View style={[styles.centerContainer, isDarkMode ? styles.viewDark : styles.viewLight]}>
|
||||
<ThemedView style={styles.centerContainer}>
|
||||
<ThemedText style={styles.emptyText}>Email not found</ThemedText>
|
||||
</View>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -471,7 +466,7 @@ export default function EmailDetailsScreen() : React.ReactNode {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.container, isDarkMode ? styles.viewDark : styles.viewLight]}>
|
||||
<ThemedView style={styles.container}>
|
||||
<Stack.Screen options={{ title: 'Email Details' }} />
|
||||
{metadataView}
|
||||
{emailView}
|
||||
@@ -492,6 +487,6 @@ export default function EmailDetailsScreen() : React.ReactNode {
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
import { Stack } from 'expo-router';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
|
||||
/**
|
||||
* Emails layout.
|
||||
*/
|
||||
export default function EmailsLayout() : React.ReactNode {
|
||||
const colors = useColors();
|
||||
|
||||
export default function EmailsLayout(): React.ReactNode {
|
||||
return (
|
||||
<Stack>
|
||||
<Stack.Screen
|
||||
@@ -21,9 +17,6 @@ export default function EmailsLayout() : React.ReactNode {
|
||||
options={{
|
||||
title: 'Email',
|
||||
headerShown: true,
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { Stack } from 'expo-router';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { defaultHeaderOptions } from '@/components/themed/ThemedHeader';
|
||||
|
||||
/**
|
||||
* Settings layout.
|
||||
*/
|
||||
export default function SettingsLayout() : React.ReactNode {
|
||||
const colors = useColors();
|
||||
|
||||
export default function SettingsLayout(): React.ReactNode {
|
||||
return (
|
||||
<Stack>
|
||||
<Stack.Screen
|
||||
@@ -21,9 +19,8 @@ export default function SettingsLayout() : React.ReactNode {
|
||||
options={{
|
||||
title: 'iOS Autofill',
|
||||
headerBackTitle: 'Settings',
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
headerShown: true,
|
||||
...defaultHeaderOptions,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -31,9 +28,8 @@ export default function SettingsLayout() : React.ReactNode {
|
||||
options={{
|
||||
title: 'Vault Unlock Method',
|
||||
headerBackTitle: 'Settings',
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
headerShown: true,
|
||||
...defaultHeaderOptions,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -41,9 +37,8 @@ export default function SettingsLayout() : React.ReactNode {
|
||||
options={{
|
||||
title: 'Auto-lock Settings',
|
||||
headerBackTitle: 'Settings',
|
||||
headerStyle: {
|
||||
backgroundColor: colors.headerBackground,
|
||||
},
|
||||
headerShown: true,
|
||||
...defaultHeaderOptions,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -4,10 +4,10 @@ import { useState, useEffect, useCallback } from 'react';
|
||||
import Toast from 'react-native-toast-message';
|
||||
|
||||
import { ThemedText } from '@/components/themed/ThemedText';
|
||||
import { ThemedSafeAreaView } from '@/components/themed/ThemedSafeAreaView';
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { AuthMethod, useAuth } from '@/context/AuthContext';
|
||||
import { ThemedScrollView } from '@/components/themed/ThemedScrollView';
|
||||
import { ThemedView } from '@/components/themed/ThemedView';
|
||||
|
||||
/**
|
||||
* Vault unlock settings screen.
|
||||
@@ -166,7 +166,7 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode {
|
||||
});
|
||||
|
||||
return (
|
||||
<ThemedSafeAreaView style={styles.container}>
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedScrollView>
|
||||
<View style={styles.header}>
|
||||
<ThemedText style={styles.headerText}>
|
||||
@@ -214,6 +214,6 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode {
|
||||
</View>
|
||||
</View>
|
||||
</ThemedScrollView>
|
||||
</ThemedSafeAreaView>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
@@ -53,6 +53,7 @@ function RootLayoutNav() : React.ReactNode {
|
||||
<Stack screenOptions={{
|
||||
headerShown: true,
|
||||
animation: 'none',
|
||||
headerTransparent: true,
|
||||
headerStyle: {
|
||||
backgroundColor: colors.accentBackground,
|
||||
},
|
||||
|
||||
57
apps/mobile-app/components/themed/ThemedHeader.tsx
Normal file
57
apps/mobile-app/components/themed/ThemedHeader.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Platform, StyleSheet, useColorScheme, View } from 'react-native';
|
||||
import { BlurView } from 'expo-blur';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
|
||||
/**
|
||||
* ThemedHeader component that provides consistent header styling across the app.
|
||||
* This component is used as a headerBackground in Stack.Screen options.
|
||||
* @returns {React.ReactNode} The themed header component
|
||||
*/
|
||||
export function ThemedHeader(): React.ReactNode {
|
||||
const colorScheme = useColorScheme();
|
||||
const colors = useColors();
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
header: {
|
||||
flex: 1,
|
||||
},
|
||||
headerBorder: {
|
||||
backgroundColor: colors.headerBorder,
|
||||
bottom: 0,
|
||||
height: StyleSheet.hairlineWidth,
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
},
|
||||
});
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
return (
|
||||
<View style={styles.header}>
|
||||
<BlurView
|
||||
tint={colorScheme === 'dark' ? 'dark' : 'light'}
|
||||
intensity={colorScheme === 'dark' ? 90 : 100}
|
||||
style={[StyleSheet.absoluteFill, { backgroundColor: colors.headerBackground }]}
|
||||
/>
|
||||
<View style={[styles.headerBorder, { backgroundColor: colors.headerBorder }]} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default header options for Stack.Screen components.
|
||||
* This provides consistent header styling across the app.
|
||||
* @returns {Object} The default header options
|
||||
*/
|
||||
export const defaultHeaderOptions = {
|
||||
headerTransparent: true,
|
||||
/**
|
||||
* Header background component that provides consistent styling.
|
||||
* @returns {React.ReactNode} The themed header background component
|
||||
*/
|
||||
headerBackground: (): React.ReactNode => <ThemedHeader />,
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ScrollView, StyleProp, StyleSheet, ViewStyle } from 'react-native';
|
||||
import { Platform, ScrollView, StyleProp, StyleSheet, ViewStyle } from 'react-native';
|
||||
|
||||
type ThemedScrollViewProps = {
|
||||
style?: StyleProp<ViewStyle>;
|
||||
@@ -23,10 +23,13 @@ export function ThemedScrollView({ style, lightColor, darkColor, ...otherProps }
|
||||
);
|
||||
}
|
||||
|
||||
const HEADER_HEIGHT = Platform.OS === 'ios' ? 96 : 56;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
marginBottom: 80,
|
||||
paddingTop: HEADER_HEIGHT,
|
||||
},
|
||||
contentContainer: {
|
||||
paddingBottom: 40,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, Platform, Animated, TouchableOpacity, useColorScheme } from 'react-native';
|
||||
import { StyleSheet, Platform, Animated, TouchableOpacity, useColorScheme, View } from 'react-native';
|
||||
import { Stack } from 'expo-router';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
import { BlurView } from 'expo-blur';
|
||||
@@ -89,7 +89,7 @@ export function CollapsibleHeader({
|
||||
flex: 1,
|
||||
},
|
||||
headerBorder: {
|
||||
backgroundColor: colors.accentBorder,
|
||||
backgroundColor: colors.headerBorder,
|
||||
bottom: 0,
|
||||
height: 1,
|
||||
left: 0,
|
||||
@@ -124,23 +124,20 @@ export function CollapsibleHeader({
|
||||
]}
|
||||
>
|
||||
{Platform.OS === 'ios' ? (
|
||||
colorScheme === 'dark' ? (
|
||||
<Animated.View style={[StyleSheet.absoluteFill, { opacity: headerOpacity }]}>
|
||||
<AnimatedBlurView
|
||||
tint="dark"
|
||||
intensity={80}
|
||||
style={[StyleSheet.absoluteFill, { opacity: headerOpacity }]}
|
||||
tint={colorScheme === 'dark' ? 'dark' : 'light'}
|
||||
intensity={colorScheme === 'dark' ? 80 : 100}
|
||||
style={[StyleSheet.absoluteFill, { backgroundColor: colors.headerBackground }]}
|
||||
/>
|
||||
) : (
|
||||
<AnimatedBlurView
|
||||
tint="light"
|
||||
intensity={100}
|
||||
style={[StyleSheet.absoluteFill, { opacity: headerOpacity }]}
|
||||
/>
|
||||
)
|
||||
<View style={styles.headerBorder} />
|
||||
</Animated.View>
|
||||
) : (
|
||||
<Animated.View
|
||||
style={[StyleSheet.absoluteFill, { backgroundColor: headerBackground }]}
|
||||
/>
|
||||
>
|
||||
<View style={styles.headerBorder} />
|
||||
</Animated.View>
|
||||
)}
|
||||
|
||||
<Animated.View
|
||||
@@ -166,10 +163,7 @@ export function CollapsibleHeader({
|
||||
))}
|
||||
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.headerBorder,
|
||||
{ opacity: headerOpacity },
|
||||
]}
|
||||
style={{ opacity: headerOpacity }}
|
||||
/>
|
||||
</Animated.View>
|
||||
</>
|
||||
|
||||
@@ -17,7 +17,8 @@ export const Colors = {
|
||||
icon: '#687076',
|
||||
tabIconDefault: '#687076',
|
||||
tabIconSelected: '#f49541',
|
||||
headerBackground: '#fff',
|
||||
headerBackground: 'rgba(255, 255, 255, 0.7)',
|
||||
headerBorder: '#eae9eb',
|
||||
tabBarBackground: '#fff',
|
||||
primary: '#f49541',
|
||||
primarySurfaceText: '#ffffff',
|
||||
@@ -40,7 +41,8 @@ export const Colors = {
|
||||
icon: '#9BA1A6',
|
||||
tabIconDefault: '#9BA1A6',
|
||||
tabIconSelected: '#f49541',
|
||||
headerBackground: '#202020',
|
||||
headerBackground: 'rgba(0, 0, 0, 0.3)',
|
||||
headerBorder: '#2f2e30',
|
||||
tabBarBackground: '#202020',
|
||||
primary: '#f49541',
|
||||
primarySurfaceText: '#ffffff',
|
||||
|
||||
23
apps/mobile-app/package-lock.json
generated
23
apps/mobile-app/package-lock.json
generated
@@ -42,6 +42,7 @@
|
||||
"react-native-argon2": "^2.0.1",
|
||||
"react-native-gesture-handler": "~2.20.2",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
||||
"react-native-quick-crypto": "^0.7.13",
|
||||
"react-native-reanimated": "~3.16.1",
|
||||
"react-native-safe-area-context": "4.12.0",
|
||||
@@ -16268,6 +16269,15 @@
|
||||
"react": "^16.6.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-iphone-x-helper": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
|
||||
"integrity": "sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.42.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-is-edge-to-edge": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz",
|
||||
@@ -16278,6 +16288,19 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-keyboard-aware-scroll-view": {
|
||||
"version": "0.9.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.9.5.tgz",
|
||||
"integrity": "sha512-XwfRn+T/qBH9WjTWIBiJD2hPWg0yJvtaEw6RtPCa5/PYHabzBaWxYBOl0usXN/368BL1XktnZPh8C2lmTpOREA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.6.2",
|
||||
"react-native-iphone-x-helper": "^1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.48.4"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-quick-base64": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-quick-base64/-/react-native-quick-base64-2.1.2.tgz",
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"react-native-argon2": "^2.0.1",
|
||||
"react-native-gesture-handler": "~2.20.2",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
||||
"react-native-quick-crypto": "^0.7.13",
|
||||
"react-native-reanimated": "~3.16.1",
|
||||
"react-native-safe-area-context": "4.12.0",
|
||||
|
||||
Reference in New Issue
Block a user