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>
66 lines
1.5 KiB
TypeScript
66 lines
1.5 KiB
TypeScript
import React from 'react';
|
|
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|
import { COLORS, BORDER_RADIUS, FONT_SIZES } from '@/constants/theme';
|
|
|
|
interface ButtonProps {
|
|
title: string;
|
|
onPress: () => void;
|
|
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
|
|
disabled?: boolean;
|
|
loading?: boolean;
|
|
fullWidth?: boolean;
|
|
}
|
|
|
|
export function Button({
|
|
title,
|
|
onPress,
|
|
variant = 'primary',
|
|
disabled = false,
|
|
loading = false,
|
|
fullWidth = false,
|
|
}: ButtonProps) {
|
|
const variantColors = {
|
|
primary: { bg: COLORS.primary, text: '#fff' },
|
|
secondary: { bg: COLORS.secondary, text: '#fff' },
|
|
danger: { bg: COLORS.danger, text: '#fff' },
|
|
ghost: { bg: 'transparent', text: COLORS.primary },
|
|
};
|
|
|
|
const colors = variantColors[variant];
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.button,
|
|
{ backgroundColor: colors.bg, opacity: disabled || loading ? 0.5 : 1 },
|
|
fullWidth && styles.fullWidth,
|
|
]}
|
|
onPress={onPress}
|
|
disabled={disabled || loading}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={[styles.text, { color: colors.text }]}>
|
|
{loading ? '...' : title}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
button: {
|
|
borderRadius: BORDER_RADIUS.md,
|
|
paddingVertical: 12,
|
|
paddingHorizontal: 24,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginVertical: 4,
|
|
},
|
|
fullWidth: {
|
|
width: '100%',
|
|
},
|
|
text: {
|
|
fontSize: FONT_SIZES.md,
|
|
fontWeight: '600',
|
|
},
|
|
});
|