import React, {useEffect, useState} from "react"; import { RefreshControl, SectionList, SectionListData, Text, TouchableOpacity, View, } from "react-native"; import { ReportSummary, ReportHeader, ReportHeaderProps, ReportItem, } from "../../components/home/report"; import Snackbar from "react-native-snackbar"; import {getStr} from "../../utils/i18n"; import themes from "../../assets/themes/themes"; import {helper} from "../../redux/store"; import {Course} from "thu-info-lib/dist/models/home/report"; import {useColorScheme} from "react-native"; export const semesterWeight = (semester: string): number => { const year = Number(semester.slice(0, 4)); let term: number; switch (semester[5]) { case "春": term = 0; break; case "夏": term = 1; break; case "秋": term = 2; break; default: term = 3; } return year * 10 + term; }; type Section = SectionListData<Course> & ReportHeaderProps; // totalCredits <= allCredits const prepareData = ( src: Course[], ): { gpa: number; totalCredits: number; allCredits: number; totalPoints: number; sections: Section[]; } => { const semesters = new Set(src.map((course) => course.semester)); const sortedSemesters = [...semesters].sort( (a, b) => semesterWeight(a) - semesterWeight(b), ); let totalCredits = 0; let totalPoints = 0; let allCredits = 0; const sections = sortedSemesters.map((semester) => { const courses = src.filter((course) => course.semester === semester); const credits = courses.reduce( (acc, course) => acc + (isNaN(course.point) ? 0 : course.credit), 0, ); const points = courses.reduce( (acc, course) => acc + (isNaN(course.point) ? 0 : course.point * course.credit), 0, ); totalCredits += credits; totalPoints += points; const allCreditsPerSection = courses.reduce( (acc, course) => acc + (isNaN(course.credit) || ![ "A+", "A", "A-", "B+", "B", "B-", "C+", "C", "C-", "D+", "D", "P", "EX", ].includes(course.grade) ? 0 : course.credit), 0, ); allCredits += allCreditsPerSection; return { semester, gpa: points / credits, totalCredits: credits, allCredits: allCreditsPerSection, totalPoints: points, data: courses, }; }); return { gpa: totalPoints / totalCredits, sections: sections.reverse(), allCredits, totalCredits, totalPoints, }; }; export const ReportScreen = () => { const [report, setReport] = useState<Course[]>(); const [refreshing, setRefreshing] = useState(true); const [flag, setFlag] = useState<1 | 2 | 3>(1); const [bx, setBx] = useState(false); const themeName = useColorScheme(); const theme = themes(themeName); const fetchData = () => { setRefreshing(true); helper .getReport(bx && flag === 1, true, flag) .then((res) => { setReport(res); setRefreshing(false); }) .catch(() => { Snackbar.show({ text: getStr("networkRetry"), duration: Snackbar.LENGTH_SHORT, }); setRefreshing(false); }); }; useEffect(fetchData, [bx, flag]); const {gpa, sections, allCredits, totalCredits, totalPoints} = prepareData( report || [], ); return ( <View style={{marginHorizontal: 20, flex: 1}}> <View style={{flexDirection: "row", margin: 5}}> <TouchableOpacity style={{padding: 6, flex: 1}} onPress={() => setFlag(1)}> <Text style={{ color: flag === 1 ? "blue" : theme.colors.text, textAlign: "center", }}> {getStr("reportFlag1")} </Text> </TouchableOpacity> <TouchableOpacity style={{padding: 6, flex: 1}} onPress={() => setFlag(2)}> <Text style={{ color: flag === 2 ? "blue" : theme.colors.text, textAlign: "center", }}> {getStr("reportFlag2")} </Text> </TouchableOpacity> <TouchableOpacity style={{padding: 6, flex: 1}} onPress={() => setFlag(3)}> <Text style={{ color: flag === 3 ? "blue" : theme.colors.text, textAlign: "center", }}> {getStr("reportFlag3")} </Text> </TouchableOpacity> <TouchableOpacity style={{padding: 6, flex: 1}} onPress={() => setBx((o) => !o)}> <Text style={{color: "blue", textAlign: "center"}}> {getStr(bx ? "bx" : "bxr")} </Text> </TouchableOpacity> </View> <SectionList sections={sections} stickySectionHeadersEnabled={false} renderSectionHeader={({section}) => ( <ReportHeader semester={section.semester} gpa={section.gpa} totalCredits={section.totalCredits} allCredits={section.allCredits} totalPoints={section.totalPoints} /> )} renderItem={({item}) => ( <ReportItem name={item.name} credit={item.credit} grade={item.grade} point={item.point} /> )} ListHeaderComponent={ <ReportSummary gpa={gpa} totalCredits={totalCredits} allCredits={allCredits} totalPoints={totalPoints} /> } refreshControl={ <RefreshControl refreshing={refreshing} onRefresh={fetchData} colors={[theme.colors.accent]} /> } keyExtractor={(item, index) => `${item.semester}${index}`} /> </View> ); };