import { useState, useEffect, useRef } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Alert, ActivityIndicator, ScrollView, Platform, Linking, BackHandler } from 'react-native'; import { StatusBar } from 'expo-status-bar'; import { Ionicons } from '@expo/vector-icons'; import { getGitHubToken, clearGitHubToken, getAllCategories, getTotalRepoCount, setGitHubToken } from '../services/database'; import { fetchStarredRepos, checkUpdate } from '../services/github'; import TokenInput from '../components/TokenInput'; // 设置页:Token 管理、数据统计、版本更新检查 export default function SettingsScreen({ onGoBack, onTokenExpired }) { const [token, setToken] = useState(null); const [stats, setStats] = useState({ repos: 0, categories: 0 }); const [showTokenInput, setShowTokenInput] = useState(false); const [verifying, setVerifying] = useState(false); const [checkingUpdate, setCheckingUpdate] = useState(false); const [updateInfo, setUpdateInfo] = useState(null); const goBackRef = useRef(onGoBack); goBackRef.current = onGoBack; // 加载已保存的 Token 和数据统计 const loadSettings = async () => { const savedToken = await getGitHubToken(); setToken(savedToken); const cats = await getAllCategories(); const total = await getTotalRepoCount(); setStats({ repos: total, categories: cats.length }); }; useEffect(() => { loadSettings(); }, []); // Android 硬件返回按钮 useEffect(() => { const onBackPress = () => { goBackRef.current(); return true; }; const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress); return () => subscription.remove(); }, []); const handleChangeToken = () => { setShowTokenInput(true); }; // 验证当前 Token 是否仍然有效 const handleVerifyToken = async () => { setVerifying(true); try { await fetchStarredRepos(token); Alert.alert('验证成功', 'Token 有效'); } catch (e) { Alert.alert('验证失败', e.message); } finally { setVerifying(false); } }; // 清除 Token(需用户确认) const handleClearToken = () => { Alert.alert( '清除 Token', '确定要清除 GitHub Token 吗?清除后需要重新输入才能同步数据。', [ { text: '取消', style: 'cancel' }, { text: '清除', style: 'destructive', onPress: async () => { await clearGitHubToken(); onTokenExpired(); }, }, ] ); }; const handleTokenSaved = async () => { setShowTokenInput(false); await loadSettings(); }; // 检查 GitHub Releases 是否有新版本 const handleCheckUpdate = async () => { setCheckingUpdate(true); setUpdateInfo(null); const savedToken = await getGitHubToken(); const result = await checkUpdate(savedToken); setUpdateInfo(result); setCheckingUpdate(false); if (result.error) { Alert.alert('检查更新', result.error); } else if (result.hasUpdate) { Alert.alert( '发现新版本', `当前版本:v${result.currentVersion}\n最新版本:v${result.latestVersion}\n\n${result.releaseName || ''}\n\n${result.releaseBody ? result.releaseBody : ''}`, [ { text: '取消', style: 'cancel' }, { text: '前往下载', onPress: () => { if (result.releaseUrl) { Linking.openURL(result.releaseUrl); } }, }, ] ); } else { Alert.alert('检查更新', result.message || '已是最新版本'); } }; if (showTokenInput) { return setShowTokenInput(false)} />; } const maskedToken = token ? token.slice(0, 8) + '••••••••' + token.slice(-4) : ''; return ( 设置 GitHub 账号 访问令牌 {token ? maskedToken : '未设置'} {token ? '修改' : '设置'} {token ? ( <> {verifying ? ( ) : ( <> 验证 )} 清除 ) : null} 数据统计 {stats.repos} 星标仓库 {stats.categories} 分类 AI 分析(即将推出) 智能分析你的星标仓库 自动分类、代码质量评估、技术趋势分析等强大功能即将上线 即将推出 关于 版本 {checkingUpdate ? ( ) : updateInfo && updateInfo.hasUpdate ? ( 有新版本 ) : null} 1.0.0 ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#f5f5f5', }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: '#fff', paddingTop: Platform.OS === 'ios' ? 50 : 40, paddingBottom: 12, paddingHorizontal: 16, borderBottomWidth: 1, borderBottomColor: '#e8e8e8', }, backBtn: { width: 40, height: 40, justifyContent: 'center', alignItems: 'center', }, headerTitle: { fontSize: 18, fontWeight: '600', color: '#1a1a1a', }, scroll: { flex: 1, }, section: { marginTop: 20, paddingHorizontal: 16, }, sectionTitle: { fontSize: 13, fontWeight: '600', color: '#888', marginBottom: 8, marginLeft: 4, textTransform: 'uppercase', }, card: { backgroundColor: '#fff', borderRadius: 12, padding: 16, elevation: 1, shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 2, }, row: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 8, }, rowLabel: { fontSize: 15, fontWeight: '500', color: '#333', }, tokenText: { fontSize: 13, color: '#888', fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', marginBottom: 12, }, tokenActions: { flexDirection: 'row', gap: 10, }, tokenBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#0366d6', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 8, gap: 4, }, tokenBtnOutline: { backgroundColor: 'transparent', borderWidth: 1, borderColor: '#0366d6', }, tokenBtnDanger: { backgroundColor: '#d73a4a', }, tokenBtnText: { color: '#fff', fontSize: 13, fontWeight: '500', }, tokenBtnTextOutline: { color: '#0366d6', }, statsRow: { flexDirection: 'row', gap: 12, }, statCard: { flex: 1, backgroundColor: '#fff', borderRadius: 12, padding: 20, alignItems: 'center', elevation: 1, shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 2, }, statNumber: { fontSize: 28, fontWeight: '700', color: '#1a1a1a', marginTop: 8, }, statLabel: { fontSize: 13, color: '#888', marginTop: 4, }, aiPlaceholder: { alignItems: 'center', paddingVertical: 10, }, aiIconWrap: { width: 60, height: 60, borderRadius: 30, backgroundColor: '#f5f0ff', justifyContent: 'center', alignItems: 'center', marginBottom: 12, }, aiTitle: { fontSize: 16, fontWeight: '600', color: '#333', marginBottom: 6, }, aiDesc: { fontSize: 13, color: '#999', textAlign: 'center', lineHeight: 18, marginBottom: 12, }, comingBadge: { backgroundColor: '#f5f0ff', paddingHorizontal: 14, paddingVertical: 4, borderRadius: 12, }, comingBadgeText: { fontSize: 12, color: '#8b5cf6', fontWeight: '500', }, aboutRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, aboutLeft: { flexDirection: 'row', alignItems: 'center', gap: 8, }, aboutRight: { flexDirection: 'row', alignItems: 'center', gap: 8, }, aboutLabel: { fontSize: 15, color: '#333', }, aboutValue: { fontSize: 15, color: '#888', }, updateBadge: { backgroundColor: '#fff3cd', paddingHorizontal: 8, paddingVertical: 2, borderRadius: 8, }, updateBadgeText: { fontSize: 11, color: '#856404', fontWeight: '500', }, });