Add progress to plans
This commit is contained in:
parent
67b22f9f51
commit
ecb436f8a6
|
@ -86,7 +86,7 @@ export default function EditPlan({
|
||||||
<Portal>
|
<Portal>
|
||||||
<Dialog visible={show} onDismiss={() => setShow(false)}>
|
<Dialog visible={show} onDismiss={() => setShow(false)}>
|
||||||
<Dialog.Title>{id ? `Edit "${days}"` : 'Add a plan'}</Dialog.Title>
|
<Dialog.Title>{id ? `Edit "${days}"` : 'Add a plan'}</Dialog.Title>
|
||||||
<Dialog.Content style={{alignItems: 'flex-end'}}>
|
<Dialog.Content>
|
||||||
{days.split(',').map((day, index) => (
|
{days.split(',').map((day, index) => (
|
||||||
<DayMenu
|
<DayMenu
|
||||||
index={index}
|
index={index}
|
||||||
|
|
|
@ -72,6 +72,7 @@ export default function EditSet({
|
||||||
value={name}
|
value={name}
|
||||||
onChangeText={setName}
|
onChangeText={setName}
|
||||||
onSubmitEditing={() => repsRef.current?.focus()}
|
onSubmitEditing={() => repsRef.current?.focus()}
|
||||||
|
autoCorrect={false}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={styles.text}
|
style={styles.text}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import {useFocusEffect} from '@react-navigation/native';
|
||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack';
|
import {NativeStackScreenProps} from '@react-navigation/native-stack';
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import {FlatList, StyleSheet, View} from 'react-native';
|
import {FlatList, StyleSheet, View} from 'react-native';
|
||||||
|
@ -6,9 +7,7 @@ import {RootStackParamList} from './App';
|
||||||
import {getDb} from './db';
|
import {getDb} from './db';
|
||||||
import Exercise from './exercise';
|
import Exercise from './exercise';
|
||||||
|
|
||||||
export default function Exercises({
|
export default function Exercises() {
|
||||||
navigation,
|
|
||||||
}: NativeStackScreenProps<RootStackParamList, 'Exercises'>) {
|
|
||||||
const [exercises, setExercises] = useState<Exercise[]>([]);
|
const [exercises, setExercises] = useState<Exercise[]>([]);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [refreshing, setRefresing] = useState(false);
|
const [refreshing, setRefresing] = useState(false);
|
||||||
|
@ -26,7 +25,10 @@ export default function Exercises({
|
||||||
setExercises(result.rows.raw());
|
setExercises(result.rows.raw());
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => navigation.addListener('focus', refresh), [navigation]);
|
useFocusEffect(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh();
|
refresh();
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
29
Home.tsx
29
Home.tsx
|
@ -1,13 +1,7 @@
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import {useFocusEffect} from '@react-navigation/native';
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import {
|
import {FlatList, NativeModules, SafeAreaView, StyleSheet} from 'react-native';
|
||||||
FlatList,
|
|
||||||
NativeModules,
|
|
||||||
SafeAreaView,
|
|
||||||
StyleSheet,
|
|
||||||
View,
|
|
||||||
} from 'react-native';
|
|
||||||
import {AnimatedFAB, Searchbar} from 'react-native-paper';
|
import {AnimatedFAB, Searchbar} from 'react-native-paper';
|
||||||
import {getSets} from './db';
|
import {getSets} from './db';
|
||||||
import EditSet from './EditSet';
|
import EditSet from './EditSet';
|
||||||
|
@ -24,23 +18,26 @@ export default function Home() {
|
||||||
const [showEdit, setShowEdit] = useState(false);
|
const [showEdit, setShowEdit] = useState(false);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [refreshing, setRefresing] = useState(false);
|
const [refreshing, setRefresing] = useState(false);
|
||||||
const navigation = useNavigation();
|
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
setRefresing(true);
|
const [result] = await getSets({search, limit, offset: 0});
|
||||||
const [result] = await getSets({search, limit, offset: 0}).finally(() =>
|
|
||||||
setRefresing(false),
|
|
||||||
);
|
|
||||||
if (!result) return setSets([]);
|
if (!result) return setSets([]);
|
||||||
setSets(result.rows.raw());
|
setSets(result.rows.raw());
|
||||||
setOffset(0);
|
setOffset(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const refreshLoader = async () => {
|
||||||
|
setRefresing(true);
|
||||||
|
refresh().finally(() => setRefresing(false));
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh();
|
refresh();
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
useEffect(() => navigation.addListener('focus', refresh), [navigation]);
|
useFocusEffect(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
const renderItem = ({item}: {item: Set}) => (
|
const renderItem = ({item}: {item: Set}) => (
|
||||||
<SetItem
|
<SetItem
|
||||||
|
@ -78,13 +75,12 @@ export default function Home() {
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
<Searchbar placeholder="Search" value={search} onChangeText={setSearch} />
|
<Searchbar placeholder="Search" value={search} onChangeText={setSearch} />
|
||||||
<FlatList
|
<FlatList
|
||||||
style={{height: '90%'}}
|
|
||||||
data={sets}
|
data={sets}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={set => set.id.toString()}
|
keyExtractor={set => set.id.toString()}
|
||||||
onEndReached={next}
|
onEndReached={next}
|
||||||
refreshing={refreshing}
|
refreshing={refreshing}
|
||||||
onRefresh={refresh}
|
onRefresh={refreshLoader}
|
||||||
/>
|
/>
|
||||||
<EditSet
|
<EditSet
|
||||||
clearId={() => setId(undefined)}
|
clearId={() => setId(undefined)}
|
||||||
|
@ -113,6 +109,7 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
|
flexGrow: 1,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
},
|
},
|
||||||
|
|
53
Plans.tsx
53
Plans.tsx
|
@ -1,10 +1,13 @@
|
||||||
|
import {useFocusEffect} from '@react-navigation/native';
|
||||||
|
import {format} from 'date-fns';
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import {FlatList, View} from 'react-native';
|
import {FlatList, StyleSheet, Text, View} from 'react-native';
|
||||||
import {AnimatedFAB, Searchbar} from 'react-native-paper';
|
import {AnimatedFAB, ProgressBar, Searchbar} from 'react-native-paper';
|
||||||
import {getPlans} from './db';
|
import {getPlans, getProgress} from './db';
|
||||||
import EditPlan from './EditPlan';
|
import EditPlan from './EditPlan';
|
||||||
import {Plan} from './plan';
|
import {Plan} from './plan';
|
||||||
import PlanItem from './PlanItem';
|
import PlanItem from './PlanItem';
|
||||||
|
import Progress from './progress';
|
||||||
|
|
||||||
export default function Plans() {
|
export default function Plans() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
|
@ -12,12 +15,31 @@ export default function Plans() {
|
||||||
const [refreshing, setRefresing] = useState(false);
|
const [refreshing, setRefresing] = useState(false);
|
||||||
const [id, setId] = useState<number>();
|
const [id, setId] = useState<number>();
|
||||||
const [showEdit, setShowEdit] = useState(false);
|
const [showEdit, setShowEdit] = useState(false);
|
||||||
|
const [progresses, setProgresses] = useState<Progress[]>([]);
|
||||||
|
const today = `%${format(new Date(new Date().toUTCString()), 'EEEE')}%`;
|
||||||
|
const now = `${format(new Date(new Date().toUTCString()), 'yyyy-MM-dd')}%`;
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
const [plansResult] = await getPlans({search});
|
const [plansResult] = await getPlans({search});
|
||||||
setPlans(plansResult.rows.raw());
|
setPlans(plansResult.rows.raw());
|
||||||
|
const [todaysResult] = await getPlans({search: today});
|
||||||
|
if (todaysResult.rows.length === 0) return;
|
||||||
|
const workouts: string[] = todaysResult.rows.item(0).workouts.split(',');
|
||||||
|
const newProgress: Progress[] = [];
|
||||||
|
for (const workout of workouts) {
|
||||||
|
const [workoutResult] = await getProgress({
|
||||||
|
created: now,
|
||||||
|
name: workout,
|
||||||
|
});
|
||||||
|
newProgress.push({name: workout, sets: workoutResult.rows.item(0).count});
|
||||||
|
}
|
||||||
|
setProgresses(newProgress);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useFocusEffect(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh();
|
refresh();
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
@ -33,15 +55,24 @@ export default function Plans() {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{padding: 10}}>
|
<View style={styles.container}>
|
||||||
<Searchbar value={search} onChangeText={setSearch} placeholder="Search" />
|
<Searchbar value={search} onChangeText={setSearch} placeholder="Search" />
|
||||||
|
{progresses.map(progress => (
|
||||||
|
<React.Fragment key={progress.name}>
|
||||||
|
<Text style={styles.progress}>{progress.name}</Text>
|
||||||
|
<ProgressBar progress={progress.sets / 3} />
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
|
||||||
<FlatList
|
<FlatList
|
||||||
style={{height: '90%'}}
|
|
||||||
data={plans}
|
data={plans}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={set => set.id.toString()}
|
keyExtractor={set => set.id.toString()}
|
||||||
refreshing={refreshing}
|
refreshing={refreshing}
|
||||||
onRefresh={refresh}
|
onRefresh={() => {
|
||||||
|
setRefresing(true);
|
||||||
|
refresh().finally(() => setRefresing(false));
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<EditPlan
|
<EditPlan
|
||||||
|
@ -65,3 +96,13 @@ export default function Plans() {
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexGrow: 1,
|
||||||
|
padding: 10,
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
30
Settings.tsx
30
Settings.tsx
|
@ -1,28 +1,29 @@
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack';
|
import {useFocusEffect, useNavigation} from '@react-navigation/native';
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import {NativeModules, StyleSheet, Text, View} from 'react-native';
|
import {NativeModules, StyleSheet, Text, View} from 'react-native';
|
||||||
import {Button, Snackbar, Switch, TextInput} from 'react-native-paper';
|
import {Button, Snackbar, Switch, TextInput} from 'react-native-paper';
|
||||||
import {RootStackParamList} from './App';
|
|
||||||
import {getDb} from './db';
|
|
||||||
import BatteryDialog from './BatteryDialog';
|
import BatteryDialog from './BatteryDialog';
|
||||||
|
import {getDb} from './db';
|
||||||
|
|
||||||
export default function Settings({
|
export default function Settings() {
|
||||||
navigation,
|
|
||||||
}: NativeStackScreenProps<RootStackParamList, 'Settings'>) {
|
|
||||||
const [minutes, setMinutes] = useState<string>('');
|
const [minutes, setMinutes] = useState<string>('');
|
||||||
const [seconds, setSeconds] = useState<string>('');
|
const [seconds, setSeconds] = useState<string>('');
|
||||||
const [alarmEnabled, setAlarmEnabled] = useState<boolean>(true);
|
const [alarmEnabled, setAlarmEnabled] = useState<boolean>(true);
|
||||||
const [snackbar, setSnackbar] = useState('');
|
const [snackbar, setSnackbar] = useState('');
|
||||||
const [showBattery, setShowBattery] = useState(false);
|
const [showBattery, setShowBattery] = useState(false);
|
||||||
|
const [ignoring, setIgnoring] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
const refresh = async () => {
|
||||||
(async () => {
|
|
||||||
setMinutes((await AsyncStorage.getItem('minutes')) || '3');
|
setMinutes((await AsyncStorage.getItem('minutes')) || '3');
|
||||||
setSeconds((await AsyncStorage.getItem('seconds')) || '');
|
setSeconds((await AsyncStorage.getItem('seconds')) || '');
|
||||||
setAlarmEnabled((await AsyncStorage.getItem('alarmEnabled')) === 'true');
|
setAlarmEnabled((await AsyncStorage.getItem('alarmEnabled')) === 'true');
|
||||||
})();
|
NativeModules.AlarmModule.ignoringBatteryOptimizations(setIgnoring);
|
||||||
}, [navigation]);
|
};
|
||||||
|
|
||||||
|
useFocusEffect(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (minutes) AsyncStorage.setItem('minutes', minutes);
|
if (minutes) AsyncStorage.setItem('minutes', minutes);
|
||||||
|
@ -46,13 +47,8 @@ export default function Settings({
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeAlarmEnabled = (enabled: boolean) => {
|
const changeAlarmEnabled = (enabled: boolean) => {
|
||||||
if (!enabled) return setAlarmEnabled(enabled);
|
setAlarmEnabled(enabled);
|
||||||
NativeModules.AlarmModule.ignoringBatteryOptimizations(
|
if (enabled && !ignoring) setShowBattery(true);
|
||||||
(ignoring: boolean) => {
|
|
||||||
if (ignoring) return setAlarmEnabled(true);
|
|
||||||
setShowBattery(true);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
8
db.ts
8
db.ts
|
@ -37,6 +37,14 @@ export const getPlans = ({search}: {search: string}) =>
|
||||||
db.executeSql(selectPlans, [`%${search}%`, `%${search}%`]),
|
db.executeSql(selectPlans, [`%${search}%`, `%${search}%`]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectProgress = `
|
||||||
|
SELECT count(*) as count from sets
|
||||||
|
WHERE created LIKE ?
|
||||||
|
AND name = ?
|
||||||
|
`;
|
||||||
|
export const getProgress = ({created, name}: {created: string; name: string}) =>
|
||||||
|
getDb().then(db => db.executeSql(selectProgress, [`%${created}%`, name]));
|
||||||
|
|
||||||
const selectSets = `
|
const selectSets = `
|
||||||
SELECT * from sets
|
SELECT * from sets
|
||||||
WHERE name LIKE ?
|
WHERE name LIKE ?
|
||||||
|
|
4
progress.ts
Normal file
4
progress.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default interface Progress {
|
||||||
|
name: string;
|
||||||
|
sets: number;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user