Massive/StartPlan.tsx
Brandon Presley 3bf2193d46 Remove needless double negation in StartPlan
We used to store the numbers as sqlite presented
the booleans, but now TypeOrm automatically converts
it into bools for us so we don't need to.
2022-11-02 12:43:01 +13:00

182 lines
5.9 KiB
TypeScript

import {RouteProp, useFocusEffect, useRoute} from '@react-navigation/native'
import {useCallback, useMemo, useRef, useState} from 'react'
import {NativeModules, TextInput, View} from 'react-native'
import {FlatList} from 'react-native-gesture-handler'
import {Button} from 'react-native-paper'
import {getBestSet} from './best.service'
import {PADDING} from './constants'
import CountMany from './count-many'
import {AppDataSource} from './data-source'
import {getNow, setRepo, settingsRepo} from './db'
import GymSet from './gym-set'
import MassiveInput from './MassiveInput'
import {PlanPageParams} from './plan-page-params'
import SetForm from './SetForm'
import Settings from './settings'
import StackHeader from './StackHeader'
import StartPlanItem from './StartPlanItem'
import {toast} from './toast'
export default function StartPlan() {
const {params} = useRoute<RouteProp<PlanPageParams, 'StartPlan'>>()
const [name, setName] = useState('')
const [reps, setReps] = useState('')
const [weight, setWeight] = useState('')
const [unit, setUnit] = useState<string>('kg')
const [minutes, setMinutes] = useState(3)
const [seconds, setSeconds] = useState(30)
const [best, setBest] = useState<GymSet>()
const [selected, setSelected] = useState(0)
const [settings, setSettings] = useState<Settings>()
const [counts, setCounts] = useState<CountMany[]>()
const weightRef = useRef<TextInput>(null)
const repsRef = useRef<TextInput>(null)
const unitRef = useRef<TextInput>(null)
const workouts = useMemo(() => params.plan.workouts.split(','), [params])
const [selection, setSelection] = useState({
start: 0,
end: 0,
})
const refresh = useCallback(() => {
const questions = workouts
.map((workout, index) => `('${workout}',${index})`)
.join(',')
console.log({questions, workouts})
const select = `
SELECT workouts.name, COUNT(sets.id) as total
FROM (select 0 as name, 0 as sequence union values ${questions}) as workouts
LEFT JOIN sets ON sets.name = workouts.name
AND sets.created LIKE STRFTIME('%Y-%m-%d%%', 'now', 'localtime')
AND NOT sets.hidden
GROUP BY workouts.name
ORDER BY workouts.sequence
LIMIT -1
OFFSET 1
`
return AppDataSource.manager.query(select).then(newCounts => {
setCounts(newCounts)
console.log(`${StartPlan.name}.focus:`, {newCounts})
return newCounts
})
}, [workouts])
const select = useCallback(
async (index: number, newCounts?: CountMany[]) => {
setSelected(index)
console.log(`${StartPlan.name}.next:`, {name, index})
if (!counts && !newCounts) return
const workout = counts ? counts[index] : newCounts[index]
console.log(`${StartPlan.name}.next:`, {workout})
const newBest = await getBestSet(workout.name)
console.log(`${StartPlan.name}.next:`, {newBest})
setMinutes(newBest.minutes)
setSeconds(newBest.seconds)
setName(newBest.name)
setReps(newBest.reps.toString())
setWeight(newBest.weight.toString())
setUnit(newBest.unit)
setBest(newBest)
},
[name, counts],
)
useFocusEffect(
useCallback(() => {
refresh().then(newCounts => select(0, newCounts))
settingsRepo.findOne({where: {}}).then(setSettings)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refresh]),
)
const handleSubmit = async () => {
console.log(`${SetForm.name}.handleSubmit:`, {reps, weight, unit, best})
const [{now}] = await getNow()
await setRepo.save({
name,
weight: +weight,
reps: +reps,
unit,
created: now,
minutes,
seconds,
sets: best.sets,
hidden: false,
})
await refresh()
if (
settings.notify &&
(+weight > best.weight || (+reps > best.reps && +weight === best.weight))
)
toast("Great work King! That's a new record.")
else if (settings.alarm) toast('Resting...')
else toast('Added set')
if (!settings.alarm) return
const milliseconds = Number(minutes) * 60 * 1000 + Number(seconds) * 1000
const {vibrate, sound, noSound} = settings
const args = [milliseconds, vibrate, sound, noSound]
NativeModules.AlarmModule.timer(...args)
}
const handleUnit = useCallback((value: string) => {
setUnit(value.replace(/,|'/g, ''))
if (value.match(/,|'/))
toast('Commas and single quotes would break CSV exports')
}, [])
return (
<>
<StackHeader title={params.plan.days.replace(/,/g, ', ')} />
<View style={{padding: PADDING, flex: 1, flexDirection: 'column'}}>
<View style={{flex: 1}}>
<MassiveInput
label="Reps"
keyboardType="numeric"
value={reps}
onChangeText={setReps}
onSubmitEditing={() => weightRef.current?.focus()}
selection={selection}
onSelectionChange={e => setSelection(e.nativeEvent.selection)}
innerRef={repsRef}
/>
<MassiveInput
label="Weight"
keyboardType="numeric"
value={weight}
onChangeText={setWeight}
onSubmitEditing={handleSubmit}
innerRef={weightRef}
blurOnSubmit
/>
{settings?.showUnit && (
<MassiveInput
autoCapitalize="none"
label="Unit"
value={unit}
onChangeText={handleUnit}
innerRef={unitRef}
/>
)}
{counts && (
<FlatList
data={counts}
renderItem={props => (
<StartPlanItem
{...props}
onUndo={refresh}
onSelect={select}
selected={selected}
/>
)}
/>
)}
</View>
<Button mode="contained" icon="save" onPress={handleSubmit}>
Save
</Button>
</View>
</>
)
}