Factor out EditSet and SetForm
This commit is contained in:
parent
e9e7651d65
commit
e191323fff
216
EditSet.tsx
216
EditSet.tsx
|
@ -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 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 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.toISOString(),
|
||||
unit || 'kg',
|
||||
]);
|
||||
await db.executeSql(insert, [name, reps, weight, created, unit]);
|
||||
notify();
|
||||
navigation.goBack();
|
||||
}, [name, reps, weight, created, unit, db, navigation, notify]);
|
||||
},
|
||||
[db, navigation, notify],
|
||||
);
|
||||
|
||||
const save = useCallback(async () => {
|
||||
if (params.set.id) return update();
|
||||
return add();
|
||||
}, [update, add, params.set.id]);
|
||||
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
196
SetForm.tsx
Normal 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,
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user