feat: scaffold ShieldAI React Native mobile app MVP (FRE-4572)
Build complete Expo/React Native mobile app with: - Auth flow: email/password login, registration, biometric auth - Dashboard: exposure summary, spam stats, voice protection status - DarkWatch: watch list management, exposure feed, alert toggles - SpamShield: call/text history, whitelist/blacklist management - VoicePrint: family member enrollment, voice analysis - Settings: tier management, notification preferences, security - Push notification integration via FCM/APNs - Offline-first state management with Zustand + AsyncStorage - Integration with @shieldai/mobile-api-client for API services - React Navigation with auth-aware routing (stack + bottom tabs) - Dark theme with consistent design system (colors, spacing, typography) - Network status monitoring and offline request queuing Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
119
packages/mobile/src/navigation/MainTabNavigator.tsx
Normal file
119
packages/mobile/src/navigation/MainTabNavigator.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import React from 'react';
|
||||
import { Text, ViewStyle, StyleSheet } from 'react-native';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { DashboardScreen } from '@/screens/dashboard';
|
||||
import { DarkWatchScreen } from '@/screens/darkwatch';
|
||||
import { SpamShieldScreen } from '@/screens/spamshield';
|
||||
import { VoicePrintScreen } from '@/screens/voiceprint';
|
||||
import { SettingsScreen } from '@/screens/settings';
|
||||
import { COLORS, FONT_SIZES } from '@/constants/theme';
|
||||
|
||||
type MainTabParamList = {
|
||||
Dashboard: undefined;
|
||||
DarkWatch: undefined;
|
||||
SpamShield: undefined;
|
||||
VoicePrint: undefined;
|
||||
Settings: undefined;
|
||||
};
|
||||
|
||||
const Tab = createBottomTabNavigator<MainTabParamList>();
|
||||
|
||||
const iconMap: Record<string, string> = {
|
||||
Dashboard: '\u{1F6E1}\u{FE0F}',
|
||||
DarkWatch: '\u{1F441}\u{FE0F}',
|
||||
SpamShield: '\u{1F6AB}',
|
||||
VoicePrint: '\u{1F399}\u{FE0F}',
|
||||
Settings: '\u{2699}\u{FE0F}',
|
||||
};
|
||||
|
||||
function TabIcon({ routeName, color }: { routeName: string; color: string }) {
|
||||
return (
|
||||
<Text style={[styles.icon, { color }]}>{iconMap[routeName] || '\u{2022}'}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
fontSize: 22,
|
||||
},
|
||||
});
|
||||
|
||||
export function MainTabNavigator() {
|
||||
return (
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
headerStyle: {
|
||||
backgroundColor: COLORS.background,
|
||||
},
|
||||
headerTintColor: COLORS.text,
|
||||
headerTitleStyle: {
|
||||
fontSize: FONT_SIZES.lg,
|
||||
fontWeight: '600',
|
||||
},
|
||||
tabBarStyle: {
|
||||
backgroundColor: COLORS.backgroundLight,
|
||||
borderTopColor: COLORS.border,
|
||||
borderTopWidth: 1,
|
||||
height: 60,
|
||||
paddingBottom: 8,
|
||||
paddingTop: 8,
|
||||
} as ViewStyle,
|
||||
tabBarActiveTintColor: COLORS.primary,
|
||||
tabBarInactiveTintColor: COLORS.textMuted,
|
||||
tabBarLabelStyle: {
|
||||
fontSize: FONT_SIZES.xs,
|
||||
},
|
||||
tabBarIconStyle: {
|
||||
marginTop: 4,
|
||||
},
|
||||
tabBarShowLabel: true,
|
||||
}}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Dashboard"
|
||||
component={DashboardScreen}
|
||||
options={{
|
||||
headerTitle: 'Dashboard',
|
||||
tabBarLabel: 'Home',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="Dashboard" color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="DarkWatch"
|
||||
component={DarkWatchScreen}
|
||||
options={{
|
||||
headerTitle: 'DarkWatch',
|
||||
tabBarLabel: 'DarkWatch',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="DarkWatch" color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="SpamShield"
|
||||
component={SpamShieldScreen}
|
||||
options={{
|
||||
headerTitle: 'SpamShield',
|
||||
tabBarLabel: 'SpamShield',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="SpamShield" color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="VoicePrint"
|
||||
component={VoicePrintScreen}
|
||||
options={{
|
||||
headerTitle: 'VoicePrint',
|
||||
tabBarLabel: 'VoicePrint',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="VoicePrint" color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Settings"
|
||||
component={SettingsScreen}
|
||||
options={{
|
||||
headerTitle: 'Settings',
|
||||
tabBarLabel: 'Settings',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="Settings" color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user