import { RouteProp, useFocusEffect, useRoute } from "@react-navigation/native"; import { format } from "date-fns"; import { useCallback, useEffect, useMemo, useState } from "react"; import { Keyboard, ScrollView, View } from "react-native"; import { FileSystem } from "react-native-file-access"; import { IconButton, List } from "react-native-paper"; import Share from "react-native-share"; import { captureScreen } from "react-native-view-shot"; import { StackParams } from "./AppStack"; import AppLineChart from "./AppLineChart"; import Select from "./Select"; import StackHeader from "./StackHeader"; import { MARGIN, PADDING } from "./constants"; import { setRepo, settingsRepo } from "./db"; import GymSet from "./gym-set"; import { Metrics } from "./metrics"; import { Periods } from "./periods"; import Volume from "./volume"; import AppInput from "./AppInput"; import { DateTimePickerAndroid } from "@react-native-community/datetimepicker"; import Settings from "./settings"; export default function ViewGraph() { const { params } = useRoute>(); const [weights, setWeights] = useState(); const [volumes, setVolumes] = useState(); const [metric, setMetric] = useState(Metrics.OneRepMax); const [period, setPeriod] = useState(Periods.Monthly); const [unit, setUnit] = useState('kg'); const [start, setStart] = useState(null) const [end, setEnd] = useState(null) const [settings, setSettings] = useState({} as Settings); useFocusEffect(useCallback(() => { settingsRepo.findOne({ where: {} }).then(setSettings) }, [])) const convertWeight = (weight: number, unitFrom: string, unitTo: string) => { if (unitFrom === unitTo) return weight if (unitFrom === 'lb' && unitTo === 'kg') return weight * 0.453592; if (unitFrom === 'kg' && unitTo === 'lb') return weight * 2.20462; }; useEffect(() => { let difference = "-7 days"; if (period === Periods.Monthly) difference = "-1 months"; else if (period === Periods.Yearly) difference = "-1 years"; else if (period === Periods.TwoMonths) difference = "-2 months"; else if (period === Periods.ThreeMonths) difference = "-3 months"; else if (period === Periods.SixMonths) difference = "-6 months"; else if (period === Periods.AllTime) difference = null; let group = "%Y-%m-%d"; if (period === Periods.Yearly) group = "%Y-%m"; const builder = setRepo .createQueryBuilder() .select("STRFTIME('%Y-%m-%d', created)", "created") .addSelect("unit") .where("name = :name", { name: params.name }) .andWhere("NOT hidden") if (start) builder.andWhere("DATE(created) >= :start", { start }); if (end) builder.andWhere("DATE(created) <= :end", { end }); if (difference) builder.andWhere("DATE(created) >= DATE('now', 'weekday 0', :difference)", { difference, }); builder .groupBy("name") .addGroupBy(`STRFTIME('${group}', created)`); switch (metric) { case Metrics.Best: builder .addSelect("ROUND(MAX(weight), 2)", "weight") .getRawMany() .then(newWeights => newWeights.map(set => ({ ...set, weight: convertWeight(set.weight, set.unit, unit) }))) .then(setWeights); break; case Metrics.Volume: builder .addSelect("ROUND(SUM(weight * reps), 2)", "value") .getRawMany() .then(newWeights => newWeights.map(set => ({ ...set, value: convertWeight(set.value, set.unit, unit) }))) .then(setVolumes); break; default: // Brzycki formula https://en.wikipedia.org/wiki/One-repetition_maximum#Brzycki builder .addSelect( "ROUND(MAX(weight / (1.0278 - 0.0278 * reps)), 2)", "weight" ) .getRawMany() .then(newWeights => newWeights.map(set => ({ ...set, weight: convertWeight(set.weight, set.unit, unit) }))) .then((newWeights) => { console.log(`${ViewGraph.name}.oneRepMax:`, { weights: newWeights }); setWeights(newWeights); }); } }, [params.name, metric, period, unit, start, end]); const weightChart = useMemo(() => { if (weights === undefined) return null; if (weights.length === 0) return ; return ( set.weight)} labels={weights.map((set) => format(new Date(set.created), "yyyy-MM-d") )} /> ); }, [weights]); const volumeChart = useMemo(() => { if (volumes === undefined) return null; if (volumes.length === 0) return ; return ( volume.value)} labels={volumes.map((volume) => format(new Date(volume.created), "yyyy-MM-d") )} /> ); }, [volumes]); const pickStart = useCallback(() => { DateTimePickerAndroid.open({ value: start || new Date(), onChange: (event, date) => { if (event.type === 'dismissed') return; if (date === start) return; setStart(date); setPeriod(Periods.AllTime); Keyboard.dismiss(); }, mode: "date", }); }, [start]); const pickEnd = useCallback(() => { DateTimePickerAndroid.open({ value: end || new Date(), onChange: (event, date) => { if (event.type === 'dismissed') return; if (date === end) return; setEnd(date); setPeriod(Periods.AllTime); Keyboard.dismiss(); }, mode: "date", }); }, [end]); return ( <> captureScreen().then(async (uri) => { const base64 = await FileSystem.readFile(uri, "base64"); const url = `data:image/jpeg;base64,${base64}`; Share.open({ type: "image/jpeg", url, }); }) } icon="share" /> { setPeriod(value as Periods); setStart(null); setEnd(null); }} value={period} />