diff --git a/apps/mobile-app/app/(tabs)/credentials/_layout.tsx b/apps/mobile-app/app/(tabs)/credentials/_layout.tsx
index 1bfdf8963..20efd4a48 100644
--- a/apps/mobile-app/app/(tabs)/credentials/_layout.tsx
+++ b/apps/mobile-app/app/(tabs)/credentials/_layout.tsx
@@ -1,5 +1,6 @@
import { Stack } from 'expo-router';
import { Platform, Text } from 'react-native';
+import { useRouter } from 'expo-router';
import { defaultHeaderOptions } from '@/components/themed/ThemedHeader';
import { AndroidHeader } from '@/components/ui/AndroidHeader';
@@ -16,12 +17,6 @@ export default function CredentialsLayout(): React.ReactNode {
options={{
title: 'Credentials',
headerShown: Platform.OS === 'android',
- /**
- * On Android, we use a custom header component that includes the AliasVault logo.
- * On iOS, we don't show the header as a custom collapsible header is used.
- * @returns {React.ReactNode} The header component
- */
- headerTitle: (): React.ReactNode => Platform.OS === 'android' ? : Credentials,
...defaultHeaderOptions,
}}
/>
diff --git a/apps/mobile-app/app/(tabs)/credentials/index.tsx b/apps/mobile-app/app/(tabs)/credentials/index.tsx
index de6b52c30..59c6d65c4 100644
--- a/apps/mobile-app/app/(tabs)/credentials/index.tsx
+++ b/apps/mobile-app/app/(tabs)/credentials/index.tsx
@@ -1,5 +1,5 @@
import { StyleSheet, Text, FlatList, TouchableOpacity, TextInput, RefreshControl, Platform, Animated, Alert } from 'react-native';
-import { useState, useEffect, useCallback, useRef } from 'react';
+import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useNavigation } from '@react-navigation/native';
import { useRouter } from 'expo-router';
import Toast from 'react-native-toast-message';
@@ -18,6 +18,7 @@ import { CredentialCard } from '@/components/credentials/CredentialCard';
import { TitleContainer } from '@/components/ui/TitleContainer';
import { CollapsibleHeader } from '@/components/ui/CollapsibleHeader';
import { SkeletonLoader } from '@/components/ui/SkeletonLoader';
+import { AndroidHeader } from '@/components/ui/AndroidHeader';
import emitter from '@/utils/EventEmitter';
import { useMinDurationLoading } from '@/hooks/useMinDurationLoading';
import { useWebApi } from '@/context/WebApiContext';
@@ -65,14 +66,14 @@ export default function CredentialsScreen() : React.ReactNode {
}
}, [dbContext.sqliteClient, setIsLoadingCredentials]);
- const headerButtons = [{
+ const headerButtons = useMemo(() => [{
icon: 'add' as const,
position: 'right' as const,
/**
* Add credential.
*/
onPress: () : void => router.push('/(tabs)/credentials/add-edit')
- }];
+ }], [router]);
useEffect(() => {
const unsubscribeFocus = navigation.addListener('focus', () => {
@@ -261,6 +262,17 @@ export default function CredentialsScreen() : React.ReactNode {
},
});
+ // Set header buttons
+ useEffect(() => {
+ navigation.setOptions({
+ /**
+ * Define custom header which is shown on Android. iOS displays the custom CollapsibleHeader component instead.
+ * @returns
+ */
+ headerTitle: (): React.ReactNode => Platform.OS === 'android' ? : Credentials,
+ });
+ }, [navigation, headerButtons]);
+
return (
void;
+ position: 'left' | 'right';
+}
+
interface IAndroidHeaderProps {
title: string;
+ headerButtons?: HeaderButton[];
}
/**
@@ -11,27 +21,62 @@ interface IAndroidHeaderProps {
* @param {IAndroidHeaderProps} props - The component props
* @returns {React.ReactNode} The Android header component
*/
-export function AndroidHeader({ title }: IAndroidHeaderProps): React.ReactNode {
+export function AndroidHeader({ title, headerButtons = [] }: IAndroidHeaderProps): React.ReactNode {
+ const colors = useColors();
+
+ const styles = StyleSheet.create({
+ headerButton: {
+ padding: 4,
+ },
+ headerContainer: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ gap: 8,
+ verticalAlign: 'middle',
+ },
+ headerTitle: {
+ fontSize: 22,
+ fontWeight: 'bold',
+ },
+ leftButton: {
+ marginRight: 'auto',
+ },
+ logo: {
+ marginBottom: 0,
+ },
+ rightButton: {
+ marginLeft: 'auto',
+ },
+ });
+
return (
+ {headerButtons.find(b => b.position === 'left') && (
+ b.position === 'left')?.onPress}
+ >
+ b.position === 'left')?.icon ?? 'add'}
+ size={28}
+ color={colors.primary}
+ />
+
+ )}
{title}
+ {headerButtons.find(b => b.position === 'right') && (
+ b.position === 'right')?.onPress}
+ >
+ b.position === 'right')?.icon ?? 'add'}
+ size={28}
+ color={colors.primary}
+ />
+
+ )}
);
-}
-
-const styles = StyleSheet.create({
- headerContainer: {
- alignItems: 'center',
- flexDirection: 'row',
- gap: 8,
- verticalAlign: 'middle',
- },
- headerTitle: {
- fontSize: 22,
- fontWeight: 'bold',
- },
- logo: {
- marginBottom: 0,
- },
-});
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/apps/mobile-app/components/ui/CollapsibleHeader.tsx b/apps/mobile-app/components/ui/CollapsibleHeader.tsx
index 1ea217cb7..42f89c094 100644
--- a/apps/mobile-app/components/ui/CollapsibleHeader.tsx
+++ b/apps/mobile-app/components/ui/CollapsibleHeader.tsx
@@ -99,7 +99,7 @@ export function CollapsibleHeader({
right: 0,
},
headerButton: {
- bottom: 6,
+ bottom: 2,
padding: 4,
position: 'absolute',
},