Add workouts page
The workouts page can be used to add a new type of workout, or to edit the name of an already existing one. Closes #1.
This commit is contained in:
parent
fd38439756
commit
23d8c91c69
15
App.tsx
15
App.tsx
|
@ -14,7 +14,15 @@ import {
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {SQLiteDatabase} from 'react-native-sqlite-storage';
|
import {SQLiteDatabase} from 'react-native-sqlite-storage';
|
||||||
import Ionicon from 'react-native-vector-icons/Ionicons';
|
import Ionicon from 'react-native-vector-icons/Ionicons';
|
||||||
import {addSound, createPlans, createSets, createSettings, getDb} from './db';
|
import {
|
||||||
|
addHidden,
|
||||||
|
addSound,
|
||||||
|
createPlans,
|
||||||
|
createSets,
|
||||||
|
createSettings,
|
||||||
|
createWorkouts,
|
||||||
|
getDb,
|
||||||
|
} from './db';
|
||||||
import Routes from './Routes';
|
import Routes from './Routes';
|
||||||
|
|
||||||
export const Drawer = createDrawerNavigator<DrawerParamList>();
|
export const Drawer = createDrawerNavigator<DrawerParamList>();
|
||||||
|
@ -23,6 +31,7 @@ export type DrawerParamList = {
|
||||||
Settings: {};
|
Settings: {};
|
||||||
Best: {};
|
Best: {};
|
||||||
Plans: {};
|
Plans: {};
|
||||||
|
Workouts: {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DatabaseContext = React.createContext<SQLiteDatabase>({} as any);
|
export const DatabaseContext = React.createContext<SQLiteDatabase>({} as any);
|
||||||
|
@ -61,13 +70,15 @@ const App = () => {
|
||||||
await _db.executeSql(createSets);
|
await _db.executeSql(createSets);
|
||||||
await _db.executeSql(createSettings);
|
await _db.executeSql(createSettings);
|
||||||
await _db.executeSql(addSound).catch(() => null);
|
await _db.executeSql(addSound).catch(() => null);
|
||||||
setDb(_db);
|
await _db.executeSql(createWorkouts);
|
||||||
|
await _db.executeSql(addHidden).catch(() => null);
|
||||||
const [result] = await _db.executeSql(`SELECT * FROM settings LIMIT 1`);
|
const [result] = await _db.executeSql(`SELECT * FROM settings LIMIT 1`);
|
||||||
if (result.rows.length === 0)
|
if (result.rows.length === 0)
|
||||||
return _db.executeSql(`
|
return _db.executeSql(`
|
||||||
INSERT INTO settings(minutes,seconds,alarm,vibrate,predict,sets)
|
INSERT INTO settings(minutes,seconds,alarm,vibrate,predict,sets)
|
||||||
VALUES(3,30,false,true,true,3);
|
VALUES(3,30,false,true,true,3);
|
||||||
`);
|
`);
|
||||||
|
setDb(_db);
|
||||||
};
|
};
|
||||||
init();
|
init();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -22,14 +22,13 @@ export default function BestList() {
|
||||||
const bestWeight = `
|
const bestWeight = `
|
||||||
SELECT name, reps, unit, MAX(weight) AS weight
|
SELECT name, reps, unit, MAX(weight) AS weight
|
||||||
FROM sets
|
FROM sets
|
||||||
WHERE name LIKE ?
|
WHERE name LIKE ? AND NOT hidden
|
||||||
GROUP BY name;
|
GROUP BY name;
|
||||||
`;
|
`;
|
||||||
const bestReps = `
|
const bestReps = `
|
||||||
SELECT name, MAX(reps) as reps, unit, weight
|
SELECT name, MAX(reps) as reps, unit, weight
|
||||||
FROM sets
|
FROM sets
|
||||||
WHERE name = ?
|
WHERE name = ? AND weight = ? AND NOT hidden
|
||||||
AND weight = ?
|
|
||||||
GROUP BY name;
|
GROUP BY name;
|
||||||
`;
|
`;
|
||||||
const [weight] = await db.executeSql(bestWeight, [`%${search}%`]);
|
const [weight] = await db.executeSql(bestWeight, [`%${search}%`]);
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default function ConfirmDialog({
|
||||||
setShow,
|
setShow,
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
children: string;
|
children: JSX.Element | JSX.Element[] | string;
|
||||||
onOk: () => void;
|
onOk: () => void;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
setShow: (show: boolean) => void;
|
setShow: (show: boolean) => void;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {Plan} from './plan';
|
||||||
import Set from './set';
|
import Set from './set';
|
||||||
import {write} from './write';
|
import {write} from './write';
|
||||||
|
|
||||||
const setFields = 'id,name,reps,weight,created,unit';
|
const setFields = 'id,name,reps,weight,created,unit,hidden';
|
||||||
const planFields = 'id,days,workouts';
|
const planFields = 'id,days,workouts';
|
||||||
|
|
||||||
export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
|
@ -27,7 +27,7 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
.concat(
|
.concat(
|
||||||
sets.map(
|
sets.map(
|
||||||
set =>
|
set =>
|
||||||
`${set.id},${set.name},${set.reps},${set.weight},${set.created},${set.unit}`,
|
`${set.id},${set.name},${set.reps},${set.weight},${set.created},${set.unit},${set.hidden}`,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
@ -63,11 +63,11 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||||
.filter(line => line)
|
.filter(line => line)
|
||||||
.map(set => {
|
.map(set => {
|
||||||
const cells = set.split(',');
|
const cells = set.split(',');
|
||||||
return `('${cells[1]}',${cells[2]},${cells[3]},'${cells[4]}','${cells[5]}')`;
|
return `('${cells[1]}',${cells[2]},${cells[3]},'${cells[4]}','${cells[5]}',${cells[6]})`;
|
||||||
})
|
})
|
||||||
.join(',');
|
.join(',');
|
||||||
await db.executeSql(
|
await db.executeSql(
|
||||||
`INSERT INTO sets(name,reps,weight,created,unit) VALUES ${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}]});
|
||||||
|
|
80
EditWorkout.tsx
Normal file
80
EditWorkout.tsx
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import {
|
||||||
|
RouteProp,
|
||||||
|
useFocusEffect,
|
||||||
|
useNavigation,
|
||||||
|
useRoute,
|
||||||
|
} from '@react-navigation/native';
|
||||||
|
import React, {useCallback, useContext, useState} from 'react';
|
||||||
|
import {ScrollView} from 'react-native';
|
||||||
|
import {Button, IconButton} from 'react-native-paper';
|
||||||
|
import {set} from 'react-native-reanimated';
|
||||||
|
import {DatabaseContext} from './App';
|
||||||
|
import MassiveInput from './MassiveInput';
|
||||||
|
import {WorkoutsPageParams} from './WorkoutsPage';
|
||||||
|
|
||||||
|
export default function EditWorkout() {
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
const {params} = useRoute<RouteProp<WorkoutsPageParams, 'EditWorkout'>>();
|
||||||
|
const db = useContext(DatabaseContext);
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
navigation.getParent()?.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<IconButton icon="arrow-back" onPress={() => navigation.goBack()} />
|
||||||
|
),
|
||||||
|
headerRight: null,
|
||||||
|
title: params.value.name ? 'Edit workout' : 'New workout',
|
||||||
|
});
|
||||||
|
}, [navigation, params.value.name]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const update = useCallback(async () => {
|
||||||
|
console.log(`${EditWorkout.name}.update`, set);
|
||||||
|
await db.executeSql(`UPDATE sets SET name = ? WHERE name = ?`, [
|
||||||
|
name,
|
||||||
|
params.value.name,
|
||||||
|
]);
|
||||||
|
await db.executeSql(
|
||||||
|
`UPDATE plans SET workouts = REPLACE(workouts, ?, ?)
|
||||||
|
WHERE workouts LIKE ?`,
|
||||||
|
[params.value.name, name, `%${params.value.name}%`],
|
||||||
|
);
|
||||||
|
navigation.goBack();
|
||||||
|
}, [db, navigation, params.value.name, name]);
|
||||||
|
|
||||||
|
const add = useCallback(async () => {
|
||||||
|
const insert = `
|
||||||
|
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();
|
||||||
|
}, [db, navigation, name]);
|
||||||
|
|
||||||
|
const save = useCallback(async () => {
|
||||||
|
if (params.value.name) return update();
|
||||||
|
return add();
|
||||||
|
}, [update, add, params.value.name]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView style={{padding: 10, height: '90%'}}>
|
||||||
|
{params.value.name ? (
|
||||||
|
<>
|
||||||
|
<MassiveInput label="Old name" value={params.value.name} disabled />
|
||||||
|
<MassiveInput label="New name" value={name} onChangeText={setName} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<MassiveInput label="Name" value={name} onChangeText={setName} />
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
disabled={!name && !!params.value.name}
|
||||||
|
mode="contained"
|
||||||
|
icon="save"
|
||||||
|
onPress={save}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
|
@ -4,10 +4,10 @@ import {IconButton} from 'react-native-paper';
|
||||||
import {SQLiteDatabase} from 'react-native-sqlite-storage';
|
import {SQLiteDatabase} from 'react-native-sqlite-storage';
|
||||||
import {DatabaseContext, Drawer, DrawerParamList} from './App';
|
import {DatabaseContext, Drawer, DrawerParamList} from './App';
|
||||||
import BestPage from './BestPage';
|
import BestPage from './BestPage';
|
||||||
import DrawerMenu from './DrawerMenu';
|
|
||||||
import HomePage from './HomePage';
|
import HomePage from './HomePage';
|
||||||
import PlanPage from './PlanPage';
|
import PlanPage from './PlanPage';
|
||||||
import SettingsPage from './SettingsPage';
|
import SettingsPage from './SettingsPage';
|
||||||
|
import WorkoutsPage from './WorkoutsPage';
|
||||||
|
|
||||||
interface Route {
|
interface Route {
|
||||||
name: keyof DrawerParamList;
|
name: keyof DrawerParamList;
|
||||||
|
@ -24,6 +24,7 @@ export default function Routes({db}: {db: SQLiteDatabase | null}) {
|
||||||
{name: 'Home', component: HomePage, icon: 'home'},
|
{name: 'Home', component: HomePage, icon: 'home'},
|
||||||
{name: 'Plans', component: PlanPage, icon: 'calendar'},
|
{name: 'Plans', component: PlanPage, icon: 'calendar'},
|
||||||
{name: 'Best', component: BestPage, icon: 'stats-chart'},
|
{name: 'Best', component: BestPage, icon: 'stats-chart'},
|
||||||
|
{name: 'Workouts', component: WorkoutsPage, icon: 'barbell'},
|
||||||
{name: 'Settings', component: SettingsPage, icon: 'settings'},
|
{name: 'Settings', component: SettingsPage, icon: 'settings'},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ export default function SetList() {
|
||||||
|
|
||||||
const selectSets = `
|
const selectSets = `
|
||||||
SELECT * from sets
|
SELECT * from sets
|
||||||
WHERE name LIKE ?
|
WHERE name LIKE ? AND NOT hidden
|
||||||
ORDER BY created DESC
|
ORDER BY created DESC
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
`;
|
`;
|
||||||
|
@ -85,14 +85,13 @@ export default function SetList() {
|
||||||
const bestWeight = `
|
const bestWeight = `
|
||||||
SELECT name, reps, unit, MAX(weight) AS weight
|
SELECT name, reps, unit, MAX(weight) AS weight
|
||||||
FROM sets
|
FROM sets
|
||||||
WHERE name = ?
|
WHERE name = ? AND NOT hidden
|
||||||
GROUP BY name;
|
GROUP BY name;
|
||||||
`;
|
`;
|
||||||
const bestReps = `
|
const bestReps = `
|
||||||
SELECT name, MAX(reps) as reps, unit, weight
|
SELECT name, MAX(reps) as reps, unit, weight
|
||||||
FROM sets
|
FROM sets
|
||||||
WHERE name = ?
|
WHERE name = ? AND weight = ? AND NOT hidden
|
||||||
AND weight = ?
|
|
||||||
GROUP BY name;
|
GROUP BY name;
|
||||||
`;
|
`;
|
||||||
const [weightResult] = await db.executeSql(bestWeight, [query]);
|
const [weightResult] = await db.executeSql(bestWeight, [query]);
|
||||||
|
|
|
@ -74,14 +74,14 @@ export default function ViewBest() {
|
||||||
SELECT max(weight) AS weight,
|
SELECT max(weight) AS weight,
|
||||||
STRFTIME('%Y-%m-%d', created) as created, unit
|
STRFTIME('%Y-%m-%d', created) as created, unit
|
||||||
FROM sets
|
FROM sets
|
||||||
WHERE name = ?
|
WHERE name = ? AND NOT hidden
|
||||||
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
||||||
`;
|
`;
|
||||||
const selectVolumes = `
|
const selectVolumes = `
|
||||||
SELECT sum(weight * reps) AS value,
|
SELECT sum(weight * reps) AS value,
|
||||||
STRFTIME('%Y-%m-%d', created) as created, unit
|
STRFTIME('%Y-%m-%d', created) as created, unit
|
||||||
FROM sets
|
FROM sets
|
||||||
WHERE name = ?
|
WHERE name = ? AND NOT hidden
|
||||||
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
GROUP BY name, STRFTIME('%Y-%m-%d', created)
|
||||||
`;
|
`;
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
|
|
74
WorkoutItem.tsx
Normal file
74
WorkoutItem.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import {NavigationProp, useNavigation} from '@react-navigation/native';
|
||||||
|
import React, {useCallback, useContext, useState} from 'react';
|
||||||
|
import {GestureResponderEvent, Text} from 'react-native';
|
||||||
|
import {List, Menu} from 'react-native-paper';
|
||||||
|
import {DatabaseContext} from './App';
|
||||||
|
import ConfirmDialog from './ConfirmDialog';
|
||||||
|
import Workout from './workout';
|
||||||
|
import {WorkoutsPageParams} from './WorkoutsPage';
|
||||||
|
|
||||||
|
export default function WorkoutItem({
|
||||||
|
item,
|
||||||
|
onRemoved,
|
||||||
|
}: {
|
||||||
|
item: Workout;
|
||||||
|
onRemoved: () => void;
|
||||||
|
}) {
|
||||||
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
|
const [anchor, setAnchor] = useState({x: 0, y: 0});
|
||||||
|
const [showRemove, setShowRemove] = useState('');
|
||||||
|
const db = useContext(DatabaseContext);
|
||||||
|
const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>();
|
||||||
|
|
||||||
|
const remove = useCallback(async () => {
|
||||||
|
await db.executeSql(`DELETE FROM sets WHERE name = ?`, [item.name]);
|
||||||
|
setShowMenu(false);
|
||||||
|
onRemoved();
|
||||||
|
}, [setShowMenu, db, onRemoved, item.name]);
|
||||||
|
|
||||||
|
const longPress = useCallback(
|
||||||
|
(e: GestureResponderEvent) => {
|
||||||
|
setAnchor({x: e.nativeEvent.pageX, y: e.nativeEvent.pageY});
|
||||||
|
setShowMenu(true);
|
||||||
|
},
|
||||||
|
[setShowMenu, setAnchor],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<List.Item
|
||||||
|
onPress={() => navigation.navigate('EditWorkout', {value: item})}
|
||||||
|
title={item.name}
|
||||||
|
onLongPress={longPress}
|
||||||
|
right={() => (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
alignSelf: 'center',
|
||||||
|
}}>
|
||||||
|
<Menu
|
||||||
|
anchor={anchor}
|
||||||
|
visible={showMenu}
|
||||||
|
onDismiss={() => setShowMenu(false)}>
|
||||||
|
<Menu.Item
|
||||||
|
icon="trash"
|
||||||
|
onPress={() => {
|
||||||
|
setShowRemove(item.name);
|
||||||
|
setShowMenu(false);
|
||||||
|
}}
|
||||||
|
title="Delete"
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ConfirmDialog
|
||||||
|
title={`Delete ${showRemove}`}
|
||||||
|
show={!!showRemove}
|
||||||
|
setShow={show => (show ? null : setShowRemove(''))}
|
||||||
|
onOk={remove}>
|
||||||
|
This irreversibly deletes ALL sets related to this workout. Are you
|
||||||
|
sure?
|
||||||
|
</ConfirmDialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
125
WorkoutList.tsx
Normal file
125
WorkoutList.tsx
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import {
|
||||||
|
NavigationProp,
|
||||||
|
useFocusEffect,
|
||||||
|
useNavigation,
|
||||||
|
} from '@react-navigation/native';
|
||||||
|
import React, {useCallback, useContext, useEffect, useState} from 'react';
|
||||||
|
import {FlatList, StyleSheet, View} from 'react-native';
|
||||||
|
import {List, Searchbar} from 'react-native-paper';
|
||||||
|
import {DatabaseContext} from './App';
|
||||||
|
import DrawerMenu from './DrawerMenu';
|
||||||
|
import MassiveFab from './MassiveFab';
|
||||||
|
import SetList from './SetList';
|
||||||
|
import Workout from './workout';
|
||||||
|
import WorkoutItem from './WorkoutItem';
|
||||||
|
import {WorkoutsPageParams} from './WorkoutsPage';
|
||||||
|
|
||||||
|
const limit = 15;
|
||||||
|
|
||||||
|
export default function WorkoutList() {
|
||||||
|
const [workouts, setWorkouts] = useState<Workout[]>();
|
||||||
|
const [offset, setOffset] = useState(0);
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
const [end, setEnd] = useState(false);
|
||||||
|
const db = useContext(DatabaseContext);
|
||||||
|
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 [result] = await db.executeSql(select, [`%${search}%`, limit, 0]);
|
||||||
|
if (!result) return setWorkouts([]);
|
||||||
|
console.log(`${WorkoutList.name}.refresh:`, {search, limit});
|
||||||
|
setWorkouts(result.rows.raw());
|
||||||
|
setOffset(0);
|
||||||
|
setEnd(false);
|
||||||
|
}, [search, db, select]);
|
||||||
|
|
||||||
|
const refreshLoader = useCallback(async () => {
|
||||||
|
setRefreshing(true);
|
||||||
|
refresh().finally(() => setRefreshing(false));
|
||||||
|
}, [setRefreshing, refresh]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refresh();
|
||||||
|
}, [search, refresh]);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
refresh();
|
||||||
|
navigation.getParent()?.setOptions({
|
||||||
|
headerRight: () => <DrawerMenu name="Home" />,
|
||||||
|
});
|
||||||
|
}, [refresh, navigation]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({item}: {item: Workout}) => (
|
||||||
|
<WorkoutItem item={item} key={item.name} onRemoved={refresh} />
|
||||||
|
),
|
||||||
|
[refresh],
|
||||||
|
);
|
||||||
|
|
||||||
|
const next = useCallback(async () => {
|
||||||
|
if (end) return;
|
||||||
|
setRefreshing(true);
|
||||||
|
const newOffset = offset + limit;
|
||||||
|
console.log(`${SetList.name}.next:`, {
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
newOffset,
|
||||||
|
search,
|
||||||
|
});
|
||||||
|
const [result] = await db
|
||||||
|
.executeSql(select, [`%${search}%`, limit, newOffset])
|
||||||
|
.finally(() => setRefreshing(false));
|
||||||
|
if (result.rows.length === 0) return setEnd(true);
|
||||||
|
if (!workouts) return;
|
||||||
|
setWorkouts([...workouts, ...result.rows.raw()]);
|
||||||
|
if (result.rows.length < limit) return setEnd(true);
|
||||||
|
setOffset(newOffset);
|
||||||
|
}, [search, end, offset, workouts, db, select]);
|
||||||
|
|
||||||
|
const onAdd = useCallback(async () => {
|
||||||
|
navigation.navigate('EditWorkout', {
|
||||||
|
value: {name: '', sets: 3},
|
||||||
|
});
|
||||||
|
}, [navigation]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Searchbar placeholder="Search" value={search} onChangeText={setSearch} />
|
||||||
|
<FlatList
|
||||||
|
data={workouts}
|
||||||
|
style={{height: '95%', paddingBottom: 10}}
|
||||||
|
ListEmptyComponent={
|
||||||
|
<List.Item
|
||||||
|
title="No workouts yet."
|
||||||
|
description="A workout is something you do at the gym. For example Deadlifts are a workout."
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
renderItem={renderItem}
|
||||||
|
keyExtractor={w => w.name}
|
||||||
|
onEndReached={next}
|
||||||
|
refreshing={refreshing}
|
||||||
|
onRefresh={refreshLoader}
|
||||||
|
/>
|
||||||
|
<MassiveFab onPress={onAdd} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexGrow: 1,
|
||||||
|
padding: 10,
|
||||||
|
paddingBottom: '10%',
|
||||||
|
},
|
||||||
|
});
|
42
WorkoutsPage.tsx
Normal file
42
WorkoutsPage.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import {DrawerNavigationProp} from '@react-navigation/drawer';
|
||||||
|
import {useNavigation} from '@react-navigation/native';
|
||||||
|
import {createStackNavigator} from '@react-navigation/stack';
|
||||||
|
import React from 'react';
|
||||||
|
import {IconButton} from 'react-native-paper';
|
||||||
|
import {DrawerParamList} from './App';
|
||||||
|
import EditWorkout from './EditWorkout';
|
||||||
|
import Workout from './workout';
|
||||||
|
import WorkoutList from './WorkoutList';
|
||||||
|
|
||||||
|
export type WorkoutsPageParams = {
|
||||||
|
WorkoutList: {};
|
||||||
|
EditWorkout: {
|
||||||
|
value: Workout;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const Stack = createStackNavigator<WorkoutsPageParams>();
|
||||||
|
|
||||||
|
export default function WorkoutsPage() {
|
||||||
|
const navigation = useNavigation<DrawerNavigationProp<DrawerParamList>>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack.Navigator
|
||||||
|
screenOptions={{headerShown: false, animationEnabled: false}}>
|
||||||
|
<Stack.Screen name="WorkoutList" component={WorkoutList} />
|
||||||
|
<Stack.Screen
|
||||||
|
name="EditWorkout"
|
||||||
|
component={EditWorkout}
|
||||||
|
listeners={{
|
||||||
|
beforeRemove: () => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<IconButton icon="menu" onPress={navigation.openDrawer} />
|
||||||
|
),
|
||||||
|
title: 'Workouts',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack.Navigator>
|
||||||
|
);
|
||||||
|
}
|
11
db.ts
11
db.ts
|
@ -36,3 +36,14 @@ export const createSettings = `
|
||||||
export const addSound = `
|
export const addSound = `
|
||||||
ALTER TABLE settings ADD COLUMN sound TEXT NULL;
|
ALTER TABLE settings ADD COLUMN sound TEXT NULL;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const createWorkouts = `
|
||||||
|
CREATE TABLE IF NOT EXISTS workouts(
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
sets INTEGER DEFAULT 3
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const addHidden = `
|
||||||
|
ALTER TABLE sets ADD COLUMN hidden DEFAULT false;
|
||||||
|
`;
|
||||||
|
|
5
plan.ts
5
plan.ts
|
@ -3,8 +3,3 @@ export interface Plan {
|
||||||
days: string;
|
days: string;
|
||||||
workouts: string;
|
workouts: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Workout {
|
|
||||||
name: string;
|
|
||||||
sets: number;
|
|
||||||
}
|
|
||||||
|
|
1
set.ts
1
set.ts
|
@ -5,4 +5,5 @@ export default interface Set {
|
||||||
weight: number;
|
weight: number;
|
||||||
created?: string;
|
created?: string;
|
||||||
unit?: string;
|
unit?: string;
|
||||||
|
hidden?: boolean;
|
||||||
}
|
}
|
||||||
|
|
4
workout.ts
Normal file
4
workout.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default interface Workout {
|
||||||
|
name: string;
|
||||||
|
sets: number;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user