Move all database operations into db.ts
This commit is contained in:
parent
607f83955d
commit
259d36d67f
34
BestList.tsx
34
BestList.tsx
|
@ -6,39 +6,21 @@ import {
|
||||||
import React, {useCallback, useEffect, useState} from 'react';
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {FlatList, StyleSheet, View} from 'react-native';
|
import {FlatList, StyleSheet, View} from 'react-native';
|
||||||
import {List, Searchbar} from 'react-native-paper';
|
import {List, Searchbar} from 'react-native-paper';
|
||||||
import Best from './best';
|
|
||||||
import {BestPageParams} from './BestPage';
|
import {BestPageParams} from './BestPage';
|
||||||
import {db} from './db';
|
import {getBestReps, getBestWeights} from './db';
|
||||||
|
import Set from './set';
|
||||||
|
|
||||||
export default function BestList() {
|
export default function BestList() {
|
||||||
const [bests, setBests] = useState<Best[]>([]);
|
const [bests, setBests] = useState<Set[]>([]);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [refreshing, setRefresing] = useState(false);
|
const [refreshing, setRefresing] = useState(false);
|
||||||
const navigation = useNavigation<NavigationProp<BestPageParams>>();
|
const navigation = useNavigation<NavigationProp<BestPageParams>>();
|
||||||
|
|
||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
const bestWeight = `
|
const weights = await getBestWeights(search);
|
||||||
SELECT name, reps, unit, MAX(weight) AS weight
|
let newBest: Set[] = [];
|
||||||
FROM sets
|
for (const set of weights)
|
||||||
WHERE name LIKE ? AND NOT hidden
|
newBest.push(...(await getBestReps(search, set.weight)));
|
||||||
GROUP BY name;
|
|
||||||
`;
|
|
||||||
const bestReps = `
|
|
||||||
SELECT name, MAX(reps) as reps, unit, weight
|
|
||||||
FROM sets
|
|
||||||
WHERE name = ? AND weight = ? AND NOT hidden
|
|
||||||
GROUP BY name;
|
|
||||||
`;
|
|
||||||
const [weight] = await db.executeSql(bestWeight, [`%${search}%`]);
|
|
||||||
if (!weight) return setBests([]);
|
|
||||||
let newBest: Best[] = [];
|
|
||||||
for (let i = 0; i < weight.rows.length; i++) {
|
|
||||||
const [reps] = await db.executeSql(bestReps, [
|
|
||||||
weight.rows.item(i).name,
|
|
||||||
weight.rows.item(i).weight,
|
|
||||||
]);
|
|
||||||
newBest = newBest.concat(reps.rows.raw());
|
|
||||||
}
|
|
||||||
setBests(newBest);
|
setBests(newBest);
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
|
@ -55,7 +37,7 @@ export default function BestList() {
|
||||||
refresh();
|
refresh();
|
||||||
}, [search, refresh]);
|
}, [search, refresh]);
|
||||||
|
|
||||||
const renderItem = ({item}: {item: Best}) => (
|
const renderItem = ({item}: {item: Set}) => (
|
||||||
<List.Item
|
<List.Item
|
||||||
key={item.name}
|
key={item.name}
|
||||||
title={item.name}
|
title={item.name}
|
||||||
|
|
|
@ -4,15 +4,15 @@ import {createStackNavigator} from '@react-navigation/stack';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {IconButton} from 'react-native-paper';
|
import {IconButton} from 'react-native-paper';
|
||||||
import {DrawerParamList} from './App';
|
import {DrawerParamList} from './App';
|
||||||
import Best from './best';
|
|
||||||
import BestList from './BestList';
|
import BestList from './BestList';
|
||||||
|
import Set from './set';
|
||||||
import ViewBest from './ViewBest';
|
import ViewBest from './ViewBest';
|
||||||
|
|
||||||
const Stack = createStackNavigator<BestPageParams>();
|
const Stack = createStackNavigator<BestPageParams>();
|
||||||
export type BestPageParams = {
|
export type BestPageParams = {
|
||||||
BestList: {};
|
BestList: {};
|
||||||
ViewBest: {
|
ViewBest: {
|
||||||
best: Best;
|
best: Set;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,15 @@ import {FileSystem} from 'react-native-file-access';
|
||||||
import {Divider, IconButton, Menu} from 'react-native-paper';
|
import {Divider, IconButton, Menu} from 'react-native-paper';
|
||||||
import {DrawerParamList, SnackbarContext} from './App';
|
import {DrawerParamList, SnackbarContext} from './App';
|
||||||
import ConfirmDialog from './ConfirmDialog';
|
import ConfirmDialog from './ConfirmDialog';
|
||||||
import {db} from './db';
|
import {
|
||||||
|
addPlans,
|
||||||
|
addSets,
|
||||||
|
deletePlans,
|
||||||
|
deleteSets,
|
||||||
|
getAllPlans,
|
||||||
|
getAllSets,
|
||||||
|
} from './db';
|
||||||
import {Plan} from './plan';
|
import {Plan} from './plan';
|
||||||
import Set from './set';
|
|
||||||
import {write} from './write';
|
import {write} from './write';
|
||||||
|
|
||||||
const setFields = 'id,name,reps,weight,created,unit,hidden';
|
const setFields = 'id,name,reps,weight,created,unit,hidden';
|
||||||
|
@ -20,9 +26,7 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
const {reset} = useNavigation<NavigationProp<DrawerParamList>>();
|
const {reset} = useNavigation<NavigationProp<DrawerParamList>>();
|
||||||
|
|
||||||
const exportSets = useCallback(async () => {
|
const exportSets = useCallback(async () => {
|
||||||
const [result] = await db.executeSql('SELECT * FROM sets');
|
const sets = await getAllSets();
|
||||||
if (result.rows.length === 0) return;
|
|
||||||
const sets: Set[] = result.rows.raw();
|
|
||||||
const data = [setFields]
|
const data = [setFields]
|
||||||
.concat(
|
.concat(
|
||||||
sets.map(
|
sets.map(
|
||||||
|
@ -36,13 +40,11 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const exportPlans = useCallback(async () => {
|
const exportPlans = useCallback(async () => {
|
||||||
const [result] = await db.executeSql('SELECT * FROM plans');
|
const plans: Plan[] = await getAllPlans();
|
||||||
if (result.rows.length === 0) return;
|
|
||||||
const sets: Plan[] = result.rows.raw();
|
|
||||||
const data = [planFields]
|
const data = [planFields]
|
||||||
.concat(sets.map(set => `"${set.id}","${set.days}","${set.workouts}"`))
|
.concat(plans.map(set => `"${set.id}","${set.days}","${set.workouts}"`))
|
||||||
.join('\n');
|
.join('\n');
|
||||||
console.log(`${DrawerMenu.name}.exportPlans`, {length: sets.length});
|
console.log(`${DrawerMenu.name}.exportPlans`, {length: plans.length});
|
||||||
await write('plans.csv', data);
|
await write('plans.csv', data);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -66,12 +68,10 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
return `('${cells[1]}',${cells[2]},${cells[3]},'${cells[4]}','${cells[5]}',${cells[6]})`;
|
return `('${cells[1]}',${cells[2]},${cells[3]},'${cells[4]}','${cells[5]}',${cells[6]})`;
|
||||||
})
|
})
|
||||||
.join(',');
|
.join(',');
|
||||||
await db.executeSql(
|
await addSets(values);
|
||||||
`INSERT INTO sets(name,reps,weight,created,unit,hidden) VALUES ${values}`,
|
|
||||||
);
|
|
||||||
toast('Data imported.', 3000);
|
toast('Data imported.', 3000);
|
||||||
reset({index: 0, routes: [{name}]});
|
reset({index: 0, routes: [{name}]});
|
||||||
}, [db, reset, name, toast]);
|
}, [reset, name, toast]);
|
||||||
|
|
||||||
const uploadPlans = useCallback(async () => {
|
const uploadPlans = useCallback(async () => {
|
||||||
const result = await DocumentPicker.pickSingle();
|
const result = await DocumentPicker.pickSingle();
|
||||||
|
@ -88,9 +88,9 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
return `('${cells[1]}','${cells[2]}')`;
|
return `('${cells[1]}','${cells[2]}')`;
|
||||||
})
|
})
|
||||||
.join(',');
|
.join(',');
|
||||||
await db.executeSql(`INSERT INTO plans(days,workouts) VALUES ${values}`);
|
await addPlans(values);
|
||||||
toast('Data imported.', 3000);
|
toast('Data imported.', 3000);
|
||||||
}, [db, toast]);
|
}, [toast]);
|
||||||
|
|
||||||
const upload = useCallback(async () => {
|
const upload = useCallback(async () => {
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
|
@ -102,11 +102,11 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
const remove = useCallback(async () => {
|
const remove = useCallback(async () => {
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
setShowRemove(false);
|
setShowRemove(false);
|
||||||
if (name === 'Home') await db.executeSql(`DELETE FROM sets`);
|
if (name === 'Home') await deleteSets();
|
||||||
else if (name === 'Plans') await db.executeSql(`DELETE FROM plans`);
|
else if (name === 'Plans') await deletePlans();
|
||||||
toast('All data has been deleted.', 4000);
|
toast('All data has been deleted.', 4000);
|
||||||
reset({index: 0, routes: [{name}]});
|
reset({index: 0, routes: [{name}]});
|
||||||
}, [db, reset, name, toast]);
|
}, [reset, name, toast]);
|
||||||
|
|
||||||
if (name === 'Home' || name === 'Plans')
|
if (name === 'Home' || name === 'Plans')
|
||||||
return (
|
return (
|
||||||
|
|
22
EditPlan.tsx
22
EditPlan.tsx
|
@ -9,7 +9,7 @@ import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {ScrollView, StyleSheet, Text, View} from 'react-native';
|
import {ScrollView, StyleSheet, Text, View} from 'react-native';
|
||||||
import {Button, IconButton} from 'react-native-paper';
|
import {Button, IconButton} from 'react-native-paper';
|
||||||
import {DrawerParamList} from './App';
|
import {DrawerParamList} from './App';
|
||||||
import {db} from './db';
|
import {addPlan, getNames, setPlan} from './db';
|
||||||
import MassiveSwitch from './MassiveSwitch';
|
import MassiveSwitch from './MassiveSwitch';
|
||||||
import {PlanPageParams} from './PlanPage';
|
import {PlanPageParams} from './PlanPage';
|
||||||
import {DAYS} from './time';
|
import {DAYS} from './time';
|
||||||
|
@ -36,14 +36,7 @@ export default function EditPlan() {
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const refresh = async () => {
|
getNames().then(setNames);
|
||||||
const [namesResult] = await db.executeSql(
|
|
||||||
'SELECT DISTINCT name FROM sets',
|
|
||||||
);
|
|
||||||
if (!namesResult.rows.length) return setNames([]);
|
|
||||||
setNames(namesResult.rows.raw().map(({name}) => name));
|
|
||||||
};
|
|
||||||
refresh();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const save = useCallback(async () => {
|
const save = useCallback(async () => {
|
||||||
|
@ -51,16 +44,9 @@ export default function EditPlan() {
|
||||||
if (!days || !workouts) return;
|
if (!days || !workouts) return;
|
||||||
const newWorkouts = workouts.filter(workout => workout).join(',');
|
const newWorkouts = workouts.filter(workout => workout).join(',');
|
||||||
const newDays = days.filter(day => day).join(',');
|
const newDays = days.filter(day => day).join(',');
|
||||||
if (!params.plan.id)
|
if (!params.plan.id) await addPlan({days: newDays, workouts: newWorkouts});
|
||||||
await db.executeSql(`INSERT INTO plans(days, workouts) VALUES (?, ?)`, [
|
|
||||||
newDays,
|
|
||||||
newWorkouts,
|
|
||||||
]);
|
|
||||||
else
|
else
|
||||||
await db.executeSql(
|
await setPlan({days: newDays, workouts: newWorkouts, id: params.plan.id});
|
||||||
`UPDATE plans SET days = ?, workouts = ? WHERE id = ?`,
|
|
||||||
[newDays, newWorkouts, params.plan.id],
|
|
||||||
);
|
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
}, [days, workouts, params, navigation]);
|
}, [days, workouts, params, navigation]);
|
||||||
|
|
||||||
|
|
25
EditSet.tsx
25
EditSet.tsx
|
@ -8,11 +8,10 @@ import React, {useCallback, useContext} from 'react';
|
||||||
import {NativeModules, View} from 'react-native';
|
import {NativeModules, View} from 'react-native';
|
||||||
import {IconButton} from 'react-native-paper';
|
import {IconButton} from 'react-native-paper';
|
||||||
import {SnackbarContext} from './App';
|
import {SnackbarContext} from './App';
|
||||||
import {db} from './db';
|
import {addSet, getSettings, setSet} from './db';
|
||||||
import {HomePageParams} from './HomePage';
|
import {HomePageParams} from './HomePage';
|
||||||
import Set from './set';
|
import Set from './set';
|
||||||
import SetForm from './SetForm';
|
import SetForm from './SetForm';
|
||||||
import Settings from './settings';
|
|
||||||
|
|
||||||
export default function EditSet() {
|
export default function EditSet() {
|
||||||
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>();
|
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>();
|
||||||
|
@ -32,8 +31,7 @@ export default function EditSet() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const startTimer = useCallback(async () => {
|
const startTimer = useCallback(async () => {
|
||||||
const [result] = await db.executeSql(`SELECT * FROM settings LIMIT 1`);
|
const settings = await getSettings();
|
||||||
const settings: Settings = result.rows.item(0);
|
|
||||||
if (!settings.alarm) return;
|
if (!settings.alarm) return;
|
||||||
const milliseconds = settings.minutes * 60 * 1000 + settings.seconds * 1000;
|
const milliseconds = settings.minutes * 60 * 1000 + settings.seconds * 1000;
|
||||||
NativeModules.AlarmModule.timer(
|
NativeModules.AlarmModule.timer(
|
||||||
|
@ -46,10 +44,7 @@ export default function EditSet() {
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
async (set: Set) => {
|
async (set: Set) => {
|
||||||
console.log(`${EditSet.name}.update`, set);
|
console.log(`${EditSet.name}.update`, set);
|
||||||
await db.executeSql(
|
await setSet(set);
|
||||||
`UPDATE sets SET name = ?, reps = ?, weight = ?, unit = ? WHERE id = ?`,
|
|
||||||
[set.name, set.reps, set.weight, set.unit, set.id],
|
|
||||||
);
|
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
},
|
},
|
||||||
[navigation],
|
[navigation],
|
||||||
|
@ -57,19 +52,13 @@ export default function EditSet() {
|
||||||
|
|
||||||
const add = useCallback(
|
const add = useCallback(
|
||||||
async (set: Set) => {
|
async (set: Set) => {
|
||||||
const {name, reps, weight, unit, image} = set;
|
|
||||||
const insert = `
|
|
||||||
INSERT INTO sets(name, reps, weight, created, unit, image)
|
|
||||||
VALUES (?,?,?,strftime('%Y-%m-%dT%H:%M:%S', 'now', 'localtime'),?, ?)
|
|
||||||
`;
|
|
||||||
startTimer();
|
startTimer();
|
||||||
await db.executeSql(insert, [name, reps, weight, unit, image]);
|
await addSet(set);
|
||||||
const [result] = await db.executeSql(`SELECT * FROM settings LIMIT 1`);
|
const settings = await getSettings();
|
||||||
const settings: Settings = result.rows.item(0);
|
|
||||||
if (settings.notify === 0) return navigation.goBack();
|
if (settings.notify === 0) return navigation.goBack();
|
||||||
if (
|
if (
|
||||||
weight > params.set.weight ||
|
set.weight > params.set.weight ||
|
||||||
(reps > params.set.reps && weight === params.set.weight)
|
(set.reps > params.set.reps && set.weight === params.set.weight)
|
||||||
)
|
)
|
||||||
toast("Great work King, that's a new record!", 3000);
|
toast("Great work King, that's a new record!", 3000);
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
|
|
|
@ -9,13 +9,14 @@ import {Image, ScrollView, View} from 'react-native';
|
||||||
import DocumentPicker from 'react-native-document-picker';
|
import DocumentPicker from 'react-native-document-picker';
|
||||||
import {Button, IconButton} from 'react-native-paper';
|
import {Button, IconButton} from 'react-native-paper';
|
||||||
import {set} from 'react-native-reanimated';
|
import {set} from 'react-native-reanimated';
|
||||||
import {db} from './db';
|
import {addSet, getSets, setSetImage, setSetName, setWorkouts} from './db';
|
||||||
import MassiveInput from './MassiveInput';
|
import MassiveInput from './MassiveInput';
|
||||||
|
import Set from './set';
|
||||||
import {WorkoutsPageParams} from './WorkoutsPage';
|
import {WorkoutsPageParams} from './WorkoutsPage';
|
||||||
|
|
||||||
export default function EditWorkout() {
|
export default function EditWorkout() {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [uri, setUri] = useState('');
|
const [uri, setUri] = useState<string>();
|
||||||
const {params} = useRoute<RouteProp<WorkoutsPageParams, 'EditWorkout'>>();
|
const {params} = useRoute<RouteProp<WorkoutsPageParams, 'EditWorkout'>>();
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
@ -28,39 +29,24 @@ export default function EditWorkout() {
|
||||||
headerRight: null,
|
headerRight: null,
|
||||||
title: params.value.name ? params.value.name : 'New workout',
|
title: params.value.name ? params.value.name : 'New workout',
|
||||||
});
|
});
|
||||||
db.executeSql(`SELECT image FROM sets WHERE name = ? LIMIT 1`, [
|
getSets({search: params.value.name, limit: 1, offset: 0}).then(sets =>
|
||||||
params.value.name,
|
setUri(sets[0]?.image),
|
||||||
]).then(([result]) => setUri(result.rows.item(0)?.image));
|
);
|
||||||
}, [navigation, params.value.name]),
|
}, [navigation, params.value.name]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const update = useCallback(async () => {
|
const update = useCallback(async () => {
|
||||||
console.log(`${EditWorkout.name}.update`, set);
|
console.log(`${EditWorkout.name}.update`, set);
|
||||||
if (name) {
|
if (name) {
|
||||||
await db.executeSql(`UPDATE sets SET name = ? WHERE name = ?`, [
|
await setSetName(params.value.name, name);
|
||||||
name,
|
await setWorkouts(params.value.name, name);
|
||||||
params.value.name,
|
|
||||||
]);
|
|
||||||
await db.executeSql(
|
|
||||||
`UPDATE plans SET workouts = REPLACE(workouts, ?, ?)
|
|
||||||
WHERE workouts LIKE ?`,
|
|
||||||
[params.value.name, name, `%${params.value.name}%`],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (uri)
|
if (uri) await setSetImage(params.value.name, uri);
|
||||||
await db.executeSql(`UPDATE sets SET image = ? WHERE name = ?`, [
|
|
||||||
uri,
|
|
||||||
params.value.name,
|
|
||||||
]);
|
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
}, [navigation, params.value.name, name, uri]);
|
}, [navigation, params.value.name, name, uri]);
|
||||||
|
|
||||||
const add = useCallback(async () => {
|
const add = useCallback(async () => {
|
||||||
const insert = `
|
await addSet({name, reps: 0, weight: 0, hidden: true} as Set);
|
||||||
INSERT INTO sets(name, reps, weight, created, unit, hidden)
|
|
||||||
VALUES (?,0,0,strftime('%Y-%m-%dT%H:%M:%S', 'now', 'localtime'),'kg',true)
|
|
||||||
`;
|
|
||||||
await db.executeSql(insert, [name]);
|
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
}, [navigation, name]);
|
}, [navigation, name]);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {NavigationProp, useNavigation} from '@react-navigation/native';
|
||||||
import React, {useCallback, useState} from 'react';
|
import React, {useCallback, useState} from 'react';
|
||||||
import {GestureResponderEvent} from 'react-native';
|
import {GestureResponderEvent} from 'react-native';
|
||||||
import {List, Menu} from 'react-native-paper';
|
import {List, Menu} from 'react-native-paper';
|
||||||
import {db} from './db';
|
import {deletePlan} from './db';
|
||||||
import {Plan} from './plan';
|
import {Plan} from './plan';
|
||||||
import {PlanPageParams} from './PlanPage';
|
import {PlanPageParams} from './PlanPage';
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export default function PlanItem({
|
||||||
const navigation = useNavigation<NavigationProp<PlanPageParams>>();
|
const navigation = useNavigation<NavigationProp<PlanPageParams>>();
|
||||||
|
|
||||||
const remove = useCallback(async () => {
|
const remove = useCallback(async () => {
|
||||||
await db.executeSql(`DELETE FROM plans WHERE id = ?`, [item.id]);
|
if (item.id) await deletePlan(item.id);
|
||||||
setShow(false);
|
setShow(false);
|
||||||
onRemove();
|
onRemove();
|
||||||
}, [setShow, item.id, onRemove]);
|
}, [setShow, item.id, onRemove]);
|
||||||
|
|
13
PlanList.tsx
13
PlanList.tsx
|
@ -6,7 +6,7 @@ import {
|
||||||
import React, {useCallback, useEffect, useState} from 'react';
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {FlatList, StyleSheet, View} from 'react-native';
|
import {FlatList, StyleSheet, View} from 'react-native';
|
||||||
import {List, Searchbar} from 'react-native-paper';
|
import {List, Searchbar} from 'react-native-paper';
|
||||||
import {db} from './db';
|
import {getPlans} from './db';
|
||||||
import DrawerMenu from './DrawerMenu';
|
import DrawerMenu from './DrawerMenu';
|
||||||
import MassiveFab from './MassiveFab';
|
import MassiveFab from './MassiveFab';
|
||||||
import {Plan} from './plan';
|
import {Plan} from './plan';
|
||||||
|
@ -20,14 +20,7 @@ export default function PlanList() {
|
||||||
const navigation = useNavigation<NavigationProp<PlanPageParams>>();
|
const navigation = useNavigation<NavigationProp<PlanPageParams>>();
|
||||||
|
|
||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
const selectPlans = `
|
getPlans(search).then(setPlans);
|
||||||
SELECT * from plans
|
|
||||||
WHERE days LIKE ? OR workouts LIKE ?
|
|
||||||
`;
|
|
||||||
const getPlans = ({s}: {s: string}) =>
|
|
||||||
db.executeSql(selectPlans, [`%${s}%`, `%${s}%`]);
|
|
||||||
const [plansResult] = await getPlans({s: search});
|
|
||||||
setPlans(plansResult.rows.raw());
|
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
|
@ -57,7 +50,7 @@ export default function PlanList() {
|
||||||
style={{height: '100%'}}
|
style={{height: '100%'}}
|
||||||
data={plans}
|
data={plans}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={set => set.id.toString()}
|
keyExtractor={set => set.id?.toString() || ''}
|
||||||
refreshing={refreshing}
|
refreshing={refreshing}
|
||||||
onRefresh={() => {
|
onRefresh={() => {
|
||||||
setRefresing(true);
|
setRefresing(true);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {useEffect, useRef, useState} from 'react';
|
import React, {useEffect, useRef, useState} from 'react';
|
||||||
import {ScrollView} from 'react-native';
|
import {ScrollView} from 'react-native';
|
||||||
import {Button, Text} from 'react-native-paper';
|
import {Button, Text} from 'react-native-paper';
|
||||||
import {db} from './db';
|
import {getSets} from './db';
|
||||||
import MassiveInput from './MassiveInput';
|
import MassiveInput from './MassiveInput';
|
||||||
import Set from './set';
|
import Set from './set';
|
||||||
|
|
||||||
|
@ -29,9 +29,9 @@ export default function SetForm({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('SetForm.useEffect:', {uri, name: set.name});
|
console.log('SetForm.useEffect:', {uri, name: set.name});
|
||||||
if (!uri)
|
if (!uri)
|
||||||
db.executeSql(`SELECT image FROM sets WHERE name = ? LIMIT 1`, [
|
getSets({search: set.name, limit: 1, offset: 0}).then(sets =>
|
||||||
set.name,
|
setUri(sets[0]?.image),
|
||||||
]).then(([result]) => setUri(result.rows.item(0)?.image));
|
);
|
||||||
}, [uri, set.name]);
|
}, [uri, set.name]);
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {NavigationProp, useNavigation} from '@react-navigation/native';
|
||||||
import React, {useCallback, useState} from 'react';
|
import React, {useCallback, useState} from 'react';
|
||||||
import {GestureResponderEvent, Image} from 'react-native';
|
import {GestureResponderEvent, Image} from 'react-native';
|
||||||
import {Divider, List, Menu, Text} from 'react-native-paper';
|
import {Divider, List, Menu, Text} from 'react-native-paper';
|
||||||
import {db} from './db';
|
import {deleteSet} from './db';
|
||||||
import {HomePageParams} from './HomePage';
|
import {HomePageParams} from './HomePage';
|
||||||
import Set from './set';
|
import Set from './set';
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ export default function SetItem({
|
||||||
const navigation = useNavigation<NavigationProp<HomePageParams>>();
|
const navigation = useNavigation<NavigationProp<HomePageParams>>();
|
||||||
|
|
||||||
const remove = useCallback(async () => {
|
const remove = useCallback(async () => {
|
||||||
await db.executeSql(`DELETE FROM sets WHERE id = ?`, [item.id]);
|
await deleteSet(item.id);
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
onRemove();
|
onRemove();
|
||||||
}, [setShowMenu, onRemove, item.id]);
|
}, [setShowMenu, onRemove, item.id]);
|
||||||
|
|
106
SetList.tsx
106
SetList.tsx
|
@ -6,24 +6,21 @@ import {
|
||||||
import React, {useCallback, useEffect, useState} from 'react';
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {FlatList, StyleSheet, View} from 'react-native';
|
import {FlatList, StyleSheet, View} from 'react-native';
|
||||||
import {List, Searchbar} from 'react-native-paper';
|
import {List, Searchbar} from 'react-native-paper';
|
||||||
import {db} from './db';
|
import {
|
||||||
|
defaultSet,
|
||||||
|
getBest,
|
||||||
|
getSets,
|
||||||
|
getSettings,
|
||||||
|
getTodaysPlan,
|
||||||
|
getTodaysSets,
|
||||||
|
} from './db';
|
||||||
import DrawerMenu from './DrawerMenu';
|
import DrawerMenu from './DrawerMenu';
|
||||||
import {HomePageParams} from './HomePage';
|
import {HomePageParams} from './HomePage';
|
||||||
import MassiveFab from './MassiveFab';
|
import MassiveFab from './MassiveFab';
|
||||||
import {Plan} from './plan';
|
|
||||||
import Set from './set';
|
import Set from './set';
|
||||||
import SetItem from './SetItem';
|
import SetItem from './SetItem';
|
||||||
import Settings from './settings';
|
|
||||||
import {DAYS} from './time';
|
|
||||||
|
|
||||||
const limit = 15;
|
const limit = 15;
|
||||||
const defaultSet = {
|
|
||||||
name: '',
|
|
||||||
id: 0,
|
|
||||||
reps: 10,
|
|
||||||
weight: 20,
|
|
||||||
unit: 'kg',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function SetList() {
|
export default function SetList() {
|
||||||
const [sets, setSets] = useState<Set[]>();
|
const [sets, setSets] = useState<Set[]>();
|
||||||
|
@ -37,21 +34,13 @@ export default function SetList() {
|
||||||
const [images, setImages] = useState(true);
|
const [images, setImages] = useState(true);
|
||||||
const navigation = useNavigation<NavigationProp<HomePageParams>>();
|
const navigation = useNavigation<NavigationProp<HomePageParams>>();
|
||||||
|
|
||||||
const selectSets = `
|
|
||||||
SELECT * from sets
|
|
||||||
WHERE name LIKE ? AND NOT hidden
|
|
||||||
ORDER BY created DESC
|
|
||||||
LIMIT ? OFFSET ?
|
|
||||||
`;
|
|
||||||
|
|
||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
const [result] = await db.executeSql(selectSets, [`%${search}%`, limit, 0]);
|
const newSets = await getSets({search: `%${search}%`, limit, offset: 0});
|
||||||
if (!result) return setSets([]);
|
if (newSets.length === 0) return setSets([]);
|
||||||
console.log(`${SetList.name}.refresh:`, {search, limit});
|
setSets(newSets);
|
||||||
setSets(result.rows.raw());
|
|
||||||
setOffset(0);
|
setOffset(0);
|
||||||
setEnd(false);
|
setEnd(false);
|
||||||
}, [search, selectSets]);
|
}, [search]);
|
||||||
|
|
||||||
const refreshLoader = useCallback(async () => {
|
const refreshLoader = useCallback(async () => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
|
@ -62,52 +51,8 @@ export default function SetList() {
|
||||||
refresh();
|
refresh();
|
||||||
}, [search, refresh]);
|
}, [search, refresh]);
|
||||||
|
|
||||||
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 = ? AND NOT hidden
|
|
||||||
GROUP BY name;
|
|
||||||
`;
|
|
||||||
const bestReps = `
|
|
||||||
SELECT name, MAX(reps) as reps, unit, weight
|
|
||||||
FROM sets
|
|
||||||
WHERE name = ? AND weight = ? AND NOT hidden
|
|
||||||
GROUP BY name;
|
|
||||||
`;
|
|
||||||
const [weightResult] = await db.executeSql(bestWeight, [query]);
|
|
||||||
if (!weightResult.rows.length) return {...defaultSet};
|
|
||||||
const [repsResult] = await db.executeSql(bestReps, [
|
|
||||||
query,
|
|
||||||
weightResult.rows.item(0).weight,
|
|
||||||
]);
|
|
||||||
return repsResult.rows.item(0);
|
|
||||||
},
|
|
||||||
[db],
|
|
||||||
);
|
|
||||||
|
|
||||||
const predict = useCallback(async () => {
|
const predict = useCallback(async () => {
|
||||||
const [result] = await db.executeSql(`SELECT * FROM settings LIMIT 1`);
|
const settings = await getSettings();
|
||||||
const settings: Settings = result.rows.item(0);
|
|
||||||
if (!settings.predict) return;
|
if (!settings.predict) return;
|
||||||
const todaysPlan = await getTodaysPlan();
|
const todaysPlan = await getTodaysPlan();
|
||||||
console.log(`${SetList.name}.predict:`, {todaysPlan});
|
console.log(`${SetList.name}.predict:`, {todaysPlan});
|
||||||
|
@ -130,7 +75,7 @@ export default function SetList() {
|
||||||
console.log(`${SetList.name}.predict:`, {best});
|
console.log(`${SetList.name}.predict:`, {best});
|
||||||
setSet({...best});
|
setSet({...best});
|
||||||
setWorkouts(todaysWorkouts);
|
setWorkouts(todaysWorkouts);
|
||||||
}, [getTodaysSets, getTodaysPlan, getBest, db]);
|
}, []);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
|
@ -139,11 +84,8 @@ export default function SetList() {
|
||||||
navigation.getParent()?.setOptions({
|
navigation.getParent()?.setOptions({
|
||||||
headerRight: () => <DrawerMenu name="Home" />,
|
headerRight: () => <DrawerMenu name="Home" />,
|
||||||
});
|
});
|
||||||
db.executeSql('SELECT * FROM settings LIMIT 1').then(([result]) => {
|
getSettings().then(settings => setImages(!!settings.images));
|
||||||
const settings: Settings = result.rows.item(0);
|
}, [refresh, predict, navigation]),
|
||||||
setImages(!!settings.images);
|
|
||||||
});
|
|
||||||
}, [refresh, predict, navigation, db]),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
|
@ -171,15 +113,17 @@ export default function SetList() {
|
||||||
newOffset,
|
newOffset,
|
||||||
search,
|
search,
|
||||||
});
|
});
|
||||||
const [result] = await db
|
const newSets = await getSets({
|
||||||
.executeSql(selectSets, [`%${search}%`, limit, newOffset])
|
search: `%${search}%`,
|
||||||
.finally(() => setRefreshing(false));
|
limit,
|
||||||
if (result.rows.length === 0) return setEnd(true);
|
offset: newOffset,
|
||||||
|
});
|
||||||
|
if (newSets.length === 0) return setEnd(true);
|
||||||
if (!sets) return;
|
if (!sets) return;
|
||||||
setSets([...sets, ...result.rows.raw()]);
|
setSets([...sets, ...newSets]);
|
||||||
if (result.rows.length < limit) return setEnd(true);
|
if (newSets.length < limit) return setEnd(true);
|
||||||
setOffset(newOffset);
|
setOffset(newOffset);
|
||||||
}, [search, end, offset, sets, db, selectSets]);
|
}, [search, end, offset, sets]);
|
||||||
|
|
||||||
const onAdd = useCallback(async () => {
|
const onAdd = useCallback(async () => {
|
||||||
navigation.navigate('EditSet', {
|
navigation.navigate('EditSet', {
|
||||||
|
|
|
@ -10,15 +10,14 @@ import DocumentPicker from 'react-native-document-picker';
|
||||||
import {Button, Searchbar, Text} from 'react-native-paper';
|
import {Button, Searchbar, Text} from 'react-native-paper';
|
||||||
import {SnackbarContext} from './App';
|
import {SnackbarContext} from './App';
|
||||||
import ConfirmDialog from './ConfirmDialog';
|
import ConfirmDialog from './ConfirmDialog';
|
||||||
import {db} from './db';
|
import {getSettings, setSettings} from './db';
|
||||||
import MassiveInput from './MassiveInput';
|
import MassiveInput from './MassiveInput';
|
||||||
import MassiveSwitch from './MassiveSwitch';
|
import MassiveSwitch from './MassiveSwitch';
|
||||||
import Settings from './settings';
|
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
const [vibrate, setVibrate] = useState(true);
|
const [vibrate, setVibrate] = useState(true);
|
||||||
const [minutes, setMinutes] = useState<string>('');
|
const [minutes, setMinutes] = useState<string>('');
|
||||||
const [maxSets, setMaxSets] = useState<string>('3');
|
const [sets, setMaxSets] = useState<string>('3');
|
||||||
const [seconds, setSeconds] = useState<string>('');
|
const [seconds, setSeconds] = useState<string>('');
|
||||||
const [alarm, setAlarm] = useState(false);
|
const [alarm, setAlarm] = useState(false);
|
||||||
const [predict, setPredict] = useState(false);
|
const [predict, setPredict] = useState(false);
|
||||||
|
@ -31,8 +30,7 @@ export default function SettingsPage() {
|
||||||
const {toast} = useContext(SnackbarContext);
|
const {toast} = useContext(SnackbarContext);
|
||||||
|
|
||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
const [result] = await db.executeSql(`SELECT * FROM settings LIMIT 1`);
|
const settings = await getSettings();
|
||||||
const settings: Settings = result.rows.item(0);
|
|
||||||
console.log('SettingsPage.refresh:', {settings});
|
console.log('SettingsPage.refresh:', {settings});
|
||||||
setMinutes(settings.minutes.toString());
|
setMinutes(settings.minutes.toString());
|
||||||
setSeconds(settings.seconds.toString());
|
setSeconds(settings.seconds.toString());
|
||||||
|
@ -51,31 +49,18 @@ export default function SettingsPage() {
|
||||||
}, [refresh]);
|
}, [refresh]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
db.executeSql(
|
setSettings({
|
||||||
`UPDATE settings SET vibrate=?,minutes=?,sets=?,seconds=?,alarm=?,predict=?,sound=?,notify=?,images=?`,
|
vibrate: +vibrate,
|
||||||
[
|
minutes: +minutes,
|
||||||
vibrate,
|
seconds: +seconds,
|
||||||
minutes,
|
alarm: +alarm,
|
||||||
maxSets,
|
predict: +predict,
|
||||||
seconds,
|
sound,
|
||||||
alarm,
|
notify: +notify,
|
||||||
predict,
|
images: +images,
|
||||||
sound,
|
sets: +sets,
|
||||||
notify,
|
});
|
||||||
images,
|
}, [vibrate, minutes, sets, seconds, alarm, predict, sound, notify, images]);
|
||||||
],
|
|
||||||
);
|
|
||||||
}, [
|
|
||||||
vibrate,
|
|
||||||
minutes,
|
|
||||||
maxSets,
|
|
||||||
seconds,
|
|
||||||
alarm,
|
|
||||||
predict,
|
|
||||||
sound,
|
|
||||||
notify,
|
|
||||||
images,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const changeAlarmEnabled = useCallback(
|
const changeAlarmEnabled = useCallback(
|
||||||
(enabled: boolean) => {
|
(enabled: boolean) => {
|
||||||
|
@ -124,7 +109,7 @@ export default function SettingsPage() {
|
||||||
element: (
|
element: (
|
||||||
<MassiveInput
|
<MassiveInput
|
||||||
label="Sets per workout"
|
label="Sets per workout"
|
||||||
value={maxSets}
|
value={sets}
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
onChangeText={value => {
|
onChangeText={value => {
|
||||||
setMaxSets(value);
|
setMaxSets(value);
|
||||||
|
|
72
ViewBest.tsx
72
ViewBest.tsx
|
@ -12,27 +12,12 @@ import Share from 'react-native-share';
|
||||||
import ViewShot from 'react-native-view-shot';
|
import ViewShot from 'react-native-view-shot';
|
||||||
import {BestPageParams} from './BestPage';
|
import {BestPageParams} from './BestPage';
|
||||||
import Chart from './Chart';
|
import Chart from './Chart';
|
||||||
import {db} from './db';
|
import {getVolumes, getWeights} from './db';
|
||||||
|
import {Metrics} from './metrics';
|
||||||
|
import {Periods} from './periods';
|
||||||
import Set from './set';
|
import Set from './set';
|
||||||
import {formatMonth} from './time';
|
import {formatMonth} from './time';
|
||||||
|
import Volume from './volume';
|
||||||
interface Volume {
|
|
||||||
name: string;
|
|
||||||
created: string;
|
|
||||||
value: number;
|
|
||||||
unit: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Metrics {
|
|
||||||
Weight = 'Best weight per day',
|
|
||||||
Volume = 'Volume per day',
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Periods {
|
|
||||||
Weekly = 'This week',
|
|
||||||
Monthly = 'This month',
|
|
||||||
Yearly = 'This year',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ViewBest() {
|
export default function ViewBest() {
|
||||||
const {params} = useRoute<RouteProp<BestPageParams, 'ViewBest'>>();
|
const {params} = useRoute<RouteProp<BestPageParams, 'ViewBest'>>();
|
||||||
|
@ -72,51 +57,14 @@ export default function ViewBest() {
|
||||||
}, [navigation, params.best]),
|
}, [navigation, params.best]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshWeight = useCallback(async () => {
|
|
||||||
const select = `
|
|
||||||
SELECT max(weight) AS weight,
|
|
||||||
STRFTIME('%Y-%m-%d', created) as created, unit
|
|
||||||
FROM sets
|
|
||||||
WHERE name = ? AND NOT hidden
|
|
||||||
AND DATE(created) >= DATE('now', 'weekday 0', ?)
|
|
||||||
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
|
||||||
`;
|
|
||||||
let difference = '-7 days';
|
|
||||||
if (period === Periods.Monthly) difference = '-1 months';
|
|
||||||
else if (period === Periods.Yearly) difference = '-1 years';
|
|
||||||
const [result] = await db.executeSql(select, [
|
|
||||||
params.best.name,
|
|
||||||
difference,
|
|
||||||
]);
|
|
||||||
if (result.rows.length === 0) return;
|
|
||||||
setWeights(result.rows.raw());
|
|
||||||
}, [params.best.name, period]);
|
|
||||||
|
|
||||||
const refreshVolume = useCallback(async () => {
|
|
||||||
const select = `
|
|
||||||
SELECT sum(weight * reps) AS value,
|
|
||||||
STRFTIME('%Y-%m-%d', created) as created, unit
|
|
||||||
FROM sets
|
|
||||||
WHERE name = ? AND NOT hidden
|
|
||||||
AND DATE(created) >= DATE('now', 'weekday 0', ?)
|
|
||||||
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
|
||||||
`;
|
|
||||||
let difference = '-7 days';
|
|
||||||
if (period === Periods.Monthly) difference = '-1 months';
|
|
||||||
else if (period === Periods.Yearly) difference = '-1 years';
|
|
||||||
const [result] = await db.executeSql(select, [
|
|
||||||
params.best.name,
|
|
||||||
difference,
|
|
||||||
]);
|
|
||||||
if (result.rows.length === 0) return;
|
|
||||||
setVolumes(result.rows.raw());
|
|
||||||
}, [params.best.name, period]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (metric === Metrics.Weight) refreshWeight();
|
if (metric === Metrics.Weight)
|
||||||
else if (metric === Metrics.Volume) refreshVolume();
|
getWeights(params.best.name, period).then(setWeights);
|
||||||
|
else if (metric === Metrics.Volume)
|
||||||
|
getVolumes(params.best.name, period).then(setVolumes);
|
||||||
|
|
||||||
console.log(`${ViewBest.name}.useEffect`, {metric, period});
|
console.log(`${ViewBest.name}.useEffect`, {metric, period});
|
||||||
}, [params.best.name, metric, period, refreshVolume, refreshWeight]);
|
}, [params.best.name, metric, period]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewShot style={{padding: 10}} ref={viewShot}>
|
<ViewShot style={{padding: 10}} ref={viewShot}>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {GestureResponderEvent, Image} from 'react-native';
|
import {GestureResponderEvent, Image} from 'react-native';
|
||||||
import {List, Menu, Text} from 'react-native-paper';
|
import {List, Menu, Text} from 'react-native-paper';
|
||||||
import ConfirmDialog from './ConfirmDialog';
|
import ConfirmDialog from './ConfirmDialog';
|
||||||
import {db} from './db';
|
import {deleteSetsBy, getSets} from './db';
|
||||||
import Workout from './workout';
|
import Workout from './workout';
|
||||||
import {WorkoutsPageParams} from './WorkoutsPage';
|
import {WorkoutsPageParams} from './WorkoutsPage';
|
||||||
|
|
||||||
|
@ -17,20 +17,17 @@ export default function WorkoutItem({
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
const [anchor, setAnchor] = useState({x: 0, y: 0});
|
const [anchor, setAnchor] = useState({x: 0, y: 0});
|
||||||
const [showRemove, setShowRemove] = useState('');
|
const [showRemove, setShowRemove] = useState('');
|
||||||
const [uri, setUri] = useState('');
|
const [uri, setUri] = useState<string>();
|
||||||
const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>();
|
const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
db.executeSql(`SELECT image FROM sets WHERE name = ? LIMIT 1`, [
|
getSets({search: item.name, limit: 1, offset: 0}).then(sets =>
|
||||||
item.name,
|
setUri(sets[0]?.image),
|
||||||
]).then(([result]) => {
|
);
|
||||||
setUri(result.rows.item(0)?.image);
|
|
||||||
console.log(WorkoutItem.name, item.name, result.rows.item(0)?.image);
|
|
||||||
});
|
|
||||||
}, [item.name]);
|
}, [item.name]);
|
||||||
|
|
||||||
const remove = useCallback(async () => {
|
const remove = useCallback(async () => {
|
||||||
await db.executeSql(`DELETE FROM sets WHERE name = ?`, [item.name]);
|
await deleteSetsBy(item.name);
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
onRemoved();
|
onRemoved();
|
||||||
}, [setShowMenu, onRemoved, item.name]);
|
}, [setShowMenu, onRemoved, item.name]);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
import React, {useCallback, useEffect, useState} from 'react';
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {FlatList, StyleSheet, View} from 'react-native';
|
import {FlatList, StyleSheet, View} from 'react-native';
|
||||||
import {List, Searchbar} from 'react-native-paper';
|
import {List, Searchbar} from 'react-native-paper';
|
||||||
import {db} from './db';
|
import {getWorkouts} from './db';
|
||||||
import MassiveFab from './MassiveFab';
|
import MassiveFab from './MassiveFab';
|
||||||
import SetList from './SetList';
|
import SetList from './SetList';
|
||||||
import Workout from './workout';
|
import Workout from './workout';
|
||||||
|
@ -23,22 +23,16 @@ export default function WorkoutList() {
|
||||||
const [end, setEnd] = useState(false);
|
const [end, setEnd] = useState(false);
|
||||||
const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>();
|
const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>();
|
||||||
|
|
||||||
const select = `
|
|
||||||
SELECT DISTINCT sets.name
|
|
||||||
FROM sets
|
|
||||||
WHERE sets.name LIKE ?
|
|
||||||
ORDER BY sets.name
|
|
||||||
LIMIT ? OFFSET ?
|
|
||||||
`;
|
|
||||||
|
|
||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
const [result] = await db.executeSql(select, [`%${search}%`, limit, 0]);
|
const newWorkouts = await getWorkouts({
|
||||||
if (!result) return setWorkouts([]);
|
search: `%${search}%`,
|
||||||
console.log(`${WorkoutList.name}.refresh:`, {search, limit});
|
limit,
|
||||||
setWorkouts(result.rows.raw());
|
offset: 0,
|
||||||
|
});
|
||||||
|
setWorkouts(newWorkouts);
|
||||||
setOffset(0);
|
setOffset(0);
|
||||||
setEnd(false);
|
setEnd(false);
|
||||||
}, [search, select]);
|
}, [search]);
|
||||||
|
|
||||||
const refreshLoader = useCallback(async () => {
|
const refreshLoader = useCallback(async () => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
|
@ -72,15 +66,17 @@ export default function WorkoutList() {
|
||||||
newOffset,
|
newOffset,
|
||||||
search,
|
search,
|
||||||
});
|
});
|
||||||
const [result] = await db
|
const newWorkouts = await getWorkouts({
|
||||||
.executeSql(select, [`%${search}%`, limit, newOffset])
|
search: `%${search}%`,
|
||||||
.finally(() => setRefreshing(false));
|
limit,
|
||||||
if (result.rows.length === 0) return setEnd(true);
|
offset: newOffset,
|
||||||
|
}).finally(() => setRefreshing(false));
|
||||||
|
if (newWorkouts.length === 0) return setEnd(true);
|
||||||
if (!workouts) return;
|
if (!workouts) return;
|
||||||
setWorkouts([...workouts, ...result.rows.raw()]);
|
setWorkouts([...workouts, ...newWorkouts]);
|
||||||
if (result.rows.length < limit) return setEnd(true);
|
if (newWorkouts.length < limit) return setEnd(true);
|
||||||
setOffset(newOffset);
|
setOffset(newOffset);
|
||||||
}, [search, end, offset, workouts, select]);
|
}, [search, end, offset, workouts]);
|
||||||
|
|
||||||
const onAdd = useCallback(async () => {
|
const onAdd = useCallback(async () => {
|
||||||
navigation.navigate('EditWorkout', {
|
navigation.navigate('EditWorkout', {
|
||||||
|
|
6
best.ts
6
best.ts
|
@ -1,6 +0,0 @@
|
||||||
export default interface Best {
|
|
||||||
name: string;
|
|
||||||
reps: number;
|
|
||||||
weight: number;
|
|
||||||
unit: string;
|
|
||||||
}
|
|
294
db.ts
294
db.ts
|
@ -3,6 +3,13 @@ import {
|
||||||
openDatabase,
|
openDatabase,
|
||||||
SQLiteDatabase,
|
SQLiteDatabase,
|
||||||
} from 'react-native-sqlite-storage';
|
} from 'react-native-sqlite-storage';
|
||||||
|
import {Periods} from './periods';
|
||||||
|
import {Plan} from './plan';
|
||||||
|
import Set from './set';
|
||||||
|
import Settings from './settings';
|
||||||
|
import {DAYS} from './time';
|
||||||
|
import Volume from './volume';
|
||||||
|
import Workout from './workout';
|
||||||
|
|
||||||
enablePromise(true);
|
enablePromise(true);
|
||||||
|
|
||||||
|
@ -86,5 +93,290 @@ export const migrations = async () => {
|
||||||
await db.executeSql(addImages).catch(() => null);
|
await db.executeSql(addImages).catch(() => null);
|
||||||
const [result] = await db.executeSql(selectSettings);
|
const [result] = await db.executeSql(selectSettings);
|
||||||
if (result.rows.length === 0) await db.executeSql(insertSettings);
|
if (result.rows.length === 0) await db.executeSql(insertSettings);
|
||||||
return db;
|
};
|
||||||
|
|
||||||
|
export const getSettings = async () => {
|
||||||
|
const [result] = await db.executeSql(`SELECT * FROM settings LIMIT 1`);
|
||||||
|
const settings: Settings = result.rows.item(0);
|
||||||
|
return settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setSettings = async (value: Settings) => {
|
||||||
|
const update = `
|
||||||
|
UPDATE settings
|
||||||
|
SET vibrate=?,minutes=?,sets=?,seconds=?,alarm=?,
|
||||||
|
predict=?,sound=?,notify=?,images=?
|
||||||
|
`;
|
||||||
|
return db.executeSql(update, [
|
||||||
|
value.vibrate,
|
||||||
|
value.minutes,
|
||||||
|
value.sets,
|
||||||
|
value.seconds,
|
||||||
|
value.alarm,
|
||||||
|
value.predict,
|
||||||
|
value.sound,
|
||||||
|
value.notify,
|
||||||
|
value.images,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setSet = async (value: Set) => {
|
||||||
|
const update = `
|
||||||
|
UPDATE sets
|
||||||
|
SET name = ?, reps = ?, weight = ?, unit = ?
|
||||||
|
WHERE id = ?
|
||||||
|
`;
|
||||||
|
return db.executeSql(update, [
|
||||||
|
value.name,
|
||||||
|
value.reps,
|
||||||
|
value.weight,
|
||||||
|
value.unit,
|
||||||
|
value.id,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addSets = async (values: string) => {
|
||||||
|
const insert = `
|
||||||
|
INSERT INTO sets(name,reps,weight,created,unit,hidden)
|
||||||
|
VALUES ${values}
|
||||||
|
`;
|
||||||
|
return db.executeSql(insert);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addSet = async (value: Set) => {
|
||||||
|
const insert = `
|
||||||
|
INSERT INTO sets(name, reps, weight, created, unit, image)
|
||||||
|
VALUES (?,?,?,strftime('%Y-%m-%dT%H:%M:%S', 'now', 'localtime'),?, ?)
|
||||||
|
`;
|
||||||
|
const {name, reps, weight, unit, image} = value;
|
||||||
|
return db.executeSql(insert, [name, reps, weight, unit, image]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setWorkouts = async (oldName: string, newName: string) => {
|
||||||
|
const update = `
|
||||||
|
UPDATE plans SET workouts = REPLACE(workouts, ?, ?)
|
||||||
|
WHERE workouts LIKE ?
|
||||||
|
`;
|
||||||
|
return db.executeSql(update, [oldName, newName, `%${oldName}%`]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setPlan = async (value: Plan) => {
|
||||||
|
const update = `UPDATE plans SET days = ?, workouts = ? WHERE id = ?`;
|
||||||
|
return db.executeSql(update, [value.days, value.workouts, value.id]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addPlan = async (value: Plan) => {
|
||||||
|
const insert = `INSERT INTO plans(days, workouts) VALUES (?, ?)`;
|
||||||
|
return db.executeSql(insert, [value.days, value.workouts]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addPlans = async (values: string) => {
|
||||||
|
const insert = `
|
||||||
|
INSERT INTO plans(days,workouts) VALUES ${values}
|
||||||
|
`;
|
||||||
|
return db.executeSql(insert);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePlans = async () => {
|
||||||
|
return db.executeSql(`DELETE FROM plans`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteSets = async () => {
|
||||||
|
return db.executeSql(`DELETE FROM sets`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePlan = async (id: number) => {
|
||||||
|
return db.executeSql(`DELETE FROM plans WHERE id = ?`, [id]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteSet = async (id: number) => {
|
||||||
|
return db.executeSql(`DELETE FROM sets WHERE id = ?`, [id]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteSetsBy = async (name: string) => {
|
||||||
|
return db.executeSql(`DELETE FROM sets WHERE name = ?`, [name]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllPlans = async (): Promise<Plan[]> => {
|
||||||
|
const select = `SELECT * from plans`;
|
||||||
|
const [result] = await db.executeSql(select);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllSets = async (): Promise<Set[]> => {
|
||||||
|
const select = `SELECT * from sets`;
|
||||||
|
const [result] = await db.executeSql(select);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface PageParams {
|
||||||
|
search: string;
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSets = async ({
|
||||||
|
search,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}: PageParams): Promise<Set[]> => {
|
||||||
|
const select = `
|
||||||
|
SELECT * from sets
|
||||||
|
WHERE name LIKE ? AND NOT hidden
|
||||||
|
ORDER BY created DESC
|
||||||
|
LIMIT ? OFFSET ?
|
||||||
|
`;
|
||||||
|
const [result] = await db.executeSql(select, [`%${search}%`, limit, offset]);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTodaysPlan = 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();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTodaysSets = 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();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const defaultSet = {
|
||||||
|
name: '',
|
||||||
|
id: 0,
|
||||||
|
reps: 10,
|
||||||
|
weight: 20,
|
||||||
|
unit: 'kg',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBest = async (query: string): Promise<Set> => {
|
||||||
|
const bestWeight = `
|
||||||
|
SELECT name, reps, unit, MAX(weight) AS weight
|
||||||
|
FROM sets
|
||||||
|
WHERE name = ? AND NOT hidden
|
||||||
|
GROUP BY name;
|
||||||
|
`;
|
||||||
|
const bestReps = `
|
||||||
|
SELECT name, MAX(reps) as reps, unit, weight
|
||||||
|
FROM sets
|
||||||
|
WHERE name = ? AND weight = ? AND NOT hidden
|
||||||
|
GROUP BY name;
|
||||||
|
`;
|
||||||
|
const [weightResult] = await db.executeSql(bestWeight, [query]);
|
||||||
|
if (!weightResult.rows.length) return {...defaultSet};
|
||||||
|
const [repsResult] = await db.executeSql(bestReps, [
|
||||||
|
query,
|
||||||
|
weightResult.rows.item(0).weight,
|
||||||
|
]);
|
||||||
|
return repsResult.rows.item(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setSetName = async (oldName: string, newName: string) => {
|
||||||
|
const update = `UPDATE sets SET name = ? WHERE name = ?`;
|
||||||
|
return db.executeSql(update, [newName, oldName]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setSetImage = async (name: string, image: string) => {
|
||||||
|
const update = `UPDATE sets SET image = ? WHERE name = ?`;
|
||||||
|
return db.executeSql(update, [name, image]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWeights = async (
|
||||||
|
name: string,
|
||||||
|
period: Periods,
|
||||||
|
): Promise<Set[]> => {
|
||||||
|
const select = `
|
||||||
|
SELECT max(weight) AS weight,
|
||||||
|
STRFTIME('%Y-%m-%d', created) as created, unit
|
||||||
|
FROM sets
|
||||||
|
WHERE name = ? AND NOT hidden
|
||||||
|
AND DATE(created) >= DATE('now', 'weekday 0', ?)
|
||||||
|
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
||||||
|
`;
|
||||||
|
let difference = '-7 days';
|
||||||
|
if (period === Periods.Monthly) difference = '-1 months';
|
||||||
|
else if (period === Periods.Yearly) difference = '-1 years';
|
||||||
|
const [result] = await db.executeSql(select, [name, difference]);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getVolumes = async (
|
||||||
|
name: string,
|
||||||
|
period: Periods,
|
||||||
|
): Promise<Volume[]> => {
|
||||||
|
const select = `
|
||||||
|
SELECT sum(weight * reps) AS value,
|
||||||
|
STRFTIME('%Y-%m-%d', created) as created, unit
|
||||||
|
FROM sets
|
||||||
|
WHERE name = ? AND NOT hidden
|
||||||
|
AND DATE(created) >= DATE('now', 'weekday 0', ?)
|
||||||
|
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
||||||
|
`;
|
||||||
|
let difference = '-7 days';
|
||||||
|
if (period === Periods.Monthly) difference = '-1 months';
|
||||||
|
else if (period === Periods.Yearly) difference = '-1 years';
|
||||||
|
const [result] = await db.executeSql(select, [name, difference]);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getNames = async (): Promise<string[]> => {
|
||||||
|
const [result] = await db.executeSql('SELECT DISTINCT name FROM sets');
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBestWeights = async (search: string): Promise<Set[]> => {
|
||||||
|
const select = `
|
||||||
|
SELECT name, reps, unit, MAX(weight) AS weight
|
||||||
|
FROM sets
|
||||||
|
WHERE name LIKE ? AND NOT hidden
|
||||||
|
GROUP BY name;
|
||||||
|
`;
|
||||||
|
const [result] = await db.executeSql(select, [`%${search}%`]);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBestReps = async (
|
||||||
|
name: string,
|
||||||
|
weight: number,
|
||||||
|
): Promise<Set[]> => {
|
||||||
|
const select = `
|
||||||
|
SELECT name, MAX(reps) as reps, unit, weight
|
||||||
|
FROM sets
|
||||||
|
WHERE name = ? AND weight = ? AND NOT hidden
|
||||||
|
GROUP BY name;
|
||||||
|
`;
|
||||||
|
const [result] = await db.executeSql(select, [name, weight]);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPlans = async (search: string): Promise<Plan[]> => {
|
||||||
|
const select = `
|
||||||
|
SELECT * from plans
|
||||||
|
WHERE days LIKE ? OR workouts LIKE ?
|
||||||
|
`;
|
||||||
|
const [result] = await db.executeSql(select, [`%${search}%`, `%${search}%`]);
|
||||||
|
return result.rows.raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWorkouts = async ({
|
||||||
|
search,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}: PageParams): Promise<Workout[]> => {
|
||||||
|
const select = `
|
||||||
|
SELECT DISTINCT sets.name
|
||||||
|
FROM sets
|
||||||
|
WHERE sets.name LIKE ?
|
||||||
|
ORDER BY sets.name
|
||||||
|
LIMIT ? OFFSET ?
|
||||||
|
`;
|
||||||
|
const [result] = await db.executeSql(select, [search, limit, offset]);
|
||||||
|
return result.rows.raw();
|
||||||
};
|
};
|
||||||
|
|
4
metrics.ts
Normal file
4
metrics.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export enum Metrics {
|
||||||
|
Weight = 'Best weight per day',
|
||||||
|
Volume = 'Volume per day',
|
||||||
|
}
|
5
periods.ts
Normal file
5
periods.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export enum Periods {
|
||||||
|
Weekly = 'This week',
|
||||||
|
Monthly = 'This month',
|
||||||
|
Yearly = 'This year',
|
||||||
|
}
|
2
plan.ts
2
plan.ts
|
@ -1,5 +1,5 @@
|
||||||
export interface Plan {
|
export interface Plan {
|
||||||
id: number;
|
id?: number;
|
||||||
days: string;
|
days: string;
|
||||||
workouts: string;
|
workouts: string;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user