From 3cbabb723ad43c6ababf54b3ea442d66921dc981 Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Wed, 5 Oct 2022 23:38:52 +1300 Subject: [PATCH] Add sessions page Related to #82 --- EditSet.tsx | 8 +-- Routes.tsx | 2 + SessionList.tsx | 71 ++++++++++++++++++++ SessionPage.tsx | 36 ++++++++++ SetForm.tsx | 128 +++++++++++++++-------------------- SetItem.tsx | 6 +- SetList.tsx | 53 ++------------- SettingsPage.tsx | 27 -------- StartSession.tsx | 147 +++++++++++++++++++++++++++++++++++++++++ TimerPage.tsx | 101 ---------------------------- count-many.ts | 4 ++ drawer-param-list.ts | 1 + home-page-params.ts | 2 - massive.db | 0 session-page-params.ts | 10 +++ set.service.ts | 12 ++++ settings.ts | 2 - use-settings.ts | 2 +- 18 files changed, 350 insertions(+), 262 deletions(-) create mode 100644 SessionList.tsx create mode 100644 SessionPage.tsx create mode 100644 StartSession.tsx delete mode 100644 TimerPage.tsx create mode 100644 count-many.ts create mode 100644 massive.db create mode 100644 session-page-params.ts diff --git a/EditSet.tsx b/EditSet.tsx index 17ebb0a..1643f00 100644 --- a/EditSet.tsx +++ b/EditSet.tsx @@ -18,7 +18,7 @@ import {useSettings} from './use-settings'; export default function EditSet() { const {params} = useRoute>(); - const {set, count, workouts} = params; + const {set} = params; const navigation = useNavigation(); const {toast} = useContext(SnackbarContext); const {settings, setSettings} = useSettings(); @@ -28,8 +28,6 @@ export default function EditSet() { console.log(`${EditSet.name}.focus:`, set); let title = 'Create set'; if (typeof set.id === 'number') title = 'Edit set'; - else if (Number(set.sets) > 0 && settings.newSet === 'predict') - title = `${set.name} (${count + 1} / ${set.sets})`; navigation.getParent()?.setOptions({ headerLeft: () => ( navigation.goBack()} /> @@ -37,7 +35,7 @@ export default function EditSet() { headerRight: null, title, }); - }, [navigation, set, count, settings.newSet]), + }, [navigation, set]), ); const startTimer = useCallback( @@ -93,7 +91,7 @@ export default function EditSet() { return ( - + ); } diff --git a/Routes.tsx b/Routes.tsx index c6cd3ff..b34bcb8 100644 --- a/Routes.tsx +++ b/Routes.tsx @@ -6,6 +6,7 @@ import {DrawerParamList} from './drawer-param-list'; import HomePage from './HomePage'; import PlanPage from './PlanPage'; import Route from './route'; +import SessionPage from './SessionPage'; import SettingsPage from './SettingsPage'; import useDark from './use-dark'; import WorkoutsPage from './WorkoutsPage'; @@ -18,6 +19,7 @@ export default function Routes() { const routes: Route[] = [ {name: 'Home', component: HomePage, icon: 'home'}, {name: 'Plans', component: PlanPage, icon: 'event'}, + {name: 'Session', component: SessionPage, icon: 'directions-run'}, {name: 'Best', component: BestPage, icon: 'insights'}, {name: 'Workouts', component: WorkoutsPage, icon: 'fitness-center'}, {name: 'Settings', component: SettingsPage, icon: 'settings'}, diff --git a/SessionList.tsx b/SessionList.tsx new file mode 100644 index 0000000..f53c4b0 --- /dev/null +++ b/SessionList.tsx @@ -0,0 +1,71 @@ +import { + NavigationProp, + useFocusEffect, + useNavigation, +} from '@react-navigation/native'; +import React, {useCallback, useEffect, useState} from 'react'; +import {FlatList} from 'react-native'; +import {List} from 'react-native-paper'; +import {getBestSet} from './best.service'; +import Page from './Page'; +import {Plan} from './plan'; +import {getPlans} from './plan.service'; +import {SessionPageParams} from './session-page-params'; + +export default function SessionList() { + const [search, setSearch] = useState(''); + const [plans, setPlans] = useState([]); + const navigation = useNavigation>(); + + const refresh = useCallback(async () => { + getPlans(search).then(setPlans); + }, [search]); + + useFocusEffect( + useCallback(() => { + refresh(); + }, [refresh]), + ); + + useEffect(() => { + refresh(); + }, [search, refresh]); + + const press = useCallback( + async (item: Plan) => { + const workouts = item.workouts.split(','); + const first = workouts[0]; + const set = await getBestSet(first); + navigation.navigate('StartSession', {plan: item, set}); + }, + [navigation], + ); + + const renderItem = useCallback( + ({item}: {item: Plan}) => ( + press(item)} + /> + ), + [press], + ); + + return ( + + set.id?.toString() || ''} + ListEmptyComponent={ + + } + /> + + ); +} diff --git a/SessionPage.tsx b/SessionPage.tsx new file mode 100644 index 0000000..3bfe1d2 --- /dev/null +++ b/SessionPage.tsx @@ -0,0 +1,36 @@ +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 './drawer-param-list'; +import {SessionPageParams} from './session-page-params'; +import SessionList from './SessionList'; +import StartSession from './StartSession'; + +const Stack = createStackNavigator(); + +export default function SessionPage() { + const navigation = useNavigation>(); + + return ( + + + { + navigation.setOptions({ + headerLeft: () => ( + + ), + title: 'Session', + }); + }, + }} + /> + + ); +} diff --git a/SetForm.tsx b/SetForm.tsx index 41fcd2e..2e4eecf 100644 --- a/SetForm.tsx +++ b/SetForm.tsx @@ -1,7 +1,7 @@ import React, {useCallback, useContext, useRef, useState} from 'react'; -import {ScrollView, TextInput, View} from 'react-native'; +import {TextInput} from 'react-native'; import DocumentPicker from 'react-native-document-picker'; -import {Button, Card, Text, TouchableRipple} from 'react-native-paper'; +import {Button, Card, TouchableRipple} from 'react-native-paper'; import ConfirmDialog from './ConfirmDialog'; import {MARGIN} from './constants'; import MassiveInput from './MassiveInput'; @@ -13,11 +13,11 @@ import {useSettings} from './use-settings'; export default function SetForm({ save, set, - workouts, + next, }: { set: Set; save: (set: Set) => void; - workouts: string[]; + next?: () => void; }) { const [name, setName] = useState(set.name); const [reps, setReps] = useState(set.reps.toString()); @@ -86,77 +86,59 @@ export default function SetForm({ return ( <> - + repsRef.current?.focus()} + /> + weightRef.current?.focus()} + selection={selection} + onSelectionChange={e => setSelection(e.nativeEvent.selection)} + autoFocus={!!name} + innerRef={repsRef} + /> + + {!!settings.showUnit && ( repsRef.current?.focus()} + autoCapitalize="none" + label="Unit" + value={unit} + onChangeText={handleUnit} + innerRef={unitRef} /> - weightRef.current?.focus()} - selection={selection} - onSelectionChange={e => setSelection(e.nativeEvent.selection)} - autoFocus={!!name} - innerRef={repsRef} - /> - - {!!settings.showUnit && ( - - )} - {workouts.length > 0 && !!settings.workouts && ( - - {workouts.map((workout, index) => ( - - - {workout} - - {index === workouts.length - 1 ? '' : ', '} - - ))} - - )} - {!!settings.images && newImage && ( - setShowRemove(true)}> - - - )} - {!!settings.images && !newImage && ( - - )} - + )} + {!!settings.images && newImage && ( + setShowRemove(true)}> + + + )} + {!!settings.images && !newImage && ( + + )} + {next && } + + ); +} diff --git a/TimerPage.tsx b/TimerPage.tsx deleted file mode 100644 index 78a959f..0000000 --- a/TimerPage.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useEffect, useState} from 'react'; -import {NativeModules, View} from 'react-native'; -import {Button, Text} from 'react-native-paper'; -import {MARGIN, PADDING} from './constants'; -import { - getNext, - getSettings, - settings, - updateSettings, -} from './settings.service'; - -export default function TimerPage() { - const [next, setNext] = useState(new Date()); - const [ms, setMs] = useState(0); - const [intervalId, setIntervalId] = useState(0); - - const seconds = - ms > 0 - ? Math.floor((ms / 1000) % 60) - .toString() - .padStart(2, '0') - : '00'; - - const minutes = - ms > 0 - ? Math.floor(ms / 1000 / 60) - .toString() - .padStart(2, '0') - : '00'; - - useFocusEffect( - useCallback(() => { - getNext().then(nextIso => - setNext(nextIso ? new Date(nextIso) : new Date()), - ); - }, []), - ); - - const tick = (date: Date) => { - const remaining = date.getTime() - new Date().getTime(); - console.log(`${TimerPage.name}.tick`, {remaining}); - if (remaining <= 0) return 0; - setMs(remaining); - return remaining; - }; - - useEffect(() => { - console.log(`${TimerPage.name}.useEffect:`, {next}); - const date = next || new Date(); - if (tick(date) <= 0) return; - const id = setInterval(() => { - if (tick(date) <= 0) clearInterval(id); - }, 1000); - setIntervalId(oldId => { - clearInterval(oldId); - return id; - }); - return () => clearInterval(id); - }, [next]); - - const stop = () => { - NativeModules.AlarmModule.stop(); - setNext(new Date()); - updateSettings({...settings, nextAlarm: undefined}); - getSettings(); - tick(new Date()); - setMs(0); - }; - - const add = async () => { - console.log(`${TimerPage.name}.add:`, {intervalId, next}); - const date = next || new Date(); - date.setTime(date.getTime() + 1000 * 60); - await updateSettings({...settings, nextAlarm: date.toISOString()}); - setNext(date); - NativeModules.AlarmModule.add(ms, !!settings.vibrate, settings.sound); - tick(date); - const id = setInterval(() => { - if (tick(date) <= 0) clearInterval(id); - }, 1000); - setIntervalId(oldId => { - clearInterval(oldId); - return id; - }); - }; - - return ( - - - {minutes}:{seconds} - - - - - ); -} diff --git a/count-many.ts b/count-many.ts new file mode 100644 index 0000000..d97c39a --- /dev/null +++ b/count-many.ts @@ -0,0 +1,4 @@ +export default interface CountMany { + name: string; + total: number; +} diff --git a/drawer-param-list.ts b/drawer-param-list.ts index e590f4c..8360cb2 100644 --- a/drawer-param-list.ts +++ b/drawer-param-list.ts @@ -4,4 +4,5 @@ export type DrawerParamList = { Best: {}; Plans: {}; Workouts: {}; + Session: {}; }; diff --git a/home-page-params.ts b/home-page-params.ts index d0eb7f8..50bcefe 100644 --- a/home-page-params.ts +++ b/home-page-params.ts @@ -4,7 +4,5 @@ export type HomePageParams = { Sets: {}; EditSet: { set: Set; - workouts: string[]; - count: number; }; }; diff --git a/massive.db b/massive.db new file mode 100644 index 0000000..e69de29 diff --git a/session-page-params.ts b/session-page-params.ts new file mode 100644 index 0000000..55b6e26 --- /dev/null +++ b/session-page-params.ts @@ -0,0 +1,10 @@ +import {Plan} from './plan'; +import Set from './set'; + +export type SessionPageParams = { + SessionList: {}; + StartSession: { + plan: Plan; + set: Set; + }; +}; diff --git a/set.service.ts b/set.service.ts index 7d5c6f0..2114e08 100644 --- a/set.service.ts +++ b/set.service.ts @@ -1,3 +1,4 @@ +import CountMany from './count-many'; import {db} from './db'; import Set from './set'; @@ -154,6 +155,17 @@ export const countToday = async (name: string): Promise => { return Number(result.rows.item(0)?.total); }; +export const countManyToday = async (): Promise => { + const select = ` + SELECT COUNT(*) as total, name FROM sets + WHERE created LIKE strftime('%Y-%m-%d%%', 'now', 'localtime') + AND NOT hidden + GROUP BY name + `; + const [result] = await db.executeSql(select); + return result.rows.raw(); +}; + export const getDistinctSets = async ({ search, limit, diff --git a/settings.ts b/settings.ts index aa5ad3d..5f81959 100644 --- a/settings.ts +++ b/settings.ts @@ -1,13 +1,11 @@ export default interface Settings { alarm: number; vibrate: number; - newSet?: 'predict' | 'empty'; sound?: string; notify?: number; images?: number; showUnit?: number; color?: string; - workouts: number; nextAlarm?: string; steps?: number; date?: string; diff --git a/use-settings.ts b/use-settings.ts index 9d1fb6a..b6a8a83 100644 --- a/use-settings.ts +++ b/use-settings.ts @@ -8,7 +8,7 @@ export const SettingsContext = React.createContext<{ settings: { alarm: 0, vibrate: 1, - workouts: 0, + showDate: 0, }, setSettings: () => null, });