import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { Alert, Pressable, ScrollView, StyleSheet, Switch, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { usePrivacyCenter } from '../../src/hooks'; import { buildPrivacySummaryText } from '../../src/api'; import { shareText } from '../../src/lib/shareFile'; import { ErrorState, LoadingState } from '../../src/components/StateView'; import { colors } from '../../src/theme'; import type { PrivacyLevelInfo, PrivacySettingItem, PrivacySection } from '../../src/types'; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack }: { onBack: () => void }) { return ( 隐私设置 ); } /* ===================== 子组件:隐私等级卡 ===================== */ function PrivacyLevelCard({ level, onExport }: { level: PrivacyLevelInfo; onExport: () => void }) { const theme = { high: { bg: colors.successLight, text: colors.successText, icon: 'shield-checkmark' }, medium: { bg: colors.actionLight, text: colors.actionText, icon: 'shield' }, low: { bg: colors.infoLight, text: colors.infoText, icon: 'shield-outline' }, }[level.level]; return ( 当前隐私等级:{level.label} {level.description} {/* 这里原先叫「查看隐私报告」并弹「功能开发中」。语义上「隐私报告」= 谁访问了我的数据, 需要后端访问日志,本机给不出;能如实做到的是导出「我的隐私设置现状」,故改名并真实现。 */} 导出隐私设置摘要 ); } /* ===================== 子组件:设置项行 ===================== */ function SettingRow({ item, isLast, onToggle, }: { item: PrivacySettingItem; isLast: boolean; onToggle?: (id: string, enabled: boolean) => void; }) { const router = useRouter(); const isDanger = item.type === 'danger-link'; const titleColor = isDanger ? colors.dangerText : colors.textPrimary; const handlePress = () => { if (item.route) router.push(item.route); }; const rowBody = ( {item.title} {item.description ? {item.description} : null} ); // 开关型条目:带 route 的(如「勿扰模式」「真实姓名可见性」)点击左侧文字区进详情页, // 右侧 Switch 保持独立可切换,两者互不干扰。 if (item.type === 'switch') { return ( {item.route ? ( {rowBody} ) : ( rowBody )} onToggle?.(item.id, next)} trackColor={{ false: colors.border, true: colors.primaryBorder }} thumbColor={item.enabled ? colors.primary : colors.white} /> ); } return ( {item.title} {item.description ? {item.description} : null} {item.value ? {item.value} : null} ); } /* ===================== 子组件:设置分组 ===================== */ function Section({ section, onToggle }: { section: PrivacySection; onToggle?: (id: string, enabled: boolean) => void }) { return ( {section.title} {section.items.map((item, index) => ( ))} ); } /* ===================== 页面 ===================== */ export default function PrivacyCenterScreen() { const router = useRouter(); const { privacyCenter, loading, error, refresh, toggle } = usePrivacyCenter(); /** * 导出隐私设置摘要:拼成文本经系统分享面板发出。 * `ok` 只代表面板已拉起,不能据此提示「导出成功」,只在失败时说明原因(见 lib/shareFile)。 */ const handleExport = async () => { if (!privacyCenter) return; const outcome = await shareText(buildPrivacySummaryText(privacyCenter), '我的隐私设置摘要'); if (!outcome.ok) Alert.alert('无法导出', outcome.message); }; if (loading) { return (
router.back()} /> ); } if (error || !privacyCenter) { return (
router.back()} /> ); } return (
router.back()} /> {privacyCenter.sections.map((section) => (
))} 权限说明 {privacyCenter.explanation} ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: colors.pageBg, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 8, height: 48, backgroundColor: colors.white, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, headerBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center', }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary, }, scroll: { flex: 1, }, content: { padding: 12, paddingBottom: 24, }, levelCard: { borderRadius: 8, padding: 16, marginBottom: 16, }, levelRow: { flexDirection: 'row', alignItems: 'flex-start', }, levelText: { flex: 1, marginLeft: 12, }, levelLabel: { fontSize: 16, fontWeight: '700', marginBottom: 4, }, levelDesc: { fontSize: 13, lineHeight: 18, color: colors.textSecondary, }, levelBtn: { flexDirection: 'row', alignItems: 'center', alignSelf: 'flex-start', marginTop: 12, paddingVertical: 4, }, levelBtnText: { fontSize: 13, fontWeight: '600', marginRight: 2, }, section: { marginBottom: 16, }, sectionTitle: { fontSize: 14, fontWeight: '700', color: colors.textPrimary, marginBottom: 10, }, card: { backgroundColor: colors.white, borderRadius: 8, overflow: 'hidden', }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 14, paddingVertical: 14, }, rowBorder: { borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, rowBody: { flex: 1, marginRight: 12, }, rowTapArea: { flex: 1, flexDirection: 'row', alignItems: 'center', }, rowTitle: { fontSize: 15, fontWeight: '600', color: colors.textPrimary, }, rowDesc: { marginTop: 3, fontSize: 12, lineHeight: 16, color: colors.textSecondary, }, rowRight: { flexDirection: 'row', alignItems: 'center', }, rowValue: { fontSize: 13, color: colors.textHint, marginRight: 4, }, explanationCard: { backgroundColor: colors.white, borderRadius: 8, padding: 16, marginBottom: 8, }, explanationTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary, marginBottom: 8, }, explanationText: { fontSize: 13, lineHeight: 20, color: colors.textSecondary, }, });