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,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { StyleSheet, Text, View, SafeAreaView, KeyboardAvoidingView, Platform, ScrollView, Alert } from 'react-native';
|
||||
import { RouteProp, useRoute, useNavigation } from '@react-navigation/native';
|
||||
import { useNavigation, NavigationProp } from '@react-navigation/native';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { Button, Input } from '@/components';
|
||||
import { COLORS, FONT_SIZES, SPACING } from '@/constants/theme';
|
||||
@@ -8,15 +8,11 @@ import { COLORS, FONT_SIZES, SPACING } from '@/constants/theme';
|
||||
type RootStackParamList = {
|
||||
Login: undefined;
|
||||
Register: undefined;
|
||||
App: undefined;
|
||||
};
|
||||
|
||||
type LoginScreenRouteProp = RouteProp<RootStackParamList, 'Login'>;
|
||||
|
||||
export function LoginScreen() {
|
||||
const route = useRoute<LoginScreenRouteProp>();
|
||||
const navigation = useNavigation<any>();
|
||||
const { login, isLoading, error, clearError } = useAuthStore();
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { login, isLoading, clearError } = useAuthStore();
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import React, { useState } from 'react';
|
||||
import { StyleSheet, Text, View, SafeAreaView, KeyboardAvoidingView, Platform, ScrollView, Alert } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useNavigation, NavigationProp } from '@react-navigation/native';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { Button, Input } from '@/components';
|
||||
import { COLORS, FONT_SIZES, SPACING } from '@/constants/theme';
|
||||
|
||||
type RootStackParamList = {
|
||||
Login: undefined;
|
||||
Register: undefined;
|
||||
};
|
||||
|
||||
export function RegisterScreen() {
|
||||
const navigation = useNavigation<any>();
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { register, isLoading } = useAuthStore();
|
||||
|
||||
const [firstName, setFirstName] = useState('');
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { StyleSheet, Text, View, SafeAreaView, ScrollView, FlatList, TouchableOpacity, Alert, Modal } from 'react-native';
|
||||
import { useDarkWatchStore } from '@/store/darkWatchStore';
|
||||
import { Card, Button, Input, EmptyState } from '@/components';
|
||||
import { COLORS, FONT_SIZES, SPACING, BORDER_RADIUS } from '@/constants/theme';
|
||||
import { COLORS, FONT_SIZES, SPACING, BORDER_RADIUS, getSeverityColor } from '@/constants/theme';
|
||||
import type { WatchListItem } from '@/types';
|
||||
|
||||
export function DarkWatchScreen() {
|
||||
@@ -178,15 +178,6 @@ export function DarkWatchScreen() {
|
||||
);
|
||||
}
|
||||
|
||||
const getSeverityColor = (severity: string) => {
|
||||
switch (severity) {
|
||||
case 'critical': return COLORS.danger;
|
||||
case 'high': return COLORS.warning;
|
||||
case 'medium': return COLORS.accent;
|
||||
default: return COLORS.textSecondary;
|
||||
}
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { StyleSheet, Text, View, SafeAreaView, ScrollView, TouchableOpacity, Alert } from 'react-native';
|
||||
import { useSpamShieldStore } from '@/store/spamShieldStore';
|
||||
import { Card, Button, Input, StatCard, EmptyState } from '@/components';
|
||||
@@ -22,6 +22,15 @@ export function SpamShieldScreen() {
|
||||
const [newNumber, setNewNumber] = useState('');
|
||||
const [newLabel, setNewLabel] = useState('');
|
||||
|
||||
const blockedCount = useMemo(
|
||||
() => callHistory.filter((r) => r.isBlocked).length,
|
||||
[callHistory]
|
||||
);
|
||||
const totalCount = useMemo(
|
||||
() => callHistory.length + textHistory.length,
|
||||
[callHistory, textHistory]
|
||||
);
|
||||
|
||||
const handleAddToList = (list: 'whitelist' | 'blacklist') => {
|
||||
if (!newNumber) return;
|
||||
|
||||
@@ -62,8 +71,8 @@ export function SpamShieldScreen() {
|
||||
|
||||
<ScrollView style={styles.content}>
|
||||
<Card>
|
||||
<StatCard title="Blocked Today" value={callHistory.filter(r => r.isBlocked).length} color={COLORS.success} />
|
||||
<StatCard title="Total Blocked" value={callHistory.length + textHistory.length} color={COLORS.accent} />
|
||||
<StatCard title="Blocked Today" value={blockedCount} color={COLORS.success} />
|
||||
<StatCard title="Total Blocked" value={totalCount} color={COLORS.accent} />
|
||||
</Card>
|
||||
|
||||
<View style={styles.tabs}>
|
||||
|
||||
Reference in New Issue
Block a user