Загрузка данных


import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, SafeAreaView, TouchableOpacity, FlatList, Alert, Image, Modal } from 'react-native';
import Svg, { Polygon, Text as SvgText } from 'react-native-svg';
import * as ImagePicker from 'expo-image-picker';
import AsyncStorage from '@react-native-async-storage/async-storage';

export default function App() {
  const [isDark, setIsDark] = useState(true);
  const [themeColor, setThemeColor] = useState('#0f0');
  const [showSettings, setShowSettings] = useState(false);
  const [avatar, setAvatar] = useState(null);
  const [activeTab, setActiveTab] = useState('Ежедневные');
  const [stats, setStats] = useState({ Сила: 20, Интеллект: 20, Дисциплина: 20, Амбиции: 20, Отношения: 20, Менталка: 20 });
  
  const pool = {
    'Ежедневные': Array.from({length: 7}, (_, i) => ({t: `Задача ${i+1}`, d: '10 мин', s: 'Сила'})),
    'Еженедельные': Array.from({length: 10}, (_, i) => ({t: `Недельная ${i+1}`, d: '30 мин', s: 'Дисциплина'})),
    'Ежемесячные': Array.from({length: 15}, (_, i) => ({t: `Месячная ${i+1}`, d: '1 час', s: 'Интеллект'}))
  };

  const [quests, setQuests] = useState([]);

  useEffect(() => {
    const loadData = async () => {
      const savedStats = await AsyncStorage.getItem('stats');
      const savedAvatar = await AsyncStorage.getItem('avatar');
      if (savedStats) setStats(JSON.parse(savedStats));
      if (savedAvatar) setAvatar(savedAvatar);
      
      const allQuests = Object.keys(pool).flatMap(type => pool[type].map(item => ({ 
        id: Math.random(), title: item.t, desc: item.d, type, stat: item.s, status: null, timeLeft: 86400 
      })));
      setQuests(allQuests);
    };
    loadData();
  }, []);

  useEffect(() => {
    AsyncStorage.setItem('stats', JSON.stringify(stats));
    if (avatar) AsyncStorage.setItem('avatar', avatar);
  }, [stats, avatar]);

  useEffect(() => {
    const timer = setInterval(() => {
      setQuests(prev => prev.map(q => q.timeLeft > 0 ? {...q, timeLeft: q.timeLeft - 1} : {...q, timeLeft: 86400, status: null}));
    }, 1000);
    return () => clearInterval(timer);
  }, []);

  const handleAction = (id, type) => {
    setQuests(prev => prev.map(q => q.id === id ? {...q, status: type} : q));
    setStats(s => ({...s, [quests.find(x => x.id === id).stat]: Math.max(0, Math.min(type === 'done' ? s[quests.find(x => x.id === id).stat] + 5 : s[quests.find(x => x.id === id).stat] - 5, 100))}));
  };

  const getPoints = (r, vals) => {
    return Object.keys(vals).map((k, i) => {
      const a = (Math.PI * 2 * i) / 6 - Math.PI / 2;
      const v = (vals[k] / 100) * r;
      return `${150 + v * Math.cos(a)},${150 + v * Math.sin(a)}`;
    }).join(" ");
  };

  return (
    <SafeAreaView style={[styles.container, { backgroundColor: isDark ? '#000' : '#fff' }]}>
      <TouchableOpacity style={styles.settingsBtn} onPress={() => setShowSettings(true)}>
        <Text style={{fontSize: 24}}>⚙️</Text>
      </TouchableOpacity>
      
      <Modal visible={showSettings} transparent={true}>
        <View style={styles.modal}>
          <TouchableOpacity onPress={() => setIsDark(!isDark)} style={styles.menuBtn}>
            <Text style={{color:'#fff'}}>Переключить тему</Text>
          </TouchableOpacity>
          <View style={{flexDirection:'row'}}>
            {['#0f0', '#f00', '#0ff', '#ffd700'].map(c => (
              <TouchableOpacity key={c} onPress={() => setThemeColor(c)} style={[styles.colorBtn, {backgroundColor: c}]}/>
            ))}
          </View>
          <TouchableOpacity onPress={() => setStats({Сила:20, Интеллект:20, Дисциплина:20, Амбиции:20, Отношения:20, Менталка:20})}>
            <Text style={{color:'red', marginTop: 10}}>СБРОС СТАТ</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => setShowSettings(false)}>
            <Text style={{color:'#fff', marginTop:20}}>Назад</Text>
          </TouchableOpacity>
        </View>
      </Modal>

      <TouchableOpacity onPress={async () => { let r = await ImagePicker.launchImageLibraryAsync(); if(!r.canceled) setAvatar(r.assets[0].uri); }}>
        <View style={[styles.avatarBorder, {borderColor: themeColor}]}>
          <Image source={avatar ? {uri: avatar} : {uri: 'https://via.placeholder.com/100'}} style={styles.avatar}/>
        </View>
      </TouchableOpacity>

      <View style={styles.radarContainer}>
        <Svg height={300} width={300}>
          {[80, 53, 26].map(r => <Polygon key={r} points={getPoints(r, {1:100,2:100,3:100,4:100,5:100,6:100})} fill="none" stroke={isDark ? "#555" : "#ccc"} strokeWidth="1"/>)}
          <Polygon points={getPoints(80, stats)} fill={themeColor} fillOpacity="0.4" stroke={themeColor} strokeWidth="2"/>
          {Object.keys(stats).map((k, i) => {
            const a = (Math.PI * 2 * i) / 6 - Math.PI / 2;
            return <SvgText key={k} x={150 + 115 * Math.cos(a)} y={150 + 115 * Math.sin(a)} fill={isDark ? '#fff' : '#000'} fontSize="12">{`${k} (${stats[k]}%)`}</SvgText>
          })}
        </Svg>
      </View>

      <View style={styles.tabBar}>
        {['Ежедневные', 'Еженедельные', 'Ежемесячные'].map(t => (
          <TouchableOpacity key={t} onPress={() => setActiveTab(t)}>
            <Text style={{color: activeTab === t ? themeColor : '#888'}}>{t}</Text>
          </TouchableOpacity>
        ))}
      </View>

      <FlatList 
        data={quests.filter(q => q.type === activeTab)} 
        renderItem={({item}) => (
          <View style={[styles.card, {backgroundColor: item.status === 'done' ? '#060' : item.status === 'fail' ? '#600' : '#333'}]}>
            <Text style={{color:'#fff'}}>{item.title} ({item.desc})</Text>
            <View style={{flexDirection:'row'}}>
              <TouchableOpacity disabled={!!item.status} onPress={() => handleAction(item.id, 'done')}><Text>✅</Text></TouchableOpacity>
              <TouchableOpacity disabled={!!item.status} onPress={() => handleAction(item.id, 'fail')}><Text>❌</Text></TouchableOpacity>
            </View>
          </View>
        )}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20 },
  avatarBorder: { width: 80, height: 80, borderRadius: 40, borderWidth: 3, alignItems: 'center', justifyContent: 'center' },
  avatar: { width: 70, height: 70, borderRadius: 35 },
  radarContainer: { alignItems: 'center', marginVertical: 20 },
  tabBar: { flexDirection: 'row', justifyContent: 'space-around', marginBottom: 20 },
  card: { padding: 15, marginVertical: 5, borderRadius: 10, flexDirection: 'row', justifyContent: 'space-between' },
  settingsBtn: { position: 'absolute', top: 50, right: 20 },
  modal: { flex: 1, backgroundColor: 'rgba(0,0,0,0.8)', justifyContent: 'center', alignItems: 'center' },
  colorBtn: { width: 40, height: 40, borderRadius: 20, margin: 5 },
  menuBtn: { padding: 10, backgroundColor: '#333', borderRadius: 5, marginBottom: 10 }
});