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>
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import type { SpamRecord } from '@/types';
|
|
|
|
/**
|
|
* TODO: Wire store operations to @shieldai/mobile-api-client for production.
|
|
* Current implementation is local-only (AsyncStorage) for offline-first MVP.
|
|
*/
|
|
|
|
type PhoneList = { number: string; label: string }[];
|
|
|
|
interface SpamShieldState {
|
|
callHistory: SpamRecord[];
|
|
textHistory: SpamRecord[];
|
|
whitelist: PhoneList;
|
|
blacklist: PhoneList;
|
|
isLoading: false;
|
|
addToWhitelist: (number: string, label: string) => void;
|
|
addToBlacklist: (number: string, label: string) => void;
|
|
removeFromWhitelist: (number: string) => void;
|
|
removeFromBlacklist: (number: string) => void;
|
|
}
|
|
|
|
export const useSpamShieldStore = create<SpamShieldState>()(
|
|
persist(
|
|
(set) => ({
|
|
callHistory: [],
|
|
textHistory: [],
|
|
whitelist: [],
|
|
blacklist: [],
|
|
isLoading: false,
|
|
|
|
addToWhitelist: (number, label) => {
|
|
set((state) => ({
|
|
whitelist: [...state.whitelist, { number, label }],
|
|
}));
|
|
},
|
|
|
|
addToBlacklist: (number, label) => {
|
|
set((state) => ({
|
|
blacklist: [...state.blacklist, { number, label }],
|
|
}));
|
|
},
|
|
|
|
removeFromWhitelist: (number) => {
|
|
set((state) => ({
|
|
whitelist: state.whitelist.filter((item) => item.number !== number),
|
|
}));
|
|
},
|
|
|
|
removeFromBlacklist: (number) => {
|
|
set((state) => ({
|
|
blacklist: state.blacklist.filter((item) => item.number !== number),
|
|
}));
|
|
},
|
|
}),
|
|
{
|
|
name: '@shieldai_spamshield',
|
|
storage: createJSONStorage(() => AsyncStorage),
|
|
}
|
|
)
|
|
);
|