fix: address code review findings for mobile app (FRE-4572)
P0 fixes: - Replace crypto.randomUUID() with uuid v4 (not available in RN) - Replace Platform.Version with expo-device osVersion - Fix auth navigation types, remove unused App route P1 fixes: - Push notification handler respects user preferences (useRef pattern) - Fix stale closure: use zustand subscribe + useRef for live preferences - Add retry logging for device registration failures - Replace emoji tab icons with @expo/vector-icons Ionicons - Document API integration TODOs in all local-only stores P2 fixes: - Add __DEV__ global declaration (global.d.ts) - Fix package.json main field to expo/AppEntry.js - Add retry logging for push device registration - Add z-index/elevation to LoadingOverlay - Add visual indicator to EmptyState icon P3 fixes: - Type navigation with NavigationProp<RootStackParamList> - Move getSeverityColor to theme.ts (single source of truth) - Add useMemo for SpamShield filter computations - Verified usesNonExemptEncryption: false is correct for expo-secure-store Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Text, ViewStyle, StyleSheet } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { DashboardScreen } from '@/screens/dashboard';
|
||||
import { DarkWatchScreen } from '@/screens/darkwatch';
|
||||
@@ -18,25 +19,29 @@ type MainTabParamList = {
|
||||
|
||||
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}',
|
||||
const iconMap: Record<string, keyof typeof Ionicons.glyphMap> = {
|
||||
Dashboard: 'shield-outline',
|
||||
DarkWatch: 'eye-outline',
|
||||
SpamShield: 'ban-outline',
|
||||
VoicePrint: 'mic-outline',
|
||||
Settings: 'settings-outline',
|
||||
};
|
||||
|
||||
function TabIcon({ routeName, color }: { routeName: string; color: string }) {
|
||||
return (
|
||||
<Text style={[styles.icon, { color }]}>{iconMap[routeName] || '\u{2022}'}</Text>
|
||||
);
|
||||
}
|
||||
const iconActiveMap: Record<string, keyof typeof Ionicons.glyphMap> = {
|
||||
Dashboard: 'shield',
|
||||
DarkWatch: 'eye',
|
||||
SpamShield: 'ban',
|
||||
VoicePrint: 'mic',
|
||||
Settings: 'settings',
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
fontSize: 22,
|
||||
},
|
||||
});
|
||||
function TabIcon({ routeName, color, focused }: { routeName: string; color: string; focused: boolean }) {
|
||||
const iconName = focused
|
||||
? (iconActiveMap[routeName] as keyof typeof Ionicons.glyphMap)
|
||||
: (iconMap[routeName] as keyof typeof Ionicons.glyphMap);
|
||||
|
||||
return <Ionicons name={iconName} size={24} color={color} />;
|
||||
}
|
||||
|
||||
export function MainTabNavigator() {
|
||||
return (
|
||||
@@ -75,7 +80,7 @@ export function MainTabNavigator() {
|
||||
options={{
|
||||
headerTitle: 'Dashboard',
|
||||
tabBarLabel: 'Home',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="Dashboard" color={color} />,
|
||||
tabBarIcon: ({ color, focused }) => <TabIcon routeName="Dashboard" color={color} focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
@@ -84,7 +89,7 @@ export function MainTabNavigator() {
|
||||
options={{
|
||||
headerTitle: 'DarkWatch',
|
||||
tabBarLabel: 'DarkWatch',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="DarkWatch" color={color} />,
|
||||
tabBarIcon: ({ color, focused }) => <TabIcon routeName="DarkWatch" color={color} focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
@@ -93,7 +98,7 @@ export function MainTabNavigator() {
|
||||
options={{
|
||||
headerTitle: 'SpamShield',
|
||||
tabBarLabel: 'SpamShield',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="SpamShield" color={color} />,
|
||||
tabBarIcon: ({ color, focused }) => <TabIcon routeName="SpamShield" color={color} focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
@@ -102,7 +107,7 @@ export function MainTabNavigator() {
|
||||
options={{
|
||||
headerTitle: 'VoicePrint',
|
||||
tabBarLabel: 'VoicePrint',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="VoicePrint" color={color} />,
|
||||
tabBarIcon: ({ color, focused }) => <TabIcon routeName="VoicePrint" color={color} focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
@@ -111,7 +116,7 @@ export function MainTabNavigator() {
|
||||
options={{
|
||||
headerTitle: 'Settings',
|
||||
tabBarLabel: 'Settings',
|
||||
tabBarIcon: ({ color }) => <TabIcon routeName="Settings" color={color} />,
|
||||
tabBarIcon: ({ color, focused }) => <TabIcon routeName="Settings" color={color} focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
|
||||
Reference in New Issue
Block a user