From 82423fffcbb80aedb275399b4a3d1b6c4daaf759 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Tue, 6 May 2025 14:22:22 +0200 Subject: [PATCH] Add translucent headers (#771) --- .../app/(tabs)/credentials/_layout.tsx | 21 ++----- .../app/(tabs)/credentials/add-edit.tsx | 26 ++++++--- apps/mobile-app/app/(tabs)/emails/[id].tsx | 27 ++++----- apps/mobile-app/app/(tabs)/emails/_layout.tsx | 9 +-- .../app/(tabs)/settings/_layout.tsx | 21 +++---- .../app/(tabs)/settings/vault-unlock.tsx | 6 +- apps/mobile-app/app/_layout.tsx | 1 + .../components/themed/ThemedHeader.tsx | 57 +++++++++++++++++++ .../components/themed/ThemedScrollView.tsx | 5 +- .../components/ui/CollapsibleHeader.tsx | 30 ++++------ apps/mobile-app/constants/Colors.ts | 6 +- apps/mobile-app/package-lock.json | 23 ++++++++ apps/mobile-app/package.json | 1 + 13 files changed, 148 insertions(+), 85 deletions(-) create mode 100644 apps/mobile-app/components/themed/ThemedHeader.tsx diff --git a/apps/mobile-app/app/(tabs)/credentials/_layout.tsx b/apps/mobile-app/app/(tabs)/credentials/_layout.tsx index 2900c79db..c3ba056f0 100644 --- a/apps/mobile-app/app/(tabs)/credentials/_layout.tsx +++ b/apps/mobile-app/app/(tabs)/credentials/_layout.tsx @@ -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 ( diff --git a/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx b/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx index 97579d91a..ce0ca8894 100644 --- a/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx +++ b/apps/mobile-app/app/(tabs)/credentials/add-edit.tsx @@ -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) && ( )} - + - {!isEditMode && ( @@ -658,10 +668,10 @@ export default function AddEditCredentialScreen() : React.ReactNode { )} )} - + - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/emails/[id].tsx b/apps/mobile-app/app/(tabs)/emails/[id].tsx index 0dfa621a6..e1340f1db 100644 --- a/apps/mobile-app/app/(tabs)/emails/[id].tsx +++ b/apps/mobile-app/app/(tabs)/emails/[id].tsx @@ -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 { ), }); - }, [isHtmlView, navigation, handleDelete, styles.headerRightButton, styles.headerRightContainer]); + }, [isHtmlView, navigation, handleDelete, colors.primary, styles.headerRightButton, styles.headerRightContainer]); if (isLoading) { return ( - + - + ); } if (error) { return ( - + Error: {error} - + ); } if (!email) { return ( - + Email not found - + ); } @@ -471,7 +466,7 @@ export default function EmailDetailsScreen() : React.ReactNode { } return ( - + {metadataView} {emailView} @@ -492,6 +487,6 @@ export default function EmailDetailsScreen() : React.ReactNode { ))} )} - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/emails/_layout.tsx b/apps/mobile-app/app/(tabs)/emails/_layout.tsx index 2e31bec43..9f62fda9e 100644 --- a/apps/mobile-app/app/(tabs)/emails/_layout.tsx +++ b/apps/mobile-app/app/(tabs)/emails/_layout.tsx @@ -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 ( diff --git a/apps/mobile-app/app/(tabs)/settings/_layout.tsx b/apps/mobile-app/app/(tabs)/settings/_layout.tsx index 485b0aa33..e97f4e6f1 100644 --- a/apps/mobile-app/app/(tabs)/settings/_layout.tsx +++ b/apps/mobile-app/app/(tabs)/settings/_layout.tsx @@ -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 ( diff --git a/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx b/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx index 585ae149e..d7366dc4c 100644 --- a/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx +++ b/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx @@ -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 ( - + @@ -214,6 +214,6 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { - + ); } \ No newline at end of file diff --git a/apps/mobile-app/app/_layout.tsx b/apps/mobile-app/app/_layout.tsx index 02a59af71..93c909942 100644 --- a/apps/mobile-app/app/_layout.tsx +++ b/apps/mobile-app/app/_layout.tsx @@ -53,6 +53,7 @@ function RootLayoutNav() : React.ReactNode { + + + + ); + } + + 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 => , +}; \ No newline at end of file diff --git a/apps/mobile-app/components/themed/ThemedScrollView.tsx b/apps/mobile-app/components/themed/ThemedScrollView.tsx index 302418d81..4935595ad 100644 --- a/apps/mobile-app/components/themed/ThemedScrollView.tsx +++ b/apps/mobile-app/components/themed/ThemedScrollView.tsx @@ -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; @@ -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, diff --git a/apps/mobile-app/components/ui/CollapsibleHeader.tsx b/apps/mobile-app/components/ui/CollapsibleHeader.tsx index 923c0ac4c..d91588dc8 100644 --- a/apps/mobile-app/components/ui/CollapsibleHeader.tsx +++ b/apps/mobile-app/components/ui/CollapsibleHeader.tsx @@ -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' ? ( + - ) : ( - - ) + + ) : ( + > + + )} diff --git a/apps/mobile-app/constants/Colors.ts b/apps/mobile-app/constants/Colors.ts index 4b52d2ca2..6533bcc58 100644 --- a/apps/mobile-app/constants/Colors.ts +++ b/apps/mobile-app/constants/Colors.ts @@ -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', diff --git a/apps/mobile-app/package-lock.json b/apps/mobile-app/package-lock.json index d53c92808..c2d3e5afa 100644 --- a/apps/mobile-app/package-lock.json +++ b/apps/mobile-app/package-lock.json @@ -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", diff --git a/apps/mobile-app/package.json b/apps/mobile-app/package.json index 930116485..151d46ef6 100644 --- a/apps/mobile-app/package.json +++ b/apps/mobile-app/package.json @@ -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",