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:
65
packages/mobile/src/hooks/usePushNotifications.ts
Normal file
65
packages/mobile/src/hooks/usePushNotifications.ts
Normal 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user