import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Alert, Switch, ActivityIndicator } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import Svg, { Path } from 'react-native-svg'; import { theme } from '../theme'; import * as Cache from '../utils/cache'; const TrashIcon = () => ( ); const InfoIcon = () => ( ); const ChevronRight = () => ( ); interface SettingItemProps { title: string; subtitle?: string; rightElement?: React.ReactNode; onPress?: () => void; showChevron?: boolean; isDanger?: boolean; } const SettingItem: React.FC = ({ title, subtitle, rightElement, onPress, showChevron = false, isDanger = false, }) => ( {title} {subtitle && ( {subtitle} )} {rightElement} {showChevron && } ); interface SettingsScreenProps { cacheOnly: boolean; resumePlaybackEnabled: boolean; playlistFiles: string[]; currentPlaylistFile: string; cachedTrackCount: number; trackCount: number; onCacheOnlyChange: (value: boolean) => Promise; onResumePlaybackChange: (value: boolean) => Promise; onCacheChanged: () => Promise | void; onPlaylistChange: (fileName: string) => Promise; } export const SettingsScreen: React.FC = ({ cacheOnly, resumePlaybackEnabled, playlistFiles, currentPlaylistFile, cachedTrackCount, trackCount, onCacheOnlyChange, onResumePlaybackChange, onCacheChanged, onPlaylistChange, }) => { const [cacheSize, setCacheSize] = useState('0 B'); const [isLoading, setIsLoading] = useState(false); const [playlistExpanded, setPlaylistExpanded] = useState(false); const [isSwitchingPlaylist, setIsSwitchingPlaylist] = useState(false); const loadCacheSize = async () => { try { const size = await Cache.getCacheSize(); setCacheSize(Cache.formatSize(size)); } catch (error) { console.error('Failed to get cache size:', error); } }; const toggleCacheOnly = async (value: boolean) => { try { await onCacheOnlyChange(value); } catch (error) { console.error('Failed to save setting:', error); Alert.alert('错误', '保存设置失败'); } }; const toggleResumePlayback = async (value: boolean) => { try { await onResumePlaybackChange(value); } catch (error) { console.error('Failed to save resume playback setting:', error); Alert.alert('错误', '保存设置失败'); } }; useEffect(() => { loadCacheSize(); }, []); const handlePlaylistPress = async (fileName: string) => { if (fileName === currentPlaylistFile || isSwitchingPlaylist) { setPlaylistExpanded(false); return; } setIsSwitchingPlaylist(true); try { await onPlaylistChange(fileName); setPlaylistExpanded(false); } catch (error) { console.error('切换歌单失败:', error); } finally { setIsSwitchingPlaylist(false); } }; const handleClearCache = () => { Alert.alert( '清除缓存', '确定要清除所有缓存的音频文件吗?这不会删除歌曲列表。', [ { text: '取消', style: 'cancel' }, { text: '清除', style: 'destructive', onPress: async () => { setIsLoading(true); try { await Cache.clearAllCache(); await onCacheChanged(); await loadCacheSize(); Alert.alert('完成', '缓存已清除'); } catch (error) { Alert.alert('错误', '清除缓存失败'); } finally { setIsLoading(false); } }, }, ] ); }; return ( 设置 播放设置 setPlaylistExpanded((prev) => !prev)} rightElement={ isSwitchingPlaylist ? : {cachedTrackCount}/{trackCount} 已缓存 } showChevron={!isSwitchingPlaylist} /> {playlistExpanded && ( {playlistFiles.map((fileName) => { const isActive = fileName === currentPlaylistFile; return ( { void handlePlaylistPress(fileName); }} disabled={isSwitchingPlaylist} > {fileName} {isActive && 当前} ); })} 自动读取所有以 playlist.json 结尾的歌单,并记住你上次选择。 )} 记住上次位置继续听 默认开启,下次打开时从上次暂停的位置继续播放 只用缓存听歌曲 节约流量,只播放已缓存的歌曲 缓存管理 } /> } /> 关于 观空禅音 ZenVoid 这不是一款为了喧闹和效率而设计的播放器。它更像一处留白,让人愿意慢下来,听见一点安静。 “ZenVoid”和“观空”都不是标准答案,只是我们尽力靠近这种感受的一种表达。希望它陪你在日常里,留一点空,听一点静。 ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: theme.colors.background, }, header: { paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.sm, }, headerTitle: { fontSize: 18, fontWeight: '600', color: theme.colors.text, }, section: { marginTop: theme.spacing.md, }, sectionTitle: { fontSize: 13, fontWeight: '600', color: theme.colors.textSecondary, textTransform: 'uppercase', letterSpacing: 0.5, paddingHorizontal: theme.spacing.md, marginBottom: theme.spacing.xs, }, settingItem: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.md, backgroundColor: theme.colors.surface, borderTopWidth: 1, borderBottomWidth: 1, borderColor: theme.colors.border, }, settingItemLeft: { flex: 1, }, settingTitle: { fontSize: 16, color: theme.colors.text, }, settingTitleDanger: { color: theme.colors.error, }, settingSubtitle: { fontSize: 14, color: theme.colors.textSecondary, marginTop: 2, }, settingMeta: { fontSize: 12, color: theme.colors.primary, marginRight: theme.spacing.xs, }, playlistOptions: { marginTop: 1, paddingHorizontal: theme.spacing.md, paddingTop: theme.spacing.sm, paddingBottom: theme.spacing.md, backgroundColor: theme.colors.surface, borderTopWidth: 1, borderBottomWidth: 1, borderColor: theme.colors.border, gap: theme.spacing.xs, }, playlistOption: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: theme.spacing.sm, paddingHorizontal: theme.spacing.sm, paddingVertical: theme.spacing.sm, borderRadius: theme.borderRadius.md, backgroundColor: 'rgba(255,255,255,0.03)', }, playlistOptionActive: { borderWidth: 1, borderColor: theme.colors.primary, backgroundColor: theme.colors.primarySoft, }, playlistOptionText: { flex: 1, fontSize: 14, color: theme.colors.text, }, playlistOptionTextActive: { color: theme.colors.primary, fontWeight: '600', }, playlistOptionBadge: { fontSize: 12, color: theme.colors.primary, }, playlistHint: { marginTop: theme.spacing.xs, fontSize: 12, lineHeight: 18, color: theme.colors.textSecondary, }, aboutCard: { marginHorizontal: theme.spacing.md, marginBottom: theme.spacing.xs, paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.md, backgroundColor: theme.colors.surface, borderWidth: 1, borderColor: theme.colors.border, borderRadius: theme.borderRadius.lg, }, aboutTitle: { fontSize: 18, fontWeight: '600', color: theme.colors.text, }, aboutEnglish: { marginTop: 4, fontSize: 13, letterSpacing: 0.8, color: theme.colors.primary, }, aboutBody: { marginTop: theme.spacing.sm, fontSize: 14, lineHeight: 22, color: theme.colors.textSecondary, }, });