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:
2026-05-17 10:12:46 -04:00
parent 7fb8b83810
commit a071aa736e
50 changed files with 3026 additions and 13 deletions

View File

@@ -0,0 +1,65 @@
import { useEffect, useCallback } from 'react';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
import { deviceService, notificationService } from '@shieldai/mobile-api-client';
import { useSettingsStore } from '@/store/settingsStore';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
export function usePushNotifications() {
const { preferences } = useSettingsStore();
const registerForPushNotifications = useCallback(async () => {
try {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
return null;
}
const token = (await Notifications.getExpoPushTokenAsync({
projectId: 'shieldai-project-id',
})).data;
await deviceService.registerDevice({
platform: Platform.OS === 'ios' ? 'ios' : 'android',
pushToken: token,
modelName: Platform.OS === 'ios' ? 'iPhone' : 'Android',
osVersion: Platform.Version.toString(),
appVersion: '1.0.0',
});
return token;
} catch (error) {
console.error('Failed to register for push notifications:', error);
return null;
}
}, []);
useEffect(() => {
const subscription = Notifications.addNotificationReceivedListener((notification) => {
const type = notification.request.content.data?.type;
const prefs = useSettingsStore.getState().preferences;
if (type === 'darkwatch_alert' && !prefs.darkwatchAlert) return;
if (type === 'spam_blocked' && !prefs.spamBlocked) return;
if (type === 'voiceprint_analysis' && !prefs.voiceprintAnalysis) return;
});
return () => subscription.remove();
}, []);
return { registerForPushNotifications };
}