Factor out EditSet and SetForm

This commit is contained in:
Brandon Presley 2022-07-20 15:48:48 +12:00
parent e9e7651d65
commit e191323fff
2 changed files with 233 additions and 191 deletions

View File

@ -5,31 +5,16 @@ import {
useNavigation,
useRoute,
} from '@react-navigation/native';
import React, {
useCallback,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import {NativeModules, ScrollView, StyleSheet, View} from 'react-native';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
import {Button, IconButton, TextInput} from 'react-native-paper';
import React, {useCallback, useContext} from 'react';
import {NativeModules, View} from 'react-native';
import {IconButton} from 'react-native-paper';
import {DatabaseContext} from './App';
import {HomePageParams} from './HomePage';
import {Plan} from './plan';
import Set from './set';
import {DAYS, format} from './time';
import SetForm from './SetForm';
export default function EditSet() {
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>();
const [name, setName] = useState(params.set.name);
const [reps, setReps] = useState(params.set.reps.toString());
const [weight, setWeight] = useState(params.set.weight.toString());
const [created, setCreated] = useState(new Date(params.set.created));
const [unit, setUnit] = useState(params.set.unit);
const [showDate, setShowDate] = useState(false);
const weightRef = useRef<any>(null);
const db = useContext(DatabaseContext);
const navigation = useNavigation();
@ -44,100 +29,6 @@ export default function EditSet() {
}, [navigation]),
);
const getTodaysPlan = useCallback(async (): Promise<Plan[]> => {
const today = DAYS[new Date().getDay()];
const [result] = await db.executeSql(
`SELECT * FROM plans WHERE days LIKE ? LIMIT 1`,
[`%${today}%`],
);
return result.rows.raw();
}, [db]);
const getTodaysSets = useCallback(async (): Promise<Set[]> => {
const today = new Date().toISOString().split('T')[0];
const [result] = await db.executeSql(
`SELECT * FROM sets WHERE created LIKE ? ORDER BY created DESC`,
[`${today}%`],
);
return result.rows.raw();
}, [db]);
const getBest = useCallback(
async (query: string): Promise<Set> => {
const bestWeight = `
SELECT name, reps, unit, MAX(weight) AS weight
FROM sets
WHERE name = ?
GROUP BY name;
`;
const bestReps = `
SELECT name, MAX(reps) as reps, unit, weight
FROM sets
WHERE name = ?
AND weight = ?
GROUP BY name;
`;
const [weightResult] = await db.executeSql(bestWeight, [query]);
if (!weightResult.rows.length)
return {
weight: 0,
name: '',
reps: 0,
created: new Date().toISOString(),
id: 0,
};
const [repsResult] = await db.executeSql(bestReps, [
query,
weightResult.rows.item(0).weight,
]);
return repsResult.rows.item(0);
},
[db],
);
const predict = useCallback(async () => {
if ((await AsyncStorage.getItem('predictiveSets')) === 'false') return;
const todaysPlan = await getTodaysPlan();
if (todaysPlan.length === 0) return;
const todaysSets = await getTodaysSets();
const todaysWorkouts = todaysPlan[0].workouts.split(',');
let nextWorkout = todaysWorkouts[0];
if (todaysSets.length > 0) {
const count = todaysSets.filter(
set => set.name === todaysSets[0].name,
).length;
const maxSets = await AsyncStorage.getItem('maxSets');
nextWorkout = todaysSets[0].name;
if (count >= Number(maxSets))
nextWorkout =
todaysWorkouts[todaysWorkouts.indexOf(todaysSets[0].name!) + 1];
}
const best = await getBest(nextWorkout);
setName(best.name);
setReps(best.reps.toString());
setWeight(best.weight.toString());
setUnit(best.unit);
}, [getTodaysSets, getTodaysPlan, getBest]);
useEffect(() => {
if (params.set.id) return;
predict();
}, [predict, params.set.id]);
const onConfirm = (date: Date) => {
setCreated(date);
setShowDate(false);
};
const update = useCallback(async () => {
console.log(`${EditSet.name}.update`, params.set);
await db.executeSql(
`UPDATE sets SET name = ?, reps = ?, weight = ?, created = ?, unit = ? WHERE id = ?`,
[name, reps, weight, created.toISOString(), unit, params.set.id],
);
navigation.goBack();
}, [params.set, name, reps, weight, created, unit, db, navigation]);
const notify = useCallback(async () => {
const enabled = await AsyncStorage.getItem('alarmEnabled');
if (enabled !== 'true') return;
@ -147,88 +38,43 @@ export default function EditSet() {
NativeModules.AlarmModule.timer(milliseconds);
}, []);
const add = useCallback(async () => {
if (name === undefined || reps === '' || weight === '') return;
const insert = `
INSERT INTO sets(name, reps, weight, created, unit)
VALUES (?,?,?,?,?)
`;
await db.executeSql(insert, [
name,
reps,
weight,
created.toISOString(),
unit || 'kg',
]);
notify();
navigation.goBack();
}, [name, reps, weight, created, unit, db, navigation, notify]);
const update = useCallback(
async (set: Set) => {
console.log(`${EditSet.name}.update`, set);
await db.executeSql(
`UPDATE sets SET name = ?, reps = ?, weight = ?, created = ?, unit = ? WHERE id = ?`,
[set.name, set.reps, set.weight, set.created, set.unit, set.id],
);
navigation.goBack();
},
[db, navigation],
);
const save = useCallback(async () => {
if (params.set.id) return update();
return add();
}, [update, add, params.set.id]);
const add = useCallback(
async (set: Set) => {
const {name, reps, weight, created, unit} = set;
const insert = `
INSERT INTO sets(name, reps, weight, created, unit)
VALUES (?,?,?,?,?)
`;
await db.executeSql(insert, [name, reps, weight, created, unit]);
notify();
navigation.goBack();
},
[db, navigation, notify],
);
const save = useCallback(
async (set: Set) => {
if (params.set.id) return update(set);
return add(set);
},
[update, add, params.set.id],
);
return (
<View style={{padding: 10}}>
<ScrollView style={{height: '90%'}}>
<TextInput
style={styles.marginBottom}
label="Name"
value={name}
onChangeText={setName}
autoCorrect={false}
/>
<TextInput
style={styles.marginBottom}
label="Reps"
keyboardType="numeric"
value={reps}
onChangeText={setReps}
autoFocus
blurOnSubmit={false}
onSubmitEditing={() => weightRef.current?.focus()}
/>
<TextInput
style={styles.marginBottom}
label="Weight"
keyboardType="numeric"
value={weight}
onChangeText={setWeight}
onSubmitEditing={save}
ref={weightRef}
/>
<TextInput
style={styles.marginBottom}
label="Unit (kg)"
value={unit}
onChangeText={setUnit}
onSubmitEditing={save}
/>
<Button
style={styles.marginBottom}
icon="calendar-outline"
onPress={() => setShowDate(true)}>
{format(created)}
</Button>
<DateTimePickerModal
isVisible={showDate}
mode="datetime"
onConfirm={onConfirm}
onCancel={() => setShowDate(false)}
date={created}
/>
</ScrollView>
<Button mode="contained" icon="save" onPress={save}>
Save
</Button>
<SetForm save={save} set={params.set} />
</View>
);
}
const styles = StyleSheet.create({
marginBottom: {
marginBottom: 10,
},
});

196
SetForm.tsx Normal file
View File

@ -0,0 +1,196 @@
import React, {
useCallback,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import {ScrollView, StyleSheet} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
import {Button, TextInput} from 'react-native-paper';
import {DatabaseContext} from './App';
import {Plan} from './plan';
import Set from './set';
import {DAYS, format} from './time';
export default function SetForm({
save,
set,
}: {
set: Set;
save: (set: Set) => void;
}) {
const [name, setName] = useState(set.name);
const [reps, setReps] = useState(set.reps.toString());
const [weight, setWeight] = useState(set.weight.toString());
const [created, setCreated] = useState(new Date(set.created));
const [unit, setUnit] = useState(set.unit);
const [showDate, setShowDate] = useState(false);
const weightRef = useRef<any>(null);
const db = useContext(DatabaseContext);
const getTodaysPlan = useCallback(async (): Promise<Plan[]> => {
const today = DAYS[new Date().getDay()];
const [result] = await db.executeSql(
`SELECT * FROM plans WHERE days LIKE ? LIMIT 1`,
[`%${today}%`],
);
return result.rows.raw();
}, [db]);
const getTodaysSets = useCallback(async (): Promise<Set[]> => {
const today = new Date().toISOString().split('T')[0];
const [result] = await db.executeSql(
`SELECT * FROM sets WHERE created LIKE ? ORDER BY created DESC`,
[`${today}%`],
);
return result.rows.raw();
}, [db]);
const getBest = useCallback(
async (query: string): Promise<Set> => {
const bestWeight = `
SELECT name, reps, unit, MAX(weight) AS weight
FROM sets
WHERE name = ?
GROUP BY name;
`;
const bestReps = `
SELECT name, MAX(reps) as reps, unit, weight
FROM sets
WHERE name = ?
AND weight = ?
GROUP BY name;
`;
const [weightResult] = await db.executeSql(bestWeight, [query]);
if (!weightResult.rows.length)
return {
weight: 0,
name: '',
reps: 0,
created: new Date().toISOString(),
id: 0,
};
const [repsResult] = await db.executeSql(bestReps, [
query,
weightResult.rows.item(0).weight,
]);
return repsResult.rows.item(0);
},
[db],
);
const predict = useCallback(async () => {
if ((await AsyncStorage.getItem('predictiveSets')) === 'false') return;
const todaysPlan = await getTodaysPlan();
if (todaysPlan.length === 0) return;
const todaysSets = await getTodaysSets();
const todaysWorkouts = todaysPlan[0].workouts.split(',');
let nextWorkout = todaysWorkouts[0];
if (todaysSets.length > 0) {
const count = todaysSets.filter(
s => s.name === todaysSets[0].name,
).length;
const maxSets = await AsyncStorage.getItem('maxSets');
nextWorkout = todaysSets[0].name;
if (count >= Number(maxSets))
nextWorkout =
todaysWorkouts[todaysWorkouts.indexOf(todaysSets[0].name!) + 1];
}
const best = await getBest(nextWorkout);
setName(best.name);
setReps(best.reps.toString());
setWeight(best.weight.toString());
setUnit(best.unit);
}, [getTodaysSets, getTodaysPlan, getBest]);
useEffect(() => {
if (set.id) return;
predict();
}, [predict, set.id]);
const onConfirm = (date: Date) => {
setCreated(date);
setShowDate(false);
};
const handleSubmit = () => {
save({
name,
reps: Number(reps),
created: created.toISOString(),
weight: Number(weight),
id: set.id,
unit,
});
};
const textInputs = (
<>
<TextInput
style={styles.marginBottom}
label="Name"
value={name}
onChangeText={setName}
autoCorrect={false}
/>
<TextInput
style={styles.marginBottom}
label="Reps"
keyboardType="numeric"
value={reps}
onChangeText={setReps}
autoFocus
blurOnSubmit={false}
onSubmitEditing={() => weightRef.current?.focus()}
/>
<TextInput
style={styles.marginBottom}
label="Weight"
keyboardType="numeric"
value={weight}
onChangeText={setWeight}
onSubmitEditing={handleSubmit}
ref={weightRef}
/>
<TextInput
style={styles.marginBottom}
label="Unit (kg)"
value={unit}
onChangeText={setUnit}
onSubmitEditing={handleSubmit}
/>
</>
);
return (
<>
<ScrollView style={{height: '90%'}}>
{textInputs}
<Button
style={styles.marginBottom}
icon="calendar-outline"
onPress={() => setShowDate(true)}>
{format(created)}
</Button>
<DateTimePickerModal
isVisible={showDate}
mode="datetime"
onConfirm={onConfirm}
onCancel={() => setShowDate(false)}
date={created}
/>
</ScrollView>
<Button mode="contained" icon="save" onPress={handleSubmit}>
Save
</Button>
</>
);
}
const styles = StyleSheet.create({
marginBottom: {
marginBottom: 10,
},
});