Use deno fmt instead of prettier

This commit is contained in:
Brandon Presley 2023-06-27 15:16:59 +12:00
parent 23ed95dcdb
commit 4303fe2cc4
67 changed files with 897 additions and 820 deletions

View File

@ -1,8 +0,0 @@
module.exports = {
arrowParens: 'avoid',
bracketSameLine: true,
bracketSpacing: false,
singleQuote: true,
trailingComma: 'all',
semi: false,
};

37
App.tsx
View File

@ -3,8 +3,8 @@ import {
DefaultTheme as NavigationDefaultTheme, DefaultTheme as NavigationDefaultTheme,
NavigationContainer, NavigationContainer,
} from '@react-navigation/native' } from '@react-navigation/native'
import React, {useEffect, useMemo, useState} from 'react' import React, { useEffect, useMemo, useState } from 'react'
import {DeviceEventEmitter, useColorScheme} from 'react-native' import { DeviceEventEmitter, useColorScheme } from 'react-native'
import { import {
DarkTheme as PaperDarkTheme, DarkTheme as PaperDarkTheme,
DefaultTheme as PaperDefaultTheme, DefaultTheme as PaperDefaultTheme,
@ -12,11 +12,11 @@ import {
Snackbar, Snackbar,
} from 'react-native-paper' } from 'react-native-paper'
import MaterialIcon from 'react-native-vector-icons/MaterialIcons' import MaterialIcon from 'react-native-vector-icons/MaterialIcons'
import {AppDataSource} from './data-source' import { AppDataSource } from './data-source'
import {settingsRepo} from './db' import { settingsRepo } from './db'
import Routes from './Routes' import Routes from './Routes'
import {TOAST} from './toast' import { TOAST } from './toast'
import {ThemeContext} from './use-theme' import { ThemeContext } from './use-theme'
export const CombinedDefaultTheme = { export const CombinedDefaultTheme = {
...NavigationDefaultTheme, ...NavigationDefaultTheme,
@ -53,7 +53,7 @@ const App = () => {
useEffect(() => { useEffect(() => {
;(async () => { ;(async () => {
if (!AppDataSource.isInitialized) await AppDataSource.initialize() if (!AppDataSource.isInitialized) await AppDataSource.initialize()
const settings = await settingsRepo.findOne({where: {}}) const settings = await settingsRepo.findOne({ where: {} })
setTheme(settings.theme) setTheme(settings.theme)
if (settings.lightColor) setLightColor(settings.lightColor) if (settings.lightColor) setLightColor(settings.lightColor)
if (settings.darkColor) setDarkColor(settings.darkColor) if (settings.darkColor) setDarkColor(settings.darkColor)
@ -61,7 +61,7 @@ const App = () => {
})() })()
const description = DeviceEventEmitter.addListener( const description = DeviceEventEmitter.addListener(
TOAST, TOAST,
({value}: {value: string}) => { ({ value }: { value: string }) => {
setSnackbar(value) setSnackbar(value)
}, },
) )
@ -71,15 +71,15 @@ const App = () => {
const paperTheme = useMemo(() => { const paperTheme = useMemo(() => {
const darkTheme = lightColor const darkTheme = lightColor
? { ? {
...CombinedDarkTheme, ...CombinedDarkTheme,
colors: {...CombinedDarkTheme.colors, primary: darkColor}, colors: { ...CombinedDarkTheme.colors, primary: darkColor },
} }
: CombinedDarkTheme : CombinedDarkTheme
const lightTheme = lightColor const lightTheme = lightColor
? { ? {
...CombinedDefaultTheme, ...CombinedDefaultTheme,
colors: {...CombinedDefaultTheme.colors, primary: lightColor}, colors: { ...CombinedDefaultTheme.colors, primary: lightColor },
} }
: CombinedDefaultTheme : CombinedDefaultTheme
let value = isDark ? darkTheme : lightTheme let value = isDark ? darkTheme : lightTheme
if (theme === 'dark') value = darkTheme if (theme === 'dark') value = darkTheme
@ -99,7 +99,8 @@ const App = () => {
return ( return (
<PaperProvider <PaperProvider
theme={paperTheme} theme={paperTheme}
settings={{icon: props => <MaterialIcon {...props} />}}> settings={{ icon: (props) => <MaterialIcon {...props} /> }}
>
<NavigationContainer theme={paperTheme}> <NavigationContainer theme={paperTheme}>
{initialized && ( {initialized && (
<ThemeContext.Provider <ThemeContext.Provider
@ -110,7 +111,8 @@ const App = () => {
setLightColor, setLightColor,
darkColor, darkColor,
setDarkColor, setDarkColor,
}}> }}
>
<Routes /> <Routes />
</ThemeContext.Provider> </ThemeContext.Provider>
)} )}
@ -120,7 +122,8 @@ const App = () => {
duration={3000} duration={3000}
onDismiss={() => setSnackbar('')} onDismiss={() => setSnackbar('')}
visible={!!snackbar} visible={!!snackbar}
action={action}> action={action}
>
{snackbar} {snackbar}
</Snackbar> </Snackbar>
</PaperProvider> </PaperProvider>

View File

@ -1,14 +1,14 @@
import {ComponentProps, useMemo} from 'react' import { ComponentProps, useMemo } from 'react'
import {FAB, useTheme} from 'react-native-paper' import { FAB, useTheme } from 'react-native-paper'
import {CombinedDarkTheme, CombinedDefaultTheme} from './App' import { CombinedDarkTheme, CombinedDefaultTheme } from './App'
import {lightColors} from './colors' import { lightColors } from './colors'
export default function AppFab(props: Partial<ComponentProps<typeof FAB>>) { export default function AppFab(props: Partial<ComponentProps<typeof FAB>>) {
const {colors} = useTheme() const { colors } = useTheme()
const fabColor = useMemo( const fabColor = useMemo(
() => () =>
lightColors.map(color => color.hex).includes(colors.primary) lightColors.map((color) => color.hex).includes(colors.primary)
? CombinedDarkTheme.colors.background ? CombinedDarkTheme.colors.background
: CombinedDefaultTheme.colors.background, : CombinedDefaultTheme.colors.background,
[colors.primary], [colors.primary],
@ -16,8 +16,8 @@ export default function AppFab(props: Partial<ComponentProps<typeof FAB>>) {
return ( return (
<FAB <FAB
icon="add" icon='add'
testID="add" testID='add'
color={fabColor} color={fabColor}
style={{ style={{
position: 'absolute', position: 'absolute',

View File

@ -1,7 +1,7 @@
import React, {ComponentProps, Ref} from 'react' import React, { ComponentProps, Ref } from 'react'
import {TextInput} from 'react-native-paper' import { TextInput } from 'react-native-paper'
import {CombinedDefaultTheme} from './App' import { CombinedDefaultTheme } from './App'
import {MARGIN} from './constants' import { MARGIN } from './constants'
import useDark from './use-dark' import useDark from './use-dark'
function AppInput( function AppInput(
@ -14,7 +14,7 @@ function AppInput(
return ( return (
<TextInput <TextInput
selectionColor={dark ? '#2A2A2A' : CombinedDefaultTheme.colors.border} selectionColor={dark ? '#2A2A2A' : CombinedDefaultTheme.colors.border}
style={{marginBottom: MARGIN, minWidth: 100}} style={{ marginBottom: MARGIN, minWidth: 100 }}
selectTextOnFocus selectTextOnFocus
ref={props.innerRef} ref={props.innerRef}
blurOnSubmit={false} blurOnSubmit={false}

View File

@ -3,11 +3,11 @@ import {
useFocusEffect, useFocusEffect,
useNavigation, useNavigation,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useState} from 'react' import { useCallback, useState } from 'react'
import {FlatList, Image} from 'react-native' import { FlatList, Image } from 'react-native'
import {List} from 'react-native-paper' import { List } from 'react-native-paper'
import {BestPageParams} from './BestPage' import { BestPageParams } from './BestPage'
import {setRepo, settingsRepo} from './db' import { setRepo, settingsRepo } from './db'
import DrawerHeader from './DrawerHeader' import DrawerHeader from './DrawerHeader'
import GymSet from './gym-set' import GymSet from './gym-set'
import Page from './Page' import Page from './Page'
@ -21,7 +21,7 @@ export default function BestList() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
}, []), }, []),
) )
@ -30,19 +30,19 @@ export default function BestList() {
.createQueryBuilder() .createQueryBuilder()
.select() .select()
.addSelect('MAX(weight)', 'weight') .addSelect('MAX(weight)', 'weight')
.where('name LIKE :name', {name: `%${value.trim()}%`}) .where('name LIKE :name', { name: `%${value.trim()}%` })
.andWhere('NOT hidden') .andWhere('NOT hidden')
.groupBy('name') .groupBy('name')
.getMany() .getMany()
console.log(`${BestList.name}.refresh:`, {length: weights.length}) console.log(`${BestList.name}.refresh:`, { length: weights.length })
let newBest: GymSet[] = [] let newBest: GymSet[] = []
for (const set of weights) { for (const set of weights) {
const reps = await setRepo const reps = await setRepo
.createQueryBuilder() .createQueryBuilder()
.select() .select()
.addSelect('MAX(reps)', 'reps') .addSelect('MAX(reps)', 'reps')
.where('name = :name', {name: set.name}) .where('name = :name', { name: set.name })
.andWhere('weight = :weight', {weight: set.weight}) .andWhere('weight = :weight', { weight: set.weight })
.andWhere('NOT hidden') .andWhere('NOT hidden')
.groupBy('name') .groupBy('name')
.getMany() .getMany()
@ -65,33 +65,41 @@ export default function BestList() {
[refresh], [refresh],
) )
const renderItem = ({item}: {item: GymSet}) => ( const renderItem = ({ item }: { item: GymSet }) => (
<List.Item <List.Item
key={item.name} key={item.name}
title={item.name} title={item.name}
description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`} description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`}
onPress={() => navigation.navigate('ViewBest', {best: item})} onPress={() => navigation.navigate('ViewBest', { best: item })}
left={() => left={() =>
(settings.images && item.image && ( (settings.images && item.image && (
<Image source={{uri: item.image}} style={{height: 75, width: 75}} /> <Image
source={{ uri: item.image }}
style={{ height: 75, width: 75 }}
/>
)) || )) ||
null null}
}
/> />
) )
return ( return (
<> <>
<DrawerHeader name="Best" /> <DrawerHeader name='Best' />
<Page term={term} search={search}> <Page term={term} search={search}>
{bests?.length === 0 ? ( {bests?.length === 0
<List.Item ? (
title="No exercises yet" <List.Item
description="Once sets have been added, this will highlight your personal bests." title='No exercises yet'
/> description='Once sets have been added, this will highlight your personal bests.'
) : ( />
<FlatList style={{flex: 1}} renderItem={renderItem} data={bests} /> )
)} : (
<FlatList
style={{ flex: 1 }}
renderItem={renderItem}
data={bests}
/>
)}
</Page> </Page>
</> </>
) )

View File

@ -1,4 +1,4 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import BestList from './BestList' import BestList from './BestList'
import GymSet from './gym-set' import GymSet from './gym-set'
import ViewBest from './ViewBest' import ViewBest from './ViewBest'
@ -14,9 +14,10 @@ export type BestPageParams = {
export default function BestPage() { export default function BestPage() {
return ( return (
<Stack.Navigator <Stack.Navigator
screenOptions={{headerShown: false, animationEnabled: false}}> screenOptions={{ headerShown: false, animationEnabled: false }}
<Stack.Screen name="BestList" component={BestList} /> >
<Stack.Screen name="ViewBest" component={ViewBest} /> <Stack.Screen name='BestList' component={BestList} />
<Stack.Screen name='ViewBest' component={ViewBest} />
</Stack.Navigator> </Stack.Navigator>
) )
} }

View File

@ -1,9 +1,9 @@
import {useTheme} from '@react-navigation/native' import { useTheme } from '@react-navigation/native'
import * as shape from 'd3-shape' import * as shape from 'd3-shape'
import {View} from 'react-native' import { View } from 'react-native'
import {Grid, LineChart, XAxis, YAxis} from 'react-native-svg-charts' import { Grid, LineChart, XAxis, YAxis } from 'react-native-svg-charts'
import {CombinedDarkTheme, CombinedDefaultTheme} from './App' import { CombinedDarkTheme, CombinedDefaultTheme } from './App'
import {MARGIN, PADDING} from './constants' import { MARGIN, PADDING } from './constants'
import GymSet from './gym-set' import GymSet from './gym-set'
import useDark from './use-dark' import useDark from './use-dark'
@ -18,7 +18,7 @@ export default function Chart({
xFormat: (value: any, index: number) => string xFormat: (value: any, index: number) => string
yFormat: (value: any) => string yFormat: (value: any) => string
}) { }) {
const {colors} = useTheme() const { colors } = useTheme()
const dark = useDark() const dark = useDark()
const axesSvg = { const axesSvg = {
fontSize: 10, fontSize: 10,
@ -26,7 +26,7 @@ export default function Chart({
? CombinedDarkTheme.colors.text ? CombinedDarkTheme.colors.text
: CombinedDefaultTheme.colors.text, : CombinedDefaultTheme.colors.text,
} }
const verticalContentInset = {top: 10, bottom: 10} const verticalContentInset = { top: 10, bottom: 10 }
const xAxisHeight = 30 const xAxisHeight = 30
return ( return (
@ -36,29 +36,31 @@ export default function Chart({
height: 300, height: 300,
padding: PADDING, padding: PADDING,
flexDirection: 'row', flexDirection: 'row',
}}> }}
>
<YAxis <YAxis
data={yData} data={yData}
style={{marginBottom: xAxisHeight}} style={{ marginBottom: xAxisHeight }}
contentInset={verticalContentInset} contentInset={verticalContentInset}
svg={axesSvg} svg={axesSvg}
formatLabel={yFormat} formatLabel={yFormat}
/> />
<View style={{flex: 1, marginLeft: MARGIN}}> <View style={{ flex: 1, marginLeft: MARGIN }}>
<LineChart <LineChart
style={{flex: 1}} style={{ flex: 1 }}
data={yData} data={yData}
contentInset={verticalContentInset} contentInset={verticalContentInset}
curve={shape.curveBasis} curve={shape.curveBasis}
svg={{ svg={{
stroke: colors.primary, stroke: colors.primary,
}}> }}
>
<Grid /> <Grid />
</LineChart> </LineChart>
<XAxis <XAxis
data={xData} data={xData}
formatLabel={xFormat} formatLabel={xFormat}
contentInset={{left: 15, right: 16}} contentInset={{ left: 15, right: 16 }}
svg={axesSvg} svg={axesSvg}
/> />
</View> </View>

View File

@ -1,4 +1,4 @@
import {Button, Dialog, Portal, Text} from 'react-native-paper' import { Button, Dialog, Portal, Text } from 'react-native-paper'
export default function ConfirmDialog({ export default function ConfirmDialog({
title, title,

View File

@ -1,7 +1,7 @@
import {DrawerNavigationProp} from '@react-navigation/drawer' import { DrawerNavigationProp } from '@react-navigation/drawer'
import {useNavigation} from '@react-navigation/native' import { useNavigation } from '@react-navigation/native'
import {Appbar, IconButton} from 'react-native-paper' import { Appbar, IconButton } from 'react-native-paper'
import {DrawerParamList} from './drawer-param-list' import { DrawerParamList } from './drawer-param-list'
import useDark from './use-dark' import useDark from './use-dark'
export default function DrawerHeader({ export default function DrawerHeader({
@ -18,7 +18,7 @@ export default function DrawerHeader({
<Appbar.Header> <Appbar.Header>
<IconButton <IconButton
color={dark ? 'white' : 'white'} color={dark ? 'white' : 'white'}
icon="menu" icon='menu'
onPress={navigation.openDrawer} onPress={navigation.openDrawer}
/> />
<Appbar.Content title={name} /> <Appbar.Content title={name} />

View File

@ -4,22 +4,22 @@ import {
useNavigation, useNavigation,
useRoute, useRoute,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useEffect, useState} from 'react' import { useCallback, useEffect, useState } from 'react'
import {ScrollView, StyleSheet, View} from 'react-native' import { ScrollView, StyleSheet, View } from 'react-native'
import {Button, IconButton, Text} from 'react-native-paper' import { Button, IconButton, Text } from 'react-native-paper'
import {getLast} from './best.service' import { getLast } from './best.service'
import {MARGIN, PADDING} from './constants' import { MARGIN, PADDING } from './constants'
import {planRepo, setRepo} from './db' import { planRepo, setRepo } from './db'
import {defaultSet} from './gym-set' import { defaultSet } from './gym-set'
import {PlanPageParams} from './plan-page-params' import { PlanPageParams } from './plan-page-params'
import StackHeader from './StackHeader' import StackHeader from './StackHeader'
import Switch from './Switch' import Switch from './Switch'
import {DAYS} from './time' import { DAYS } from './time'
import useDark from './use-dark' import useDark from './use-dark'
export default function EditPlan() { export default function EditPlan() {
const {params} = useRoute<RouteProp<PlanPageParams, 'EditPlan'>>() const { params } = useRoute<RouteProp<PlanPageParams, 'EditPlan'>>()
const {plan} = params const { plan } = params
const [days, setDays] = useState<string[]>( const [days, setDays] = useState<string[]>(
plan.days ? plan.days.split(',') : [], plan.days ? plan.days.split(',') : [],
) )
@ -37,18 +37,18 @@ export default function EditPlan() {
.distinct(true) .distinct(true)
.orderBy('name') .orderBy('name')
.getRawMany() .getRawMany()
.then(values => { .then((values) => {
console.log(EditPlan.name, {values}) console.log(EditPlan.name, { values })
setNames(values.map(value => value.name)) setNames(values.map((value) => value.name))
}) })
}, []) }, [])
const save = useCallback(async () => { const save = useCallback(async () => {
console.log(`${EditPlan.name}.save`, {days, workouts, plan}) console.log(`${EditPlan.name}.save`, { days, workouts, plan })
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(',')
await planRepo.save({days: newDays, workouts: newWorkouts, id: plan.id}) await planRepo.save({ days: newDays, workouts: newWorkouts, id: plan.id })
navigation.goBack() navigation.goBack()
}, [days, workouts, plan, navigation]) }, [days, workouts, plan, navigation])
@ -57,7 +57,7 @@ export default function EditPlan() {
if (on) { if (on) {
setWorkouts([...workouts, name]) setWorkouts([...workouts, name])
} else { } else {
setWorkouts(workouts.filter(workout => workout !== name)) setWorkouts(workouts.filter((workout) => workout !== name))
} }
}, },
[setWorkouts, workouts], [setWorkouts, workouts],
@ -68,7 +68,7 @@ export default function EditPlan() {
if (on) { if (on) {
setDays([...days, day]) setDays([...days, day])
} else { } else {
setDays(days.filter(d => d !== day)) setDays(days.filter((d) => d !== day))
} }
}, },
[setDays, days], [setDays, days],
@ -77,52 +77,56 @@ export default function EditPlan() {
return ( return (
<> <>
<StackHeader <StackHeader
title={typeof plan.id === 'number' ? 'Edit plan' : 'Add plan'}> title={typeof plan.id === 'number' ? 'Edit plan' : 'Add plan'}
>
<IconButton <IconButton
color={dark ? 'white' : 'white'} color={dark ? 'white' : 'white'}
onPress={async () => { onPress={async () => {
let first = await getLast(workouts[0]) let first = await getLast(workouts[0])
if (!first) first = {...defaultSet, name: workouts[0]} if (!first) first = { ...defaultSet, name: workouts[0] }
delete first.id delete first.id
navigation.navigate('StartPlan', {plan: params.plan, first}) navigation.navigate('StartPlan', { plan: params.plan, first })
}} }}
icon="play-arrow" icon='play-arrow'
/> />
</StackHeader> </StackHeader>
<View style={{padding: PADDING, flex: 1}}> <View style={{ padding: PADDING, flex: 1 }}>
<ScrollView style={{flex: 1}}> <ScrollView style={{ flex: 1 }}>
<Text style={styles.title}>Days</Text> <Text style={styles.title}>Days</Text>
{DAYS.map(day => ( {DAYS.map((day) => (
<Switch <Switch
key={day} key={day}
onChange={value => toggleDay(value, day)} onChange={(value) => toggleDay(value, day)}
value={days.includes(day)} value={days.includes(day)}
title={day} title={day}
/> />
))} ))}
<Text style={[styles.title, {marginTop: MARGIN}]}>Workouts</Text> <Text style={[styles.title, { marginTop: MARGIN }]}>Workouts</Text>
{names.length === 0 ? ( {names.length === 0
<View> ? (
<Text>No workouts found.</Text> <View>
</View> <Text>No workouts found.</Text>
) : ( </View>
names.map(name => ( )
<Switch : (
key={name} names.map((name) => (
onChange={value => toggleWorkout(value, name)} <Switch
value={workouts.includes(name)} key={name}
title={name} onChange={(value) => toggleWorkout(value, name)}
/> value={workouts.includes(name)}
)) title={name}
)} />
))
)}
</ScrollView> </ScrollView>
<Button <Button
disabled={workouts.length === 0 && days.length === 0} disabled={workouts.length === 0 && days.length === 0}
style={styles.button} style={styles.button}
mode="contained" mode='contained'
icon="save" icon='save'
onPress={save}> onPress={save}
>
Save Save
</Button> </Button>
</View> </View>

View File

@ -1,28 +1,28 @@
import {DateTimePickerAndroid} from '@react-native-community/datetimepicker' import { DateTimePickerAndroid } from '@react-native-community/datetimepicker'
import { import {
RouteProp, RouteProp,
useFocusEffect, useFocusEffect,
useNavigation, useNavigation,
useRoute, useRoute,
} from '@react-navigation/native' } from '@react-navigation/native'
import {format} from 'date-fns' import { format } from 'date-fns'
import {useCallback, useRef, useState} from 'react' import { useCallback, useRef, useState } from 'react'
import {NativeModules, TextInput, View} from 'react-native' import { NativeModules, TextInput, View } from 'react-native'
import DocumentPicker from 'react-native-document-picker' import DocumentPicker from 'react-native-document-picker'
import {Button, Card, TouchableRipple} from 'react-native-paper' import { Button, Card, TouchableRipple } from 'react-native-paper'
import AppInput from './AppInput' import AppInput from './AppInput'
import ConfirmDialog from './ConfirmDialog' import ConfirmDialog from './ConfirmDialog'
import {MARGIN, PADDING} from './constants' import { MARGIN, PADDING } from './constants'
import {getNow, setRepo, settingsRepo} from './db' import { getNow, setRepo, settingsRepo } from './db'
import GymSet from './gym-set' import GymSet from './gym-set'
import {HomePageParams} from './home-page-params' import { HomePageParams } from './home-page-params'
import Settings from './settings' import Settings from './settings'
import StackHeader from './StackHeader' import StackHeader from './StackHeader'
import {toast} from './toast' import { toast } from './toast'
export default function EditSet() { export default function EditSet() {
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>() const { params } = useRoute<RouteProp<HomePageParams, 'EditSet'>>()
const {set} = params const { set } = params
const navigation = useNavigation() const navigation = useNavigation()
const [settings, setSettings] = useState<Settings>({} as Settings) const [settings, setSettings] = useState<Settings>({} as Settings)
const [name, setName] = useState(set.name) const [name, setName] = useState(set.name)
@ -46,16 +46,16 @@ export default function EditSet() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
}, []), }, []),
) )
const startTimer = useCallback( const startTimer = useCallback(
async (value: string) => { async (value: string) => {
if (!settings.alarm) return if (!settings.alarm) return
const first = await setRepo.findOne({where: {name: value}}) const first = await setRepo.findOne({ where: { name: value } })
const milliseconds = const milliseconds = (first?.minutes ?? 3) * 60 * 1000 +
(first?.minutes ?? 3) * 60 * 1000 + (first?.seconds ?? 0) * 1000 (first?.seconds ?? 0) * 1000
if (milliseconds) NativeModules.AlarmModule.timer(milliseconds) if (milliseconds) NativeModules.AlarmModule.timer(milliseconds)
}, },
[settings], [settings],
@ -64,25 +64,27 @@ export default function EditSet() {
const added = useCallback( const added = useCallback(
async (value: GymSet) => { async (value: GymSet) => {
startTimer(value.name) startTimer(value.name)
console.log(`${EditSet.name}.add`, {set: value}) console.log(`${EditSet.name}.add`, { set: value })
if (!settings.notify) return if (!settings.notify) return
if ( if (
value.weight > set.weight || value.weight > set.weight ||
(value.reps > set.reps && value.weight === set.weight) (value.reps > set.reps && value.weight === set.weight)
) ) {
toast("Great work King! That's a new record.") toast('Great work King! That\'s a new record.')
}
}, },
[startTimer, set, settings], [startTimer, set, settings],
) )
const handleSubmit = async () => { const handleSubmit = async () => {
console.log(`${EditSet.name}.handleSubmit:`, {set, uri: newImage, name}) console.log(`${EditSet.name}.handleSubmit:`, { set, uri: newImage, name })
if (!name) return if (!name) return
let image = newImage let image = newImage
if (!newImage && !removeImage) if (!newImage && !removeImage) {
image = await setRepo.findOne({where: {name}}).then(s => s?.image) image = await setRepo.findOne({ where: { name } }).then((s) => s?.image)
}
console.log(`${EditSet.name}.handleSubmit:`, {image}) console.log(`${EditSet.name}.handleSubmit:`, { image })
const now = await getNow() const now = await getNow()
const saved = await setRepo.save({ const saved = await setRepo.save({
id: set.id, id: set.id,
@ -102,7 +104,7 @@ export default function EditSet() {
} }
const changeImage = useCallback(async () => { const changeImage = useCallback(async () => {
const {fileCopyUri} = await DocumentPicker.pickSingle({ const { fileCopyUri } = await DocumentPicker.pickSingle({
type: DocumentPicker.types.images, type: DocumentPicker.types.images,
copyTo: 'documentDirectory', copyTo: 'documentDirectory',
}) })
@ -137,9 +139,9 @@ export default function EditSet() {
title={typeof set.id === 'number' ? 'Edit set' : 'Add set'} title={typeof set.id === 'number' ? 'Edit set' : 'Add set'}
/> />
<View style={{padding: PADDING, flex: 1}}> <View style={{ padding: PADDING, flex: 1 }}>
<AppInput <AppInput
label="Name" label='Name'
value={name} value={name}
onChangeText={setName} onChangeText={setName}
autoCorrect={false} autoCorrect={false}
@ -148,20 +150,20 @@ export default function EditSet() {
/> />
<AppInput <AppInput
label="Reps" label='Reps'
keyboardType="numeric" keyboardType='numeric'
value={reps} value={reps}
onChangeText={setReps} onChangeText={setReps}
onSubmitEditing={() => weightRef.current?.focus()} onSubmitEditing={() => weightRef.current?.focus()}
selection={selection} selection={selection}
onSelectionChange={e => setSelection(e.nativeEvent.selection)} onSelectionChange={(e) => setSelection(e.nativeEvent.selection)}
autoFocus={!!name} autoFocus={!!name}
innerRef={repsRef} innerRef={repsRef}
/> />
<AppInput <AppInput
label="Weight" label='Weight'
keyboardType="numeric" keyboardType='numeric'
value={weight} value={weight}
onChangeText={setWeight} onChangeText={setWeight}
onSubmitEditing={handleSubmit} onSubmitEditing={handleSubmit}
@ -170,8 +172,8 @@ export default function EditSet() {
{settings.showUnit && ( {settings.showUnit && (
<AppInput <AppInput
autoCapitalize="none" autoCapitalize='none'
label="Unit" label='Unit'
value={unit} value={unit}
onChangeText={setUnit} onChangeText={setUnit}
innerRef={unitRef} innerRef={unitRef}
@ -180,7 +182,7 @@ export default function EditSet() {
{typeof set.id === 'number' && settings.showDate && ( {typeof set.id === 'number' && settings.showDate && (
<AppInput <AppInput
label="Created" label='Created'
value={format(created, settings.date || 'P')} value={format(created, settings.date || 'P')}
onPressOut={pickDate} onPressOut={pickDate}
/> />
@ -188,18 +190,20 @@ export default function EditSet() {
{settings.images && newImage && ( {settings.images && newImage && (
<TouchableRipple <TouchableRipple
style={{marginBottom: MARGIN}} style={{ marginBottom: MARGIN }}
onPress={changeImage} onPress={changeImage}
onLongPress={() => setShowRemove(true)}> onLongPress={() => setShowRemove(true)}
<Card.Cover source={{uri: newImage}} /> >
<Card.Cover source={{ uri: newImage }} />
</TouchableRipple> </TouchableRipple>
)} )}
{settings.images && !newImage && ( {settings.images && !newImage && (
<Button <Button
style={{marginBottom: MARGIN}} style={{ marginBottom: MARGIN }}
onPress={changeImage} onPress={changeImage}
icon="add-photo-alternate"> icon='add-photo-alternate'
>
Image Image
</Button> </Button>
)} )}
@ -207,18 +211,20 @@ export default function EditSet() {
<Button <Button
disabled={!name} disabled={!name}
mode="contained" mode='contained'
icon="save" icon='save'
style={{margin: MARGIN}} style={{ margin: MARGIN }}
onPress={handleSubmit}> onPress={handleSubmit}
>
Save Save
</Button> </Button>
<ConfirmDialog <ConfirmDialog
title="Remove image" title='Remove image'
onOk={handleRemove} onOk={handleRemove}
show={showRemove} show={showRemove}
setShow={setShowRemove}> setShow={setShowRemove}
>
Are you sure you want to remove the image? Are you sure you want to remove the image?
</ConfirmDialog> </ConfirmDialog>
</> </>

View File

@ -4,23 +4,23 @@ import {
useNavigation, useNavigation,
useRoute, useRoute,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useState} from 'react' import { useCallback, useState } from 'react'
import {View} from 'react-native' import { View } from 'react-native'
import DocumentPicker from 'react-native-document-picker' import DocumentPicker from 'react-native-document-picker'
import {Button, Card, TouchableRipple} from 'react-native-paper' import { Button, Card, TouchableRipple } from 'react-native-paper'
import {In} from 'typeorm' import { In } from 'typeorm'
import AppInput from './AppInput' import AppInput from './AppInput'
import ConfirmDialog from './ConfirmDialog' import ConfirmDialog from './ConfirmDialog'
import {MARGIN, PADDING} from './constants' import { MARGIN, PADDING } from './constants'
import {setRepo, settingsRepo} from './db' import { setRepo, settingsRepo } from './db'
import GymSet from './gym-set' import GymSet from './gym-set'
import {HomePageParams} from './home-page-params' import { HomePageParams } from './home-page-params'
import Settings from './settings' import Settings from './settings'
import StackHeader from './StackHeader' import StackHeader from './StackHeader'
export default function EditSets() { export default function EditSets() {
const {params} = useRoute<RouteProp<HomePageParams, 'EditSets'>>() const { params } = useRoute<RouteProp<HomePageParams, 'EditSets'>>()
const {ids} = params const { ids } = params
const navigation = useNavigation() const navigation = useNavigation()
const [settings, setSettings] = useState<Settings>({} as Settings) const [settings, setSettings] = useState<Settings>({} as Settings)
const [name, setName] = useState('') const [name, setName] = useState('')
@ -41,18 +41,18 @@ export default function EditSets() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
setRepo.find({where: {id: In(ids)}}).then(sets => { setRepo.find({ where: { id: In(ids) } }).then((sets) => {
setNames(sets.map(set => set.name).join(', ')) setNames(sets.map((set) => set.name).join(', '))
setOldReps(sets.map(set => set.reps).join(', ')) setOldReps(sets.map((set) => set.reps).join(', '))
setWeights(sets.map(set => set.weight).join(', ')) setWeights(sets.map((set) => set.weight).join(', '))
setUnits(sets.map(set => set.unit).join(', ')) setUnits(sets.map((set) => set.unit).join(', '))
}) })
}, [ids]), }, [ids]),
) )
const handleSubmit = async () => { const handleSubmit = async () => {
console.log(`${EditSets.name}.handleSubmit:`, {uri: newImage, name}) console.log(`${EditSets.name}.handleSubmit:`, { uri: newImage, name })
const update: Partial<GymSet> = {} const update: Partial<GymSet> = {}
if (name) update.name = name if (name) update.name = name
if (reps) update.reps = Number(reps) if (reps) update.reps = Number(reps)
@ -64,7 +64,7 @@ export default function EditSets() {
} }
const changeImage = useCallback(async () => { const changeImage = useCallback(async () => {
const {fileCopyUri} = await DocumentPicker.pickSingle({ const { fileCopyUri } = await DocumentPicker.pickSingle({
type: DocumentPicker.types.images, type: DocumentPicker.types.images,
copyTo: 'documentDirectory', copyTo: 'documentDirectory',
}) })
@ -80,7 +80,7 @@ export default function EditSets() {
<> <>
<StackHeader title={`Edit ${ids.length} sets`} /> <StackHeader title={`Edit ${ids.length} sets`} />
<View style={{padding: PADDING, flex: 1}}> <View style={{ padding: PADDING, flex: 1 }}>
<AppInput <AppInput
label={`Names: ${names}`} label={`Names: ${names}`}
value={name} value={name}
@ -91,17 +91,17 @@ export default function EditSets() {
<AppInput <AppInput
label={`Reps: ${oldReps}`} label={`Reps: ${oldReps}`}
keyboardType="numeric" keyboardType='numeric'
value={reps} value={reps}
onChangeText={setReps} onChangeText={setReps}
selection={selection} selection={selection}
onSelectionChange={e => setSelection(e.nativeEvent.selection)} onSelectionChange={(e) => setSelection(e.nativeEvent.selection)}
autoFocus={!!name} autoFocus={!!name}
/> />
<AppInput <AppInput
label={`Weights: ${weights}`} label={`Weights: ${weights}`}
keyboardType="numeric" keyboardType='numeric'
value={weight} value={weight}
onChangeText={setWeight} onChangeText={setWeight}
onSubmitEditing={handleSubmit} onSubmitEditing={handleSubmit}
@ -109,7 +109,7 @@ export default function EditSets() {
{settings.showUnit && ( {settings.showUnit && (
<AppInput <AppInput
autoCapitalize="none" autoCapitalize='none'
label={`Units: ${units}`} label={`Units: ${units}`}
value={unit} value={unit}
onChangeText={setUnit} onChangeText={setUnit}
@ -118,35 +118,39 @@ export default function EditSets() {
{settings.images && newImage && ( {settings.images && newImage && (
<TouchableRipple <TouchableRipple
style={{marginBottom: MARGIN}} style={{ marginBottom: MARGIN }}
onPress={changeImage} onPress={changeImage}
onLongPress={() => setShowRemove(true)}> onLongPress={() => setShowRemove(true)}
<Card.Cover source={{uri: newImage}} /> >
<Card.Cover source={{ uri: newImage }} />
</TouchableRipple> </TouchableRipple>
)} )}
<ConfirmDialog <ConfirmDialog
title="Remove image" title='Remove image'
onOk={handleRemove} onOk={handleRemove}
show={showRemove} show={showRemove}
setShow={setShowRemove}> setShow={setShowRemove}
>
Are you sure you want to remove the image? Are you sure you want to remove the image?
</ConfirmDialog> </ConfirmDialog>
{settings.images && !newImage && ( {settings.images && !newImage && (
<Button <Button
style={{marginBottom: MARGIN}} style={{ marginBottom: MARGIN }}
onPress={changeImage} onPress={changeImage}
icon="add-photo-alternate"> icon='add-photo-alternate'
>
Image Image
</Button> </Button>
)} )}
</View> </View>
<Button <Button
mode="contained" mode='contained'
icon="save" icon='save'
style={{margin: MARGIN}} style={{ margin: MARGIN }}
onPress={handleSubmit}> onPress={handleSubmit}
>
Save Save
</Button> </Button>
</> </>

View File

@ -4,21 +4,21 @@ import {
useNavigation, useNavigation,
useRoute, useRoute,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useRef, useState} from 'react' import { useCallback, useRef, useState } from 'react'
import {ScrollView, TextInput, View} from 'react-native' import { ScrollView, TextInput, View } from 'react-native'
import DocumentPicker from 'react-native-document-picker' import DocumentPicker from 'react-native-document-picker'
import {Button, Card, TouchableRipple} from 'react-native-paper' import { Button, Card, TouchableRipple } from 'react-native-paper'
import AppInput from './AppInput' import AppInput from './AppInput'
import ConfirmDialog from './ConfirmDialog' import ConfirmDialog from './ConfirmDialog'
import {MARGIN, PADDING} from './constants' import { MARGIN, PADDING } from './constants'
import {getNow, planRepo, setRepo, settingsRepo} from './db' import { getNow, planRepo, setRepo, settingsRepo } from './db'
import {defaultSet} from './gym-set' import { defaultSet } from './gym-set'
import Settings from './settings' import Settings from './settings'
import StackHeader from './StackHeader' import StackHeader from './StackHeader'
import {WorkoutsPageParams} from './WorkoutsPage' import { WorkoutsPageParams } from './WorkoutsPage'
export default function EditWorkout() { export default function EditWorkout() {
const {params} = useRoute<RouteProp<WorkoutsPageParams, 'EditWorkout'>>() const { params } = useRoute<RouteProp<WorkoutsPageParams, 'EditWorkout'>>()
const [removeImage, setRemoveImage] = useState(false) const [removeImage, setRemoveImage] = useState(false)
const [showRemove, setShowRemove] = useState(false) const [showRemove, setShowRemove] = useState(false)
const [name, setName] = useState(params.value.name) const [name, setName] = useState(params.value.name)
@ -40,13 +40,13 @@ export default function EditWorkout() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
}, []), }, []),
) )
const update = async () => { const update = async () => {
await setRepo.update( await setRepo.update(
{name: params.value.name}, { name: params.value.name },
{ {
name: name || params.value.name, name: name || params.value.name,
sets: Number(sets), sets: Number(sets),
@ -87,7 +87,7 @@ export default function EditWorkout() {
} }
const changeImage = useCallback(async () => { const changeImage = useCallback(async () => {
const {fileCopyUri} = await DocumentPicker.pickSingle({ const { fileCopyUri } = await DocumentPicker.pickSingle({
type: DocumentPicker.types.images, type: DocumentPicker.types.images,
copyTo: 'documentDirectory', copyTo: 'documentDirectory',
}) })
@ -108,11 +108,11 @@ export default function EditWorkout() {
return ( return (
<> <>
<StackHeader title={params.value.name ? 'Edit workout' : 'Add workout'} /> <StackHeader title={params.value.name ? 'Edit workout' : 'Add workout'} />
<View style={{padding: PADDING, flex: 1}}> <View style={{ padding: PADDING, flex: 1 }}>
<ScrollView style={{flex: 1}}> <ScrollView style={{ flex: 1 }}>
<AppInput <AppInput
autoFocus autoFocus
label="Name" label='Name'
value={name} value={name}
onChangeText={setName} onChangeText={setName}
onSubmitEditing={submitName} onSubmitEditing={submitName}
@ -123,7 +123,7 @@ export default function EditWorkout() {
selectTextOnFocus={false} selectTextOnFocus={false}
value={steps} value={steps}
onChangeText={setSteps} onChangeText={setSteps}
label="Steps" label='Steps'
multiline multiline
onSubmitEditing={() => setsRef.current?.focus()} onSubmitEditing={() => setsRef.current?.focus()}
/> />
@ -132,8 +132,8 @@ export default function EditWorkout() {
innerRef={setsRef} innerRef={setsRef}
value={sets} value={sets}
onChangeText={setSets} onChangeText={setSets}
label="Sets per workout" label='Sets per workout'
keyboardType="numeric" keyboardType='numeric'
onSubmitEditing={() => minutesRef.current?.focus()} onSubmitEditing={() => minutesRef.current?.focus()}
/> />
{settings?.alarm && ( {settings?.alarm && (
@ -143,44 +143,47 @@ export default function EditWorkout() {
onSubmitEditing={() => secondsRef.current?.focus()} onSubmitEditing={() => secondsRef.current?.focus()}
value={minutes} value={minutes}
onChangeText={setMinutes} onChangeText={setMinutes}
label="Rest minutes" label='Rest minutes'
keyboardType="numeric" keyboardType='numeric'
/> />
<AppInput <AppInput
innerRef={secondsRef} innerRef={secondsRef}
value={seconds} value={seconds}
onChangeText={setSeconds} onChangeText={setSeconds}
label="Rest seconds" label='Rest seconds'
keyboardType="numeric" keyboardType='numeric'
blurOnSubmit blurOnSubmit
/> />
</> </>
)} )}
{settings?.images && uri && ( {settings?.images && uri && (
<TouchableRipple <TouchableRipple
style={{marginBottom: MARGIN}} style={{ marginBottom: MARGIN }}
onPress={changeImage} onPress={changeImage}
onLongPress={() => setShowRemove(true)}> onLongPress={() => setShowRemove(true)}
<Card.Cover source={{uri}} /> >
<Card.Cover source={{ uri }} />
</TouchableRipple> </TouchableRipple>
)} )}
{settings?.images && !uri && ( {settings?.images && !uri && (
<Button <Button
style={{marginBottom: MARGIN}} style={{ marginBottom: MARGIN }}
onPress={changeImage} onPress={changeImage}
icon="add-photo-alternate"> icon='add-photo-alternate'
>
Image Image
</Button> </Button>
)} )}
</ScrollView> </ScrollView>
<Button disabled={!name} mode="contained" icon="save" onPress={save}> <Button disabled={!name} mode='contained' icon='save' onPress={save}>
Save Save
</Button> </Button>
<ConfirmDialog <ConfirmDialog
title="Remove image" title='Remove image'
onOk={handleRemove} onOk={handleRemove}
show={showRemove} show={showRemove}
setShow={setShowRemove}> setShow={setShowRemove}
>
Are you sure you want to remove the image? Are you sure you want to remove the image?
</ConfirmDialog> </ConfirmDialog>
</View> </View>

View File

@ -1,7 +1,7 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import EditSet from './EditSet' import EditSet from './EditSet'
import EditSets from './EditSets' import EditSets from './EditSets'
import {HomePageParams} from './home-page-params' import { HomePageParams } from './home-page-params'
import SetList from './SetList' import SetList from './SetList'
const Stack = createStackNavigator<HomePageParams>() const Stack = createStackNavigator<HomePageParams>()
@ -9,10 +9,11 @@ const Stack = createStackNavigator<HomePageParams>()
export default function HomePage() { export default function HomePage() {
return ( return (
<Stack.Navigator <Stack.Navigator
screenOptions={{headerShown: false, animationEnabled: false}}> screenOptions={{ headerShown: false, animationEnabled: false }}
<Stack.Screen name="Sets" component={SetList} /> >
<Stack.Screen name="EditSet" component={EditSet} /> <Stack.Screen name='Sets' component={SetList} />
<Stack.Screen name="EditSets" component={EditSets} /> <Stack.Screen name='EditSet' component={EditSet} />
<Stack.Screen name='EditSets' component={EditSets} />
</Stack.Navigator> </Stack.Navigator>
) )
} }

View File

@ -1,5 +1,5 @@
import {useState} from 'react' import { useState } from 'react'
import {Divider, IconButton, Menu} from 'react-native-paper' import { Divider, IconButton, Menu } from 'react-native-paper'
import ConfirmDialog from './ConfirmDialog' import ConfirmDialog from './ConfirmDialog'
import useDark from './use-dark' import useDark from './use-dark'
@ -55,45 +55,45 @@ export default function ListMenu({
<IconButton <IconButton
color={dark ? 'white' : 'white'} color={dark ? 'white' : 'white'}
onPress={() => setShowMenu(true)} onPress={() => setShowMenu(true)}
icon="more-vert" icon='more-vert'
/> />
}> }
<Menu.Item icon="done-all" title="Select all" onPress={select} /> >
<Menu.Item icon='done-all' title='Select all' onPress={select} />
<Menu.Item <Menu.Item
icon="clear" icon='clear'
title="Clear" title='Clear'
onPress={clear} onPress={clear}
disabled={ids?.length === 0} disabled={ids?.length === 0}
/> />
<Menu.Item <Menu.Item
icon="edit" icon='edit'
title="Edit" title='Edit'
onPress={edit} onPress={edit}
disabled={ids?.length === 0} disabled={ids?.length === 0}
/> />
<Menu.Item <Menu.Item
icon="content-copy" icon='content-copy'
title="Copy" title='Copy'
onPress={copy} onPress={copy}
disabled={ids?.length === 0} disabled={ids?.length === 0}
/> />
<Divider /> <Divider />
<Menu.Item <Menu.Item
icon="delete" icon='delete'
onPress={() => setShowRemove(true)} onPress={() => setShowRemove(true)}
title="Delete" title='Delete'
/> />
<ConfirmDialog <ConfirmDialog
title={ids?.length === 0 ? 'Delete all' : 'Delete selected'} title={ids?.length === 0 ? 'Delete all' : 'Delete selected'}
show={showRemove} show={showRemove}
setShow={setShowRemove} setShow={setShowRemove}
onOk={remove} onOk={remove}
onCancel={() => setShowMenu(false)}> onCancel={() => setShowMenu(false)}
{ids?.length === 0 ? ( >
<>This irreversibly deletes records from the app. Are you sure?</> {ids?.length === 0
) : ( ? <>This irreversibly deletes records from the app. Are you sure?</>
<>This will delete {ids?.length} record(s). Are you sure?</> : <>This will delete {ids?.length} record(s). Are you sure?</>}
)}
</ConfirmDialog> </ConfirmDialog>
</Menu> </Menu>
) )

View File

@ -1,7 +1,7 @@
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
import {Searchbar} from 'react-native-paper' import { Searchbar } from 'react-native-paper'
import AppFab from './AppFab' import AppFab from './AppFab'
import {PADDING} from './constants' import { PADDING } from './constants'
export default function Page({ export default function Page({
onAdd, onAdd,
@ -19,11 +19,11 @@ export default function Page({
return ( return (
<View style={[styles.view, style]}> <View style={[styles.view, style]}>
<Searchbar <Searchbar
placeholder="Search" placeholder='Search'
value={term} value={term}
onChangeText={search} onChangeText={search}
icon="search" icon='search'
clearIcon="clear" clearIcon='clear'
/> />
{children} {children}
{onAdd && <AppFab onPress={onAdd} />} {onAdd && <AppFab onPress={onAdd} />}

View File

@ -3,15 +3,15 @@ import {
useFocusEffect, useFocusEffect,
useNavigation, useNavigation,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useMemo, useState} from 'react' import { useCallback, useMemo, useState } from 'react'
import {Text} from 'react-native' import { Text } from 'react-native'
import {List} from 'react-native-paper' import { List } from 'react-native-paper'
import {getLast} from './best.service' import { getLast } from './best.service'
import {DARK_RIPPLE, LIGHT_RIPPLE} from './constants' import { DARK_RIPPLE, LIGHT_RIPPLE } from './constants'
import {defaultSet} from './gym-set' import { defaultSet } from './gym-set'
import {Plan} from './plan' import { Plan } from './plan'
import {PlanPageParams} from './plan-page-params' import { PlanPageParams } from './plan-page-params'
import {DAYS} from './time' import { DAYS } from './time'
import useDark from './use-dark' import useDark from './use-dark'
export default function PlanItem({ export default function PlanItem({
@ -38,12 +38,13 @@ export default function PlanItem({
const start = useCallback(async () => { const start = useCallback(async () => {
const workout = item.workouts.split(',')[0] const workout = item.workouts.split(',')[0]
let first = await getLast(workout) let first = await getLast(workout)
if (!first) first = {...defaultSet, name: workout} if (!first) first = { ...defaultSet, name: workout }
delete first.id delete first.id
if (ids.length === 0) if (ids.length === 0) {
return navigation.navigate('StartPlan', {plan: item, first}) return navigation.navigate('StartPlan', { plan: item, first })
const removing = ids.find(id => id === item.id) }
if (removing) setIds(ids.filter(id => id !== item.id)) const removing = ids.find((id) => id === item.id)
if (removing) setIds(ids.filter((id) => id !== item.id))
else setIds([...ids, item.id]) else setIds([...ids, item.id])
}, [ids, setIds, item, navigation]) }, [ids, setIds, item, navigation])
@ -56,13 +57,17 @@ export default function PlanItem({
() => () =>
days.map((day, index) => ( days.map((day, index) => (
<Text key={day}> <Text key={day}>
{day === today ? ( {day === today
<Text style={{fontWeight: 'bold', textDecorationLine: 'underline'}}> ? (
{day} <Text
</Text> style={{ fontWeight: 'bold', textDecorationLine: 'underline' }}
) : ( >
day {day}
)} </Text>
)
: (
day
)}
{index === days.length - 1 ? '' : ', '} {index === days.length - 1 ? '' : ', '}
</Text> </Text>
)), )),
@ -86,7 +91,7 @@ export default function PlanItem({
title={title} title={title}
description={description} description={description}
onLongPress={longPress} onLongPress={longPress}
style={{backgroundColor}} style={{ backgroundColor }}
/> />
) )
} }

View File

@ -3,16 +3,16 @@ import {
useFocusEffect, useFocusEffect,
useNavigation, useNavigation,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useState} from 'react' import { useCallback, useState } from 'react'
import {FlatList} from 'react-native' import { FlatList } from 'react-native'
import {List} from 'react-native-paper' import { List } from 'react-native-paper'
import {Like} from 'typeorm' import { Like } from 'typeorm'
import {planRepo} from './db' import { planRepo } from './db'
import DrawerHeader from './DrawerHeader' import DrawerHeader from './DrawerHeader'
import ListMenu from './ListMenu' import ListMenu from './ListMenu'
import Page from './Page' import Page from './Page'
import {Plan} from './plan' import { Plan } from './plan'
import {PlanPageParams} from './plan-page-params' import { PlanPageParams } from './plan-page-params'
import PlanItem from './PlanItem' import PlanItem from './PlanItem'
export default function PlanList() { export default function PlanList() {
@ -25,8 +25,8 @@ export default function PlanList() {
planRepo planRepo
.find({ .find({
where: [ where: [
{days: Like(`%${value.trim()}%`)}, { days: Like(`%${value.trim()}%`) },
{workouts: Like(`%${value.trim()}%`)}, { workouts: Like(`%${value.trim()}%`) },
], ],
}) })
.then(setPlans) .then(setPlans)
@ -47,27 +47,27 @@ export default function PlanList() {
) )
const renderItem = useCallback( const renderItem = useCallback(
({item}: {item: Plan}) => ( ({ item }: { item: Plan }) => (
<PlanItem ids={ids} setIds={setIds} item={item} key={item.id} /> <PlanItem ids={ids} setIds={setIds} item={item} key={item.id} />
), ),
[ids], [ids],
) )
const onAdd = () => const onAdd = () =>
navigation.navigate('EditPlan', {plan: {days: '', workouts: ''}}) navigation.navigate('EditPlan', { plan: { days: '', workouts: '' } })
const edit = useCallback(async () => { const edit = useCallback(async () => {
const plan = await planRepo.findOne({where: {id: ids.pop()}}) const plan = await planRepo.findOne({ where: { id: ids.pop() } })
navigation.navigate('EditPlan', {plan}) navigation.navigate('EditPlan', { plan })
setIds([]) setIds([])
}, [ids, navigation]) }, [ids, navigation])
const copy = useCallback(async () => { const copy = useCallback(async () => {
const plan = await planRepo.findOne({ const plan = await planRepo.findOne({
where: {id: ids.pop()}, where: { id: ids.pop() },
}) })
delete plan.id delete plan.id
navigation.navigate('EditPlan', {plan}) navigation.navigate('EditPlan', { plan })
setIds([]) setIds([])
}, [ids, navigation]) }, [ids, navigation])
@ -82,7 +82,7 @@ export default function PlanList() {
}, [ids, refresh, term]) }, [ids, refresh, term])
const select = useCallback(() => { const select = useCallback(() => {
setIds(plans.map(plan => plan.id)) setIds(plans.map((plan) => plan.id))
}, [plans]) }, [plans])
return ( return (
@ -98,19 +98,21 @@ export default function PlanList() {
/> />
</DrawerHeader> </DrawerHeader>
<Page onAdd={onAdd} term={term} search={search}> <Page onAdd={onAdd} term={term} search={search}>
{plans?.length === 0 ? ( {plans?.length === 0
<List.Item ? (
title="No plans yet" <List.Item
description="A plan is a list of workouts for certain days." title='No plans yet'
/> description='A plan is a list of workouts for certain days.'
) : ( />
<FlatList )
style={{flex: 1}} : (
data={plans} <FlatList
renderItem={renderItem} style={{ flex: 1 }}
keyExtractor={set => set.id?.toString() || ''} data={plans}
/> renderItem={renderItem}
)} keyExtractor={(set) => set.id?.toString() || ''}
/>
)}
</Page> </Page>
</> </>
) )

View File

@ -1,7 +1,7 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import EditPlan from './EditPlan' import EditPlan from './EditPlan'
import EditSet from './EditSet' import EditSet from './EditSet'
import {PlanPageParams} from './plan-page-params' import { PlanPageParams } from './plan-page-params'
import PlanList from './PlanList' import PlanList from './PlanList'
import StartPlan from './StartPlan' import StartPlan from './StartPlan'
@ -10,11 +10,12 @@ const Stack = createStackNavigator<PlanPageParams>()
export default function PlanPage() { export default function PlanPage() {
return ( return (
<Stack.Navigator <Stack.Navigator
screenOptions={{headerShown: false, animationEnabled: false}}> screenOptions={{ headerShown: false, animationEnabled: false }}
<Stack.Screen name="PlanList" component={PlanList} /> >
<Stack.Screen name="EditPlan" component={EditPlan} /> <Stack.Screen name='PlanList' component={PlanList} />
<Stack.Screen name="StartPlan" component={StartPlan} /> <Stack.Screen name='EditPlan' component={EditPlan} />
<Stack.Screen name="EditSet" component={EditSet} /> <Stack.Screen name='StartPlan' component={StartPlan} />
<Stack.Screen name='EditSet' component={EditSet} />
</Stack.Navigator> </Stack.Navigator>
) )
} }

View File

@ -1,7 +1,7 @@
import {createDrawerNavigator} from '@react-navigation/drawer' import { createDrawerNavigator } from '@react-navigation/drawer'
import {IconButton} from 'react-native-paper' import { IconButton } from 'react-native-paper'
import BestPage from './BestPage' import BestPage from './BestPage'
import {DrawerParamList} from './drawer-param-list' import { DrawerParamList } from './drawer-param-list'
import HomePage from './HomePage' import HomePage from './HomePage'
import PlanPage from './PlanPage' import PlanPage from './PlanPage'
import SettingsPage from './SettingsPage' import SettingsPage from './SettingsPage'
@ -20,36 +20,37 @@ export default function Routes() {
headerTintColor: dark ? 'white' : 'black', headerTintColor: dark ? 'white' : 'black',
swipeEdgeWidth: 1000, swipeEdgeWidth: 1000,
headerShown: false, headerShown: false,
}}> }}
>
<Drawer.Screen <Drawer.Screen
name="Home" name='Home'
component={HomePage} component={HomePage}
options={{drawerIcon: () => <IconButton icon="home" />}} options={{ drawerIcon: () => <IconButton icon='home' /> }}
/> />
<Drawer.Screen <Drawer.Screen
name="Plans" name='Plans'
component={PlanPage} component={PlanPage}
options={{drawerIcon: () => <IconButton icon="event" />}} options={{ drawerIcon: () => <IconButton icon='event' /> }}
/> />
<Drawer.Screen <Drawer.Screen
name="Best" name='Best'
component={BestPage} component={BestPage}
options={{drawerIcon: () => <IconButton icon="insights" />}} options={{ drawerIcon: () => <IconButton icon='insights' /> }}
/> />
<Drawer.Screen <Drawer.Screen
name="Workouts" name='Workouts'
component={WorkoutsPage} component={WorkoutsPage}
options={{drawerIcon: () => <IconButton icon="fitness-center" />}} options={{ drawerIcon: () => <IconButton icon='fitness-center' /> }}
/> />
<Drawer.Screen <Drawer.Screen
name="Timer" name='Timer'
component={TimerPage} component={TimerPage}
options={{drawerIcon: () => <IconButton icon="access-time" />}} options={{ drawerIcon: () => <IconButton icon='access-time' /> }}
/> />
<Drawer.Screen <Drawer.Screen
name="Settings" name='Settings'
component={SettingsPage} component={SettingsPage}
options={{drawerIcon: () => <IconButton icon="settings" />}} options={{ drawerIcon: () => <IconButton icon='settings' /> }}
/> />
</Drawer.Navigator> </Drawer.Navigator>
) )

View File

@ -1,7 +1,7 @@
import React, {useCallback, useMemo, useState} from 'react' import React, { useCallback, useMemo, useState } from 'react'
import {View} from 'react-native' import { View } from 'react-native'
import {Button, Menu, Subheading, useTheme} from 'react-native-paper' import { Button, Menu, Subheading, useTheme } from 'react-native-paper'
import {ITEM_PADDING} from './constants' import { ITEM_PADDING } from './constants'
export interface Item { export interface Item {
value: string value: string
@ -21,10 +21,10 @@ function Select({
label?: string label?: string
}) { }) {
const [show, setShow] = useState(false) const [show, setShow] = useState(false)
const {colors} = useTheme() const { colors } = useTheme()
const selected = useMemo( const selected = useMemo(
() => items.find(item => item.value === value) || items[0], () => items.find((item) => item.value === value) || items[0],
[items, value], [items, value],
) )
@ -42,8 +42,9 @@ function Select({
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingLeft: ITEM_PADDING, paddingLeft: ITEM_PADDING,
}}> }}
{label && <Subheading style={{width: 100}}>{label}</Subheading>} >
{label && <Subheading style={{ width: 100 }}>{label}</Subheading>}
<Menu <Menu
visible={show} visible={show}
onDismiss={() => setShow(false)} onDismiss={() => setShow(false)}
@ -52,14 +53,16 @@ function Select({
onPress={() => setShow(true)} onPress={() => setShow(true)}
style={{ style={{
alignSelf: 'flex-start', alignSelf: 'flex-start',
}}> }}
>
{selected?.label} {selected?.label}
</Button> </Button>
}> }
{items.map(item => ( >
{items.map((item) => (
<Menu.Item <Menu.Item
key={item.value} key={item.value}
titleStyle={{color: item.color || colors.text}} titleStyle={{ color: item.color || colors.text }}
title={item.label} title={item.label}
onPress={() => handlePress(item.value)} onPress={() => handlePress(item.value)}
/> />

View File

@ -1,11 +1,11 @@
import {NavigationProp, useNavigation} from '@react-navigation/native' import { NavigationProp, useNavigation } from '@react-navigation/native'
import {format} from 'date-fns' import { format } from 'date-fns'
import {useCallback, useMemo} from 'react' import { useCallback, useMemo } from 'react'
import {Image} from 'react-native' import { Image } from 'react-native'
import {List, Text} from 'react-native-paper' import { List, Text } from 'react-native-paper'
import {DARK_RIPPLE, LIGHT_RIPPLE} from './constants' import { DARK_RIPPLE, LIGHT_RIPPLE } from './constants'
import GymSet from './gym-set' import GymSet from './gym-set'
import {HomePageParams} from './home-page-params' import { HomePageParams } from './home-page-params'
import Settings from './settings' import Settings from './settings'
import useDark from './use-dark' import useDark from './use-dark'
@ -30,9 +30,9 @@ export default function SetItem({
}, [ids.length, item.id, setIds]) }, [ids.length, item.id, setIds])
const press = useCallback(() => { const press = useCallback(() => {
if (ids.length === 0) return navigation.navigate('EditSet', {set: item}) if (ids.length === 0) return navigation.navigate('EditSet', { set: item })
const removing = ids.find(id => id === item.id) const removing = ids.find((id) => id === item.id)
if (removing) setIds(ids.filter(id => id !== item.id)) if (removing) setIds(ids.filter((id) => id !== item.id))
else setIds([...ids, item.id]) else setIds([...ids, item.id])
}, [ids, item, navigation, setIds]) }, [ids, item, navigation, setIds])
@ -49,13 +49,15 @@ export default function SetItem({
title={item.name} title={item.name}
description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`} description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`}
onLongPress={longPress} onLongPress={longPress}
style={{backgroundColor}} style={{ backgroundColor }}
left={() => left={() =>
settings.images && settings.images &&
item.image && ( item.image && (
<Image source={{uri: item.image}} style={{height: 75, width: 75}} /> <Image
) source={{ uri: item.image }}
} style={{ height: 75, width: 75 }}
/>
)}
right={() => ( right={() => (
<> <>
{settings.showDate && ( {settings.showDate && (
@ -63,7 +65,8 @@ export default function SetItem({
style={{ style={{
alignSelf: 'center', alignSelf: 'center',
color: dark ? '#909090ff' : '#717171ff', color: dark ? '#909090ff' : '#717171ff',
}}> }}
>
{format(new Date(item.created), settings.date || 'P')} {format(new Date(item.created), settings.date || 'P')}
</Text> </Text>
)} )}

View File

@ -3,14 +3,14 @@ import {
useFocusEffect, useFocusEffect,
useNavigation, useNavigation,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useState} from 'react' import { useCallback, useState } from 'react'
import {FlatList} from 'react-native' import { FlatList } from 'react-native'
import {List} from 'react-native-paper' import { List } from 'react-native-paper'
import {Like} from 'typeorm' import { Like } from 'typeorm'
import {getNow, setRepo, settingsRepo} from './db' import { getNow, setRepo, settingsRepo } from './db'
import DrawerHeader from './DrawerHeader' import DrawerHeader from './DrawerHeader'
import GymSet, {defaultSet} from './gym-set' import GymSet, { defaultSet } from './gym-set'
import {HomePageParams} from './home-page-params' import { HomePageParams } from './home-page-params'
import ListMenu from './ListMenu' import ListMenu from './ListMenu'
import Page from './Page' import Page from './Page'
import SetItem from './SetItem' import SetItem from './SetItem'
@ -29,12 +29,12 @@ export default function SetList() {
const refresh = useCallback(async (value: string) => { const refresh = useCallback(async (value: string) => {
const newSets = await setRepo.find({ const newSets = await setRepo.find({
where: {name: Like(`%${value.trim()}%`), hidden: 0 as any}, where: { name: Like(`%${value.trim()}%`), hidden: 0 as any },
take: limit, take: limit,
skip: 0, skip: 0,
order: {created: 'DESC'}, order: { created: 'DESC' },
}) })
console.log(`${SetList.name}.refresh:`, {value, limit}) console.log(`${SetList.name}.refresh:`, { value, limit })
setSets(newSets) setSets(newSets)
setOffset(0) setOffset(0)
setEnd(false) setEnd(false)
@ -43,12 +43,12 @@ export default function SetList() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
refresh(term) refresh(term)
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
}, [refresh, term]), }, [refresh, term]),
) )
const renderItem = useCallback( const renderItem = useCallback(
({item}: {item: GymSet}) => ( ({ item }: { item: GymSet }) => (
<SetItem <SetItem
settings={settings} settings={settings}
item={item} item={item}
@ -64,12 +64,12 @@ export default function SetList() {
const next = useCallback(async () => { const next = useCallback(async () => {
if (end) return if (end) return
const newOffset = offset + limit const newOffset = offset + limit
console.log(`${SetList.name}.next:`, {offset, newOffset, term}) console.log(`${SetList.name}.next:`, { offset, newOffset, term })
const newSets = await setRepo.find({ const newSets = await setRepo.find({
where: {name: Like(`%${term}%`), hidden: 0 as any}, where: { name: Like(`%${term}%`), hidden: 0 as any },
take: limit, take: limit,
skip: newOffset, skip: newOffset,
order: {created: 'DESC'}, order: { created: 'DESC' },
}) })
if (newSets.length === 0) return setEnd(true) if (newSets.length === 0) return setEnd(true)
if (!sets) return if (!sets) return
@ -81,10 +81,10 @@ export default function SetList() {
const onAdd = useCallback(async () => { const onAdd = useCallback(async () => {
const now = await getNow() const now = await getNow()
let set = sets[0] let set = sets[0]
if (!set) set = {...defaultSet} if (!set) set = { ...defaultSet }
set.created = now set.created = now
delete set.id delete set.id
navigation.navigate('EditSet', {set}) navigation.navigate('EditSet', { set })
}, [navigation, sets]) }, [navigation, sets])
const search = useCallback( const search = useCallback(
@ -96,17 +96,17 @@ export default function SetList() {
) )
const edit = useCallback(() => { const edit = useCallback(() => {
navigation.navigate('EditSets', {ids}) navigation.navigate('EditSets', { ids })
setIds([]) setIds([])
}, [ids, navigation]) }, [ids, navigation])
const copy = useCallback(async () => { const copy = useCallback(async () => {
const set = await setRepo.findOne({ const set = await setRepo.findOne({
where: {id: ids.pop()}, where: { id: ids.pop() },
}) })
delete set.id delete set.id
delete set.created delete set.created
navigation.navigate('EditSet', {set}) navigation.navigate('EditSet', { set })
setIds([]) setIds([])
}, [ids, navigation]) }, [ids, navigation])
@ -121,7 +121,7 @@ export default function SetList() {
}, [ids, refresh, term]) }, [ids, refresh, term])
const select = useCallback(() => { const select = useCallback(() => {
setIds(sets.map(set => set.id)) setIds(sets.map((set) => set.id))
}, [sets]) }, [sets])
return ( return (
@ -138,21 +138,23 @@ export default function SetList() {
</DrawerHeader> </DrawerHeader>
<Page onAdd={onAdd} term={term} search={search}> <Page onAdd={onAdd} term={term} search={search}>
{sets?.length === 0 ? ( {sets?.length === 0
<List.Item ? (
title="No sets yet" <List.Item
description="A set is a group of repetitions. E.g. 8 reps of Squats." title='No sets yet'
/> description='A set is a group of repetitions. E.g. 8 reps of Squats.'
) : (
settings && (
<FlatList
data={sets}
style={{flex: 1}}
renderItem={renderItem}
onEndReached={next}
/> />
) )
)} : (
settings && (
<FlatList
data={sets}
style={{ flex: 1 }}
renderItem={renderItem}
onEndReached={next}
/>
)
)}
</Page> </Page>
</> </>
) )

View File

@ -1,25 +1,25 @@
import {NavigationProp, useNavigation} from '@react-navigation/native' import { NavigationProp, useNavigation } from '@react-navigation/native'
import {format} from 'date-fns' import { format } from 'date-fns'
import {useCallback, useEffect, useMemo, useState} from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import {useForm} from 'react-hook-form' import { useForm } from 'react-hook-form'
import {NativeModules, ScrollView, View} from 'react-native' import { NativeModules, ScrollView, View } from 'react-native'
import DocumentPicker from 'react-native-document-picker' import DocumentPicker from 'react-native-document-picker'
import {Dirs, FileSystem} from 'react-native-file-access' import { Dirs, FileSystem } from 'react-native-file-access'
import {Button, Subheading} from 'react-native-paper' import { Button, Subheading } from 'react-native-paper'
import ConfirmDialog from './ConfirmDialog' import ConfirmDialog from './ConfirmDialog'
import {ITEM_PADDING, MARGIN} from './constants' import { ITEM_PADDING, MARGIN } from './constants'
import {AppDataSource} from './data-source' import { AppDataSource } from './data-source'
import {setRepo, settingsRepo} from './db' import { setRepo, settingsRepo } from './db'
import {DrawerParamList} from './drawer-param-list' import { DrawerParamList } from './drawer-param-list'
import DrawerHeader from './DrawerHeader' import DrawerHeader from './DrawerHeader'
import Input from './input' import Input from './input'
import {darkOptions, lightOptions, themeOptions} from './options' import { darkOptions, lightOptions, themeOptions } from './options'
import Page from './Page' import Page from './Page'
import Select from './Select' import Select from './Select'
import Settings from './settings' import Settings from './settings'
import Switch from './Switch' import Switch from './Switch'
import {toast} from './toast' import { toast } from './toast'
import {useTheme} from './use-theme' import { useTheme } from './use-theme'
const twelveHours = [ const twelveHours = [
'dd/LL/yyyy', 'dd/LL/yyyy',
@ -45,20 +45,26 @@ export default function SettingsPage() {
const [term, setTerm] = useState('') const [term, setTerm] = useState('')
const [formatOptions, setFormatOptions] = useState<string[]>(twelveHours) const [formatOptions, setFormatOptions] = useState<string[]>(twelveHours)
const [importing, setImporting] = useState(false) const [importing, setImporting] = useState(false)
const {reset} = useNavigation<NavigationProp<DrawerParamList>>() const { reset } = useNavigation<NavigationProp<DrawerParamList>>()
const {watch, setValue} = useForm<Settings>({ const { watch, setValue } = useForm<Settings>({
defaultValues: () => settingsRepo.findOne({where: {}}), defaultValues: () => settingsRepo.findOne({ where: {} }),
}) })
const settings = watch() const settings = watch()
const {theme, setTheme, lightColor, setLightColor, darkColor, setDarkColor} = const {
useTheme() theme,
setTheme,
lightColor,
setLightColor,
darkColor,
setDarkColor,
} = useTheme()
useEffect(() => { useEffect(() => {
NativeModules.SettingsModule.ignoringBattery(setIgnoring) NativeModules.SettingsModule.ignoringBattery(setIgnoring)
NativeModules.SettingsModule.is24().then((is24: boolean) => { NativeModules.SettingsModule.is24().then((is24: boolean) => {
console.log(`${SettingsPage.name}.focus:`, {is24}) console.log(`${SettingsPage.name}.focus:`, { is24 })
if (is24) setFormatOptions(twentyFours) if (is24) setFormatOptions(twentyFours)
else setFormatOptions(twelveHours) else setFormatOptions(twelveHours)
}) })
@ -68,7 +74,7 @@ export default function SettingsPage() {
return settingsRepo return settingsRepo
.createQueryBuilder() .createQueryBuilder()
.update() .update()
.set({[key]: value}) .set({ [key]: value })
.printSql() .printSql()
.execute() .execute()
}, []) }, [])
@ -80,7 +86,7 @@ export default function SettingsPage() {
}, [settings.sound]) }, [settings.sound])
const changeSound = useCallback(async () => { const changeSound = useCallback(async () => {
const {fileCopyUri} = await DocumentPicker.pickSingle({ const { fileCopyUri } = await DocumentPicker.pickSingle({
type: DocumentPicker.types.audio, type: DocumentPicker.types.audio,
copyTo: 'documentDirectory', copyTo: 'documentDirectory',
}) })
@ -92,21 +98,21 @@ export default function SettingsPage() {
const switches: Input<boolean>[] = useMemo( const switches: Input<boolean>[] = useMemo(
() => [ () => [
{name: 'Rest timers', value: settings.alarm, key: 'alarm'}, { name: 'Rest timers', value: settings.alarm, key: 'alarm' },
{name: 'Vibrate', value: settings.vibrate, key: 'vibrate'}, { name: 'Vibrate', value: settings.vibrate, key: 'vibrate' },
{name: 'Disable sound', value: settings.noSound, key: 'noSound'}, { name: 'Disable sound', value: settings.noSound, key: 'noSound' },
{name: 'Notifications', value: settings.notify, key: 'notify'}, { name: 'Notifications', value: settings.notify, key: 'notify' },
{name: 'Show images', value: settings.images, key: 'images'}, { name: 'Show images', value: settings.images, key: 'images' },
{name: 'Show unit', value: settings.showUnit, key: 'showUnit'}, { name: 'Show unit', value: settings.showUnit, key: 'showUnit' },
{name: 'Show steps', value: settings.steps, key: 'steps'}, { name: 'Show steps', value: settings.steps, key: 'steps' },
{name: 'Show date', value: settings.showDate, key: 'showDate'}, { name: 'Show date', value: settings.showDate, key: 'showDate' },
{name: 'Automatic backup', value: settings.backup, key: 'backup'}, { name: 'Automatic backup', value: settings.backup, key: 'backup' },
], ],
[settings], [settings],
) )
const filter = useCallback( const filter = useCallback(
({name}) => name.toLowerCase().includes(term.toLowerCase()), ({ name }) => name.toLowerCase().includes(term.toLowerCase()),
[term], [term],
) )
@ -168,7 +174,7 @@ export default function SettingsPage() {
<Switch <Switch
key={item.name} key={item.name}
value={item.value} value={item.value}
onChange={value => changeBoolean(item.key, value)} onChange={(value) => changeBoolean(item.key, value)}
title={item.name} title={item.name}
/> />
), ),
@ -176,7 +182,7 @@ export default function SettingsPage() {
) )
const switchesMarkup = useMemo( const switchesMarkup = useMemo(
() => switches.filter(filter).map(s => renderSwitch(s)), () => switches.filter(filter).map((s) => renderSwitch(s)),
[filter, switches, renderSwitch], [filter, switches, renderSwitch],
) )
@ -211,7 +217,7 @@ export default function SettingsPage() {
const selects: Input<string>[] = useMemo(() => { const selects: Input<string>[] = useMemo(() => {
const today = new Date() const today = new Date()
return [ return [
{name: 'Theme', value: theme, items: themeOptions, key: 'theme'}, { name: 'Theme', value: theme, items: themeOptions, key: 'theme' },
{ {
name: 'Dark color', name: 'Dark color',
value: darkColor, value: darkColor,
@ -227,7 +233,7 @@ export default function SettingsPage() {
{ {
name: 'Date format', name: 'Date format',
value: settings.date, value: settings.date,
items: formatOptions.map(option => ({ items: formatOptions.map((option) => ({
label: format(today, option), label: format(today, option),
value: option, value: option,
})), })),
@ -241,7 +247,7 @@ export default function SettingsPage() {
<Select <Select
key={item.name} key={item.name}
value={item.value} value={item.value}
onChange={value => changeString(item.key, value)} onChange={(value) => changeString(item.key, value)}
label={item.name} label={item.name}
items={item.items} items={item.items}
/> />
@ -260,17 +266,17 @@ export default function SettingsPage() {
const file = await DocumentPicker.pickSingle() const file = await DocumentPicker.pickSingle()
await FileSystem.cp(file.uri, Dirs.DatabaseDir + '/massive.db') await FileSystem.cp(file.uri, Dirs.DatabaseDir + '/massive.db')
await AppDataSource.initialize() await AppDataSource.initialize()
await setRepo.createQueryBuilder().update().set({image: null}).execute() await setRepo.createQueryBuilder().update().set({ image: null }).execute()
await update('sound', null) await update('sound', null)
const {alarm, backup} = await settingsRepo.findOne({where: {}}) const { alarm, backup } = await settingsRepo.findOne({ where: {} })
console.log({backup}) console.log({ backup })
const directory = await DocumentPicker.pickDirectory() const directory = await DocumentPicker.pickDirectory()
if (backup) NativeModules.BackupModule.start(directory.uri) if (backup) NativeModules.BackupModule.start(directory.uri)
else NativeModules.BackupModule.stop() else NativeModules.BackupModule.stop()
NativeModules.SettingsModule.ignoringBattery( NativeModules.SettingsModule.ignoringBattery(
async (isIgnoring: boolean) => { async (isIgnoring: boolean) => {
if (alarm && !isIgnoring) NativeModules.SettingsModule.ignoreBattery() if (alarm && !isIgnoring) NativeModules.SettingsModule.ignoreBattery()
reset({index: 0, routes: [{name: 'Settings'}]}) reset({ index: 0, routes: [{ name: 'Settings' }] })
}, },
) )
}, [reset, update]) }, [reset, update])
@ -287,13 +293,14 @@ export default function SettingsPage() {
name: 'Alarm sound', name: 'Alarm sound',
element: ( element: (
<View <View
key="alarm-sound" key='alarm-sound'
style={{ style={{
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingLeft: ITEM_PADDING, paddingLeft: ITEM_PADDING,
}}> }}
<Subheading style={{width: 100}}>Alarm sound</Subheading> >
<Subheading style={{ width: 100 }}>Alarm sound</Subheading>
<Button onPress={changeSound}>{soundString || 'Default'}</Button> <Button onPress={changeSound}>{soundString || 'Default'}</Button>
</View> </View>
), ),
@ -302,9 +309,10 @@ export default function SettingsPage() {
name: 'Export database', name: 'Export database',
element: ( element: (
<Button <Button
key="export-db" key='export-db'
style={{alignSelf: 'flex-start'}} style={{ alignSelf: 'flex-start' }}
onPress={exportDatabase}> onPress={exportDatabase}
>
Export database Export database
</Button> </Button>
), ),
@ -313,9 +321,10 @@ export default function SettingsPage() {
name: 'Import database', name: 'Import database',
element: ( element: (
<Button <Button
key="import-db" key='import-db'
style={{alignSelf: 'flex-start'}} style={{ alignSelf: 'flex-start' }}
onPress={() => setImporting(true)}> onPress={() => setImporting(true)}
>
Import database Import database
</Button> </Button>
), ),
@ -325,16 +334,16 @@ export default function SettingsPage() {
) )
const buttonsMarkup = useMemo( const buttonsMarkup = useMemo(
() => buttons.filter(filter).map(b => b.element), () => buttons.filter(filter).map((b) => b.element),
[buttons, filter], [buttons, filter],
) )
return ( return (
<> <>
<DrawerHeader name="Settings" /> <DrawerHeader name='Settings' />
<Page term={term} search={setTerm} style={{flexGrow: 1}}> <Page term={term} search={setTerm} style={{ flexGrow: 1 }}>
<ScrollView style={{marginTop: MARGIN, flex: 1}}> <ScrollView style={{ marginTop: MARGIN, flex: 1 }}>
{switchesMarkup} {switchesMarkup}
{selectsMarkup} {selectsMarkup}
{buttonsMarkup} {buttonsMarkup}
@ -342,10 +351,11 @@ export default function SettingsPage() {
</Page> </Page>
<ConfirmDialog <ConfirmDialog
title="Are you sure?" title='Are you sure?'
onOk={confirmImport} onOk={confirmImport}
setShow={setImporting} setShow={setImporting}
show={importing}> show={importing}
>
Importing a database overwrites your current data. This action cannot be Importing a database overwrites your current data. This action cannot be
reversed! reversed!
</ConfirmDialog> </ConfirmDialog>

View File

@ -1,5 +1,5 @@
import {useNavigation} from '@react-navigation/native' import { useNavigation } from '@react-navigation/native'
import {Appbar, IconButton} from 'react-native-paper' import { Appbar, IconButton } from 'react-native-paper'
import useDark from './use-dark' import useDark from './use-dark'
export default function StackHeader({ export default function StackHeader({
@ -16,7 +16,7 @@ export default function StackHeader({
<Appbar.Header> <Appbar.Header>
<IconButton <IconButton
color={dark ? 'white' : 'white'} color={dark ? 'white' : 'white'}
icon="arrow-back" icon='arrow-back'
onPress={navigation.goBack} onPress={navigation.goBack}
/> />
<Appbar.Content title={title} /> <Appbar.Content title={title} />

View File

@ -5,25 +5,25 @@ import {
useNavigation, useNavigation,
useRoute, useRoute,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useMemo, useRef, useState} from 'react' import { useCallback, useMemo, useRef, useState } from 'react'
import {FlatList, NativeModules, TextInput, View} from 'react-native' import { FlatList, NativeModules, TextInput, View } from 'react-native'
import {Button, IconButton, ProgressBar} from 'react-native-paper' import { Button, IconButton, ProgressBar } from 'react-native-paper'
import AppInput from './AppInput' import AppInput from './AppInput'
import {getBestSet, getLast} from './best.service' import { getBestSet, getLast } from './best.service'
import {PADDING} from './constants' import { PADDING } from './constants'
import CountMany from './count-many' import CountMany from './count-many'
import {AppDataSource} from './data-source' import { AppDataSource } from './data-source'
import {getNow, setRepo, settingsRepo} from './db' import { getNow, setRepo, settingsRepo } from './db'
import GymSet from './gym-set' import GymSet from './gym-set'
import {PlanPageParams} from './plan-page-params' import { PlanPageParams } from './plan-page-params'
import Settings from './settings' import Settings from './settings'
import StackHeader from './StackHeader' import StackHeader from './StackHeader'
import StartPlanItem from './StartPlanItem' import StartPlanItem from './StartPlanItem'
import {toast} from './toast' import { toast } from './toast'
import useDark from './use-dark' import useDark from './use-dark'
export default function StartPlan() { export default function StartPlan() {
const {params} = useRoute<RouteProp<PlanPageParams, 'StartPlan'>>() const { params } = useRoute<RouteProp<PlanPageParams, 'StartPlan'>>()
const [reps, setReps] = useState(params.first?.reps.toString() || '0') const [reps, setReps] = useState(params.first?.reps.toString() || '0')
const [weight, setWeight] = useState(params.first?.weight.toString() || '0') const [weight, setWeight] = useState(params.first?.weight.toString() || '0')
const [unit, setUnit] = useState<string>(params.first?.unit || 'kg') const [unit, setUnit] = useState<string>(params.first?.unit || 'kg')
@ -58,7 +58,7 @@ export default function StartPlan() {
OFFSET 1 OFFSET 1
` `
const newCounts = await AppDataSource.manager.query(select) const newCounts = await AppDataSource.manager.query(select)
console.log(`${StartPlan.name}.focus:`, {newCounts}) console.log(`${StartPlan.name}.focus:`, { newCounts })
setCounts(newCounts) setCounts(newCounts)
}, [workouts]) }, [workouts])
@ -67,11 +67,11 @@ export default function StartPlan() {
setSelected(index) setSelected(index)
if (!counts && !newCounts) return if (!counts && !newCounts) return
const workout = counts ? counts[index] : newCounts[index] const workout = counts ? counts[index] : newCounts[index]
console.log(`${StartPlan.name}.next:`, {workout}) console.log(`${StartPlan.name}.next:`, { workout })
const last = await getLast(workout.name) const last = await getLast(workout.name)
if (!last) return if (!last) return
delete last.id delete last.id
console.log(`${StartPlan.name}.select:`, {last}) console.log(`${StartPlan.name}.select:`, { last })
setReps(last.reps.toString()) setReps(last.reps.toString())
setWeight(last.weight.toString()) setWeight(last.weight.toString())
setUnit(last.unit) setUnit(last.unit)
@ -81,7 +81,7 @@ export default function StartPlan() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
refresh() refresh()
}, [refresh]), }, [refresh]),
) )
@ -104,11 +104,12 @@ export default function StartPlan() {
if ( if (
settings.notify && settings.notify &&
(+weight > best.weight || (+reps > best.reps && +weight === best.weight)) (+weight > best.weight || (+reps > best.reps && +weight === best.weight))
) ) {
toast("Great work King! That's a new record.") toast('Great work King! That\'s a new record.')
}
if (!settings.alarm) return if (!settings.alarm) return
const milliseconds = const milliseconds = Number(best.minutes) * 60 * 1000 +
Number(best.minutes) * 60 * 1000 + Number(best.seconds) * 1000 Number(best.seconds) * 1000
NativeModules.AlarmModule.timer(milliseconds) NativeModules.AlarmModule.timer(milliseconds)
} }
@ -117,25 +118,25 @@ export default function StartPlan() {
<StackHeader title={params.plan.days.replace(/,/g, ', ')}> <StackHeader title={params.plan.days.replace(/,/g, ', ')}>
<IconButton <IconButton
color={dark ? 'white' : 'white'} color={dark ? 'white' : 'white'}
onPress={() => navigation.navigate('EditPlan', {plan: params.plan})} onPress={() => navigation.navigate('EditPlan', { plan: params.plan })}
icon="edit" icon='edit'
/> />
</StackHeader> </StackHeader>
<View style={{padding: PADDING, flex: 1, flexDirection: 'column'}}> <View style={{ padding: PADDING, flex: 1, flexDirection: 'column' }}>
<View style={{flex: 1}}> <View style={{ flex: 1 }}>
<AppInput <AppInput
label="Reps" label='Reps'
keyboardType="numeric" keyboardType='numeric'
value={reps} value={reps}
onChangeText={setReps} onChangeText={setReps}
onSubmitEditing={() => weightRef.current?.focus()} onSubmitEditing={() => weightRef.current?.focus()}
selection={selection} selection={selection}
onSelectionChange={e => setSelection(e.nativeEvent.selection)} onSelectionChange={(e) => setSelection(e.nativeEvent.selection)}
innerRef={repsRef} innerRef={repsRef}
/> />
<AppInput <AppInput
label="Weight" label='Weight'
keyboardType="numeric" keyboardType='numeric'
value={weight} value={weight}
onChangeText={setWeight} onChangeText={setWeight}
onSubmitEditing={handleSubmit} onSubmitEditing={handleSubmit}
@ -144,8 +145,8 @@ export default function StartPlan() {
/> />
{settings?.showUnit && ( {settings?.showUnit && (
<AppInput <AppInput
autoCapitalize="none" autoCapitalize='none'
label="Unit" label='Unit'
value={unit} value={unit}
onChangeText={setUnit} onChangeText={setUnit}
innerRef={unitRef} innerRef={unitRef}
@ -154,7 +155,7 @@ export default function StartPlan() {
{counts && ( {counts && (
<FlatList <FlatList
data={counts} data={counts}
renderItem={props => ( renderItem={(props) => (
<View> <View>
<StartPlanItem <StartPlanItem
{...props} {...props}
@ -170,7 +171,7 @@ export default function StartPlan() {
/> />
)} )}
</View> </View>
<Button mode="contained" icon="save" onPress={handleSubmit}> <Button mode='contained' icon='save' onPress={handleSubmit}>
Save Save
</Button> </Button>
</View> </View>

View File

@ -1,12 +1,12 @@
import {NavigationProp, useNavigation} from '@react-navigation/native' import { NavigationProp, useNavigation } from '@react-navigation/native'
import React, {useCallback, useState} from 'react' import React, { useCallback, useState } from 'react'
import {GestureResponderEvent, ListRenderItemInfo, View} from 'react-native' import { GestureResponderEvent, ListRenderItemInfo, View } from 'react-native'
import {List, Menu, RadioButton, useTheme} from 'react-native-paper' import { List, Menu, RadioButton, useTheme } from 'react-native-paper'
import {Like} from 'typeorm' import { Like } from 'typeorm'
import CountMany from './count-many' import CountMany from './count-many'
import {getNow, setRepo} from './db' import { getNow, setRepo } from './db'
import {PlanPageParams} from './plan-page-params' import { PlanPageParams } from './plan-page-params'
import {toast} from './toast' import { toast } from './toast'
interface Props extends ListRenderItemInfo<CountMany> { interface Props extends ListRenderItemInfo<CountMany> {
onSelect: (index: number) => void onSelect: (index: number) => void
@ -15,11 +15,11 @@ interface Props extends ListRenderItemInfo<CountMany> {
} }
export default function StartPlanItem(props: Props) { export default function StartPlanItem(props: Props) {
const {index, item, onSelect, selected, onUndo} = props const { index, item, onSelect, selected, onUndo } = props
const {colors} = useTheme() const { colors } = useTheme()
const [anchor, setAnchor] = useState({x: 0, y: 0}) const [anchor, setAnchor] = useState({ x: 0, y: 0 })
const [showMenu, setShowMenu] = useState(false) const [showMenu, setShowMenu] = useState(false)
const {navigate} = useNavigation<NavigationProp<PlanPageParams>>() const { navigate } = useNavigation<NavigationProp<PlanPageParams>>()
const undo = useCallback(async () => { const undo = useCallback(async () => {
const now = await getNow() const now = await getNow()
@ -30,7 +30,7 @@ export default function StartPlanItem(props: Props) {
hidden: 0 as any, hidden: 0 as any,
created: Like(`${created}%`), created: Like(`${created}%`),
}, },
order: {created: 'desc'}, order: { created: 'desc' },
}) })
setShowMenu(false) setShowMenu(false)
if (!first) return toast('Nothing to undo.') if (!first) return toast('Nothing to undo.')
@ -40,7 +40,7 @@ export default function StartPlanItem(props: Props) {
const longPress = useCallback( const longPress = useCallback(
(e: GestureResponderEvent) => { (e: GestureResponderEvent) => {
setAnchor({x: e.nativeEvent.pageX, y: e.nativeEvent.pageY}) setAnchor({ x: e.nativeEvent.pageX, y: e.nativeEvent.pageY })
setShowMenu(true) setShowMenu(true)
}, },
[setShowMenu, setAnchor], [setShowMenu, setAnchor],
@ -55,25 +55,26 @@ export default function StartPlanItem(props: Props) {
hidden: 0 as any, hidden: 0 as any,
created: Like(`${created}%`), created: Like(`${created}%`),
}, },
order: {created: 'desc'}, order: { created: 'desc' },
}) })
setShowMenu(false) setShowMenu(false)
if (!first) return toast('Nothing to edit.') if (!first) return toast('Nothing to edit.')
navigate('EditSet', {set: first}) navigate('EditSet', { set: first })
} }
return ( return (
<List.Item <List.Item
onLongPress={longPress} onLongPress={longPress}
title={item.name} title={item.name}
description={ description={item.sets
item.sets ? `${item.total} / ${item.sets}` : item.total.toString() ? `${item.total} / ${item.sets}`
} : item.total.toString()}
onPress={() => onSelect(index)} onPress={() => onSelect(index)}
left={() => ( left={() => (
<View style={{alignItems: 'center', justifyContent: 'center'}}> <View style={{ alignItems: 'center', justifyContent: 'center' }}>
<RadioButton <RadioButton
onPress={() => onSelect(index)} onPress={() =>
onSelect(index)}
value={index.toString()} value={index.toString()}
status={selected === index ? 'checked' : 'unchecked'} status={selected === index ? 'checked' : 'unchecked'}
color={colors.primary} color={colors.primary}
@ -85,13 +86,15 @@ export default function StartPlanItem(props: Props) {
style={{ style={{
width: '25%', width: '25%',
justifyContent: 'center', justifyContent: 'center',
}}> }}
>
<Menu <Menu
anchor={anchor} anchor={anchor}
visible={showMenu} visible={showMenu}
onDismiss={() => setShowMenu(false)}> onDismiss={() => setShowMenu(false)}
<Menu.Item icon="edit" onPress={edit} title="Edit" /> >
<Menu.Item icon="undo" onPress={undo} title="Undo" /> <Menu.Item icon='edit' onPress={edit} title='Edit' />
<Menu.Item icon='undo' onPress={undo} title='Undo' />
</Menu> </Menu>
</View> </View>
)} )}

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import {Platform, Pressable} from 'react-native' import { Platform, Pressable } from 'react-native'
import {Switch as PaperSwitch, Text, useTheme} from 'react-native-paper' import { Switch as PaperSwitch, Text, useTheme } from 'react-native-paper'
import {MARGIN} from './constants' import { MARGIN } from './constants'
function Switch({ function Switch({
value, value,
@ -12,7 +12,7 @@ function Switch({
onChange: (value: boolean) => void onChange: (value: boolean) => void
title: string title: string
}) { }) {
const {colors} = useTheme() const { colors } = useTheme()
return ( return (
<Pressable <Pressable
@ -22,13 +22,14 @@ function Switch({
flexWrap: 'wrap', flexWrap: 'wrap',
alignItems: 'center', alignItems: 'center',
marginBottom: Platform.OS === 'ios' ? MARGIN : null, marginBottom: Platform.OS === 'ios' ? MARGIN : null,
}}> }}
>
<PaperSwitch <PaperSwitch
color={colors.primary} color={colors.primary}
style={{marginRight: MARGIN}} style={{ marginRight: MARGIN }}
value={value} value={value}
onValueChange={onChange} onValueChange={onChange}
trackColor={{true: colors.primary + '80', false: colors.disabled}} trackColor={{ true: colors.primary + '80', false: colors.disabled }}
/> />
<Text>{title}</Text> <Text>{title}</Text>
</Pressable> </Pressable>

View File

@ -1,11 +1,11 @@
import {useFocusEffect} from '@react-navigation/native' import { useFocusEffect } from '@react-navigation/native'
import React, {useCallback, useMemo, useState} from 'react' import React, { useCallback, useMemo, useState } from 'react'
import {Dimensions, NativeModules, View} from 'react-native' import { Dimensions, NativeModules, View } from 'react-native'
import {Button, Text, useTheme} from 'react-native-paper' import { Button, Text, useTheme } from 'react-native-paper'
import {ProgressCircle} from 'react-native-svg-charts' import { ProgressCircle } from 'react-native-svg-charts'
import AppFab from './AppFab' import AppFab from './AppFab'
import {MARGIN, PADDING} from './constants' import { MARGIN, PADDING } from './constants'
import {settingsRepo} from './db' import { settingsRepo } from './db'
import DrawerHeader from './DrawerHeader' import DrawerHeader from './DrawerHeader'
import Settings from './settings' import Settings from './settings'
import useTimer from './use-timer' import useTimer from './use-timer'
@ -16,13 +16,13 @@ export interface TickEvent {
} }
export default function TimerPage() { export default function TimerPage() {
const {minutes, seconds} = useTimer() const { minutes, seconds } = useTimer()
const [settings, setSettings] = useState<Settings>() const [settings, setSettings] = useState<Settings>()
const {colors} = useTheme() const { colors } = useTheme()
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
}, []), }, []),
) )
@ -45,19 +45,20 @@ export default function TimerPage() {
return ( return (
<> <>
<DrawerHeader name="Timer" /> <DrawerHeader name='Timer' />
<View style={{flexGrow: 1, padding: PADDING}}> <View style={{ flexGrow: 1, padding: PADDING }}>
<View <View
style={{ style={{
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}}> }}
<Text style={{fontSize: 70, position: 'absolute'}}> >
<Text style={{ fontSize: 70, position: 'absolute' }}>
{minutes}:{seconds} {minutes}:{seconds}
</Text> </Text>
<ProgressCircle <ProgressCircle
style={{height: 300, width: 300, marginBottom: MARGIN}} style={{ height: 300, width: 300, marginBottom: MARGIN }}
progress={progress} progress={progress}
strokeWidth={10} strokeWidth={10}
progressColor={colors.primary} progressColor={colors.primary}
@ -65,10 +66,10 @@ export default function TimerPage() {
/> />
</View> </View>
</View> </View>
<Button onPress={add} style={{position: 'absolute', top: '82%', left}}> <Button onPress={add} style={{ position: 'absolute', top: '82%', left }}>
Add 1 min Add 1 min
</Button> </Button>
<AppFab icon="stop" onPress={stop} /> <AppFab icon='stop' onPress={stop} />
</> </>
) )
} }

View File

@ -1,25 +1,25 @@
import {RouteProp, useRoute} from '@react-navigation/native' import { RouteProp, useRoute } from '@react-navigation/native'
import {format} from 'date-fns' import { format } from 'date-fns'
import {useEffect, useMemo, useState} from 'react' import { useEffect, useMemo, useState } from 'react'
import {View} from 'react-native' import { View } from 'react-native'
import {FileSystem} from 'react-native-file-access' import { FileSystem } from 'react-native-file-access'
import {IconButton, List} from 'react-native-paper' import { IconButton, List } from 'react-native-paper'
import Share from 'react-native-share' import Share from 'react-native-share'
import {captureScreen} from 'react-native-view-shot' import { captureScreen } from 'react-native-view-shot'
import {BestPageParams} from './BestPage' import { BestPageParams } from './BestPage'
import Chart from './Chart' import Chart from './Chart'
import {PADDING} from './constants' import { PADDING } from './constants'
import {setRepo} from './db' import { setRepo } from './db'
import GymSet from './gym-set' import GymSet from './gym-set'
import {Metrics} from './metrics' import { Metrics } from './metrics'
import {Periods} from './periods' import { Periods } from './periods'
import Select from './Select' import Select from './Select'
import StackHeader from './StackHeader' import StackHeader from './StackHeader'
import useDark from './use-dark' import useDark from './use-dark'
import Volume from './volume' import Volume from './volume'
export default function ViewBest() { export default function ViewBest() {
const {params} = useRoute<RouteProp<BestPageParams, 'ViewBest'>>() const { params } = useRoute<RouteProp<BestPageParams, 'ViewBest'>>()
const [weights, setWeights] = useState<GymSet[]>() const [weights, setWeights] = useState<GymSet[]>()
const [volumes, setVolumes] = useState<Volume[]>() const [volumes, setVolumes] = useState<Volume[]>()
const [metric, setMetric] = useState(Metrics.Weight) const [metric, setMetric] = useState(Metrics.Weight)
@ -34,11 +34,11 @@ export default function ViewBest() {
if (period === Periods.Yearly) group = '%Y-%m' if (period === Periods.Yearly) group = '%Y-%m'
const builder = setRepo const builder = setRepo
.createQueryBuilder() .createQueryBuilder()
.select("STRFTIME('%Y-%m-%d', created)", 'created') .select('STRFTIME(\'%Y-%m-%d\', created)', 'created')
.addSelect('unit') .addSelect('unit')
.where('name = :name', {name: params.best.name}) .where('name = :name', { name: params.best.name })
.andWhere('NOT hidden') .andWhere('NOT hidden')
.andWhere("DATE(created) >= DATE('now', 'weekday 0', :difference)", { .andWhere('DATE(created) >= DATE(\'now\', \'weekday 0\', :difference)', {
difference, difference,
}) })
.groupBy('name') .groupBy('name')
@ -64,8 +64,8 @@ export default function ViewBest() {
'weight', 'weight',
) )
.getRawMany() .getRawMany()
.then(newWeights => { .then((newWeights) => {
console.log({weights: newWeights}) console.log({ weights: newWeights })
setWeights(newWeights) setWeights(newWeights)
}) })
} }
@ -76,32 +76,31 @@ export default function ViewBest() {
(metric === Metrics.Volume && volumes?.length === 0) || (metric === Metrics.Volume && volumes?.length === 0) ||
(metric === Metrics.Weight && weights?.length === 0) || (metric === Metrics.Weight && weights?.length === 0) ||
(metric === Metrics.OneRepMax && weights?.length === 0) (metric === Metrics.OneRepMax && weights?.length === 0)
) ) {
return <List.Item title="No data yet." /> return <List.Item title='No data yet.' />
if (metric === Metrics.Volume && volumes?.length && weights?.length) }
if (metric === Metrics.Volume && volumes?.length && weights?.length) {
return ( return (
<Chart <Chart
yData={volumes.map(v => v.value)} yData={volumes.map((v) => v.value)}
yFormat={(value: number) => yFormat={(value: number) =>
`${value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}${ `${value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}${
volumes[0].unit || 'kg' volumes[0].unit || 'kg'
}` }`}
}
xData={weights} xData={weights}
xFormat={(_value, index) => xFormat={(_value, index) =>
format(new Date(weights[index].created), 'd/M') format(new Date(weights[index].created), 'd/M')}
}
/> />
) )
}
return ( return (
<Chart <Chart
yData={weights?.map(set => set.weight) || []} yData={weights?.map((set) => set.weight) || []}
yFormat={value => `${value}${weights?.[0].unit}`} yFormat={(value) => `${value}${weights?.[0].unit}`}
xData={weights || []} xData={weights || []}
xFormat={(_value, index) => xFormat={(_value, index) =>
format(new Date(weights?.[index].created), 'd/M') format(new Date(weights?.[index].created), 'd/M')}
}
/> />
) )
}, [volumes, weights, metric]) }, [volumes, weights, metric])
@ -112,40 +111,39 @@ export default function ViewBest() {
<IconButton <IconButton
color={dark ? 'white' : 'white'} color={dark ? 'white' : 'white'}
onPress={() => onPress={() =>
captureScreen().then(async uri => { captureScreen().then(async (uri) => {
const base64 = await FileSystem.readFile(uri, 'base64') const base64 = await FileSystem.readFile(uri, 'base64')
const url = `data:image/jpeg;base64,${base64}` const url = `data:image/jpeg;base64,${base64}`
Share.open({ Share.open({
type: 'image/jpeg', type: 'image/jpeg',
url, url,
}) })
}) })}
} icon='share'
icon="share"
/> />
</StackHeader> </StackHeader>
<View style={{padding: PADDING}}> <View style={{ padding: PADDING }}>
<Select <Select
label="Metric" label='Metric'
items={[ items={[
{value: Metrics.Volume, label: Metrics.Volume}, { value: Metrics.Volume, label: Metrics.Volume },
{value: Metrics.OneRepMax, label: Metrics.OneRepMax}, { value: Metrics.OneRepMax, label: Metrics.OneRepMax },
{ {
label: Metrics.Weight, label: Metrics.Weight,
value: Metrics.Weight, value: Metrics.Weight,
}, },
]} ]}
onChange={value => setMetric(value as Metrics)} onChange={(value) => setMetric(value as Metrics)}
value={metric} value={metric}
/> />
<Select <Select
label="Period" label='Period'
items={[ items={[
{value: Periods.Weekly, label: Periods.Weekly}, { value: Periods.Weekly, label: Periods.Weekly },
{value: Periods.Monthly, label: Periods.Monthly}, { value: Periods.Monthly, label: Periods.Monthly },
{value: Periods.Yearly, label: Periods.Yearly}, { value: Periods.Yearly, label: Periods.Yearly },
]} ]}
onChange={value => setPeriod(value as Periods)} onChange={(value) => setPeriod(value as Periods)}
value={period} value={period}
/> />
{charts} {charts}

View File

@ -1,11 +1,11 @@
import {NavigationProp, useNavigation} from '@react-navigation/native' import { NavigationProp, useNavigation } from '@react-navigation/native'
import {useCallback, useMemo, useState} from 'react' import { useCallback, useMemo, 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 {setRepo} from './db' import { setRepo } from './db'
import GymSet from './gym-set' import GymSet from './gym-set'
import {WorkoutsPageParams} from './WorkoutsPage' import { WorkoutsPageParams } from './WorkoutsPage'
export default function WorkoutItem({ export default function WorkoutItem({
item, item,
@ -17,19 +17,19 @@ export default function WorkoutItem({
images: boolean images: boolean
}) { }) {
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 navigation = useNavigation<NavigationProp<WorkoutsPageParams>>() const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>()
const remove = useCallback(async () => { const remove = useCallback(async () => {
await setRepo.delete({name: item.name}) await setRepo.delete({ name: item.name })
setShowMenu(false) setShowMenu(false)
onRemove() onRemove()
}, [setShowMenu, onRemove, item.name]) }, [setShowMenu, onRemove, item.name])
const longPress = useCallback( const longPress = useCallback(
(e: GestureResponderEvent) => { (e: GestureResponderEvent) => {
setAnchor({x: e.nativeEvent.pageX, y: e.nativeEvent.pageY}) setAnchor({ x: e.nativeEvent.pageX, y: e.nativeEvent.pageY })
setShowMenu(true) setShowMenu(true)
}, },
[setShowMenu, setAnchor], [setShowMenu, setAnchor],
@ -43,32 +43,36 @@ export default function WorkoutItem({
return ( return (
<> <>
<List.Item <List.Item
onPress={() => navigation.navigate('EditWorkout', {value: item})} onPress={() => navigation.navigate('EditWorkout', { value: item })}
title={item.name} title={item.name}
description={description} description={description}
onLongPress={longPress} onLongPress={longPress}
left={() => left={() =>
images && images &&
item.image && ( item.image && (
<Image source={{uri: item.image}} style={{height: 75, width: 75}} /> <Image
) source={{ uri: item.image }}
} style={{ height: 75, width: 75 }}
/>
)}
right={() => ( right={() => (
<Text <Text
style={{ style={{
alignSelf: 'center', alignSelf: 'center',
}}> }}
>
<Menu <Menu
anchor={anchor} anchor={anchor}
visible={showMenu} visible={showMenu}
onDismiss={() => setShowMenu(false)}> onDismiss={() => setShowMenu(false)}
>
<Menu.Item <Menu.Item
icon="delete" icon='delete'
onPress={() => { onPress={() => {
setShowRemove(item.name) setShowRemove(item.name)
setShowMenu(false) setShowMenu(false)
}} }}
title="Delete" title='Delete'
/> />
</Menu> </Menu>
</Text> </Text>
@ -77,8 +81,9 @@ export default function WorkoutItem({
<ConfirmDialog <ConfirmDialog
title={`Delete ${showRemove}`} title={`Delete ${showRemove}`}
show={!!showRemove} show={!!showRemove}
setShow={show => (show ? null : setShowRemove(''))} setShow={(show) => (show ? null : setShowRemove(''))}
onOk={remove}> onOk={remove}
>
This irreversibly deletes ALL sets related to this workout. Are you This irreversibly deletes ALL sets related to this workout. Are you
sure? sure?
</ConfirmDialog> </ConfirmDialog>

View File

@ -3,17 +3,17 @@ import {
useFocusEffect, useFocusEffect,
useNavigation, useNavigation,
} from '@react-navigation/native' } from '@react-navigation/native'
import {useCallback, useState} from 'react' import { useCallback, useState } from 'react'
import {FlatList} from 'react-native' import { FlatList } from 'react-native'
import {List} from 'react-native-paper' import { List } from 'react-native-paper'
import {setRepo, settingsRepo} from './db' import { setRepo, settingsRepo } from './db'
import DrawerHeader from './DrawerHeader' import DrawerHeader from './DrawerHeader'
import GymSet from './gym-set' import GymSet from './gym-set'
import Page from './Page' import Page from './Page'
import SetList from './SetList' import SetList from './SetList'
import Settings from './settings' import Settings from './settings'
import WorkoutItem from './WorkoutItem' import WorkoutItem from './WorkoutItem'
import {WorkoutsPageParams} from './WorkoutsPage' import { WorkoutsPageParams } from './WorkoutsPage'
const limit = 15 const limit = 15
@ -29,12 +29,12 @@ export default function WorkoutList() {
const newWorkouts = await setRepo const newWorkouts = await setRepo
.createQueryBuilder() .createQueryBuilder()
.select() .select()
.where('name LIKE :name', {name: `%${value.trim()}%`}) .where('name LIKE :name', { name: `%${value.trim()}%` })
.groupBy('name') .groupBy('name')
.orderBy('name') .orderBy('name')
.limit(limit) .limit(limit)
.getMany() .getMany()
console.log(`${WorkoutList.name}`, {newWorkout: newWorkouts[0]}) console.log(`${WorkoutList.name}`, { newWorkout: newWorkouts[0] })
setWorkouts(newWorkouts) setWorkouts(newWorkouts)
setOffset(0) setOffset(0)
setEnd(false) setEnd(false)
@ -43,12 +43,12 @@ export default function WorkoutList() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
refresh(term) refresh(term)
settingsRepo.findOne({where: {}}).then(setSettings) settingsRepo.findOne({ where: {} }).then(setSettings)
}, [refresh, term]), }, [refresh, term]),
) )
const renderItem = useCallback( const renderItem = useCallback(
({item}: {item: GymSet}) => ( ({ item }: { item: GymSet }) => (
<WorkoutItem <WorkoutItem
images={settings?.images} images={settings?.images}
item={item} item={item}
@ -71,7 +71,7 @@ export default function WorkoutList() {
const newWorkouts = await setRepo const newWorkouts = await setRepo
.createQueryBuilder() .createQueryBuilder()
.select() .select()
.where('name LIKE :name', {name: `%${term.trim()}%`}) .where('name LIKE :name', { name: `%${term.trim()}%` })
.groupBy('name') .groupBy('name')
.orderBy('name') .orderBy('name')
.limit(limit) .limit(limit)
@ -100,22 +100,24 @@ export default function WorkoutList() {
return ( return (
<> <>
<DrawerHeader name="Workouts" /> <DrawerHeader name='Workouts' />
<Page onAdd={onAdd} term={term} search={search}> <Page onAdd={onAdd} term={term} search={search}>
{workouts?.length === 0 ? ( {workouts?.length === 0
<List.Item ? (
title="No workouts yet." <List.Item
description="A workout is something you do at the gym. For example Deadlifts are a workout." title='No workouts yet.'
/> description='A workout is something you do at the gym. For example Deadlifts are a workout.'
) : ( />
<FlatList )
data={workouts} : (
style={{flex: 1}} <FlatList
renderItem={renderItem} data={workouts}
keyExtractor={w => w.name} style={{ flex: 1 }}
onEndReached={next} renderItem={renderItem}
/> keyExtractor={(w) => w.name}
)} onEndReached={next}
/>
)}
</Page> </Page>
</> </>
) )

View File

@ -1,4 +1,4 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import EditWorkout from './EditWorkout' import EditWorkout from './EditWorkout'
import GymSet from './gym-set' import GymSet from './gym-set'
import WorkoutList from './WorkoutList' import WorkoutList from './WorkoutList'
@ -15,9 +15,10 @@ const Stack = createStackNavigator<WorkoutsPageParams>()
export default function WorkoutsPage() { export default function WorkoutsPage() {
return ( return (
<Stack.Navigator <Stack.Navigator
screenOptions={{headerShown: false, animationEnabled: false}}> screenOptions={{ headerShown: false, animationEnabled: false }}
<Stack.Screen name="WorkoutList" component={WorkoutList} /> >
<Stack.Screen name="EditWorkout" component={EditWorkout} /> <Stack.Screen name='WorkoutList' component={WorkoutList} />
<Stack.Screen name='EditWorkout' component={EditWorkout} />
</Stack.Navigator> </Stack.Navigator>
) )
} }

View File

@ -1,4 +1,4 @@
import {setRepo} from './db' import { setRepo } from './db'
import GymSet from './gym-set' import GymSet from './gym-set'
export const getBestSet = async (name: string): Promise<GymSet> => { export const getBestSet = async (name: string): Promise<GymSet> => {
@ -6,7 +6,7 @@ export const getBestSet = async (name: string): Promise<GymSet> => {
.createQueryBuilder() .createQueryBuilder()
.select() .select()
.addSelect('MAX(weight)', 'weight') .addSelect('MAX(weight)', 'weight')
.where('name = :name', {name}) .where('name = :name', { name })
.groupBy('name') .groupBy('name')
.addGroupBy('reps') .addGroupBy('reps')
.orderBy('weight', 'DESC') .orderBy('weight', 'DESC')
@ -17,10 +17,10 @@ export const getBestSet = async (name: string): Promise<GymSet> => {
export const getLast = async (name: string): Promise<GymSet> => { export const getLast = async (name: string): Promise<GymSet> => {
return setRepo return setRepo
.createQueryBuilder() .createQueryBuilder()
.where('name = :name', {name}) .where('name = :name', { name })
.andWhere('reps >= 5') .andWhere('reps >= 5')
.andWhere("strftime('%Y-%m-%d', 'now', 'localtime') > created") .andWhere('strftime(\'%Y-%m-%d\', \'now\', \'localtime\') > created')
.groupBy("STRFTIME('%Y-%m-%d', created)") .groupBy('STRFTIME(\'%Y-%m-%d\', created)')
.orderBy('created', 'DESC') .orderBy('created', 'DESC')
.select('reps') .select('reps')
.addSelect('MAX(weight) as weight') .addSelect('MAX(weight) as weight')

View File

@ -1,25 +1,26 @@
import {DarkTheme, DefaultTheme} from 'react-native-paper' import { DarkTheme, DefaultTheme } from 'react-native-paper'
export const lightColors = [ export const lightColors = [
{hex: DarkTheme.colors.primary, name: 'Purple'}, { hex: DarkTheme.colors.primary, name: 'Purple' },
{hex: '#B3E5FC', name: 'Blue'}, { hex: '#B3E5FC', name: 'Blue' },
{hex: '#FA8072', name: 'Salmon'}, { hex: '#FA8072', name: 'Salmon' },
{hex: '#FFC0CB', name: 'Pink'}, { hex: '#FFC0CB', name: 'Pink' },
{hex: '#E9DCC9', name: 'Linen'}, { hex: '#E9DCC9', name: 'Linen' },
] ]
export const darkColors = [ export const darkColors = [
{hex: DefaultTheme.colors.primary, name: 'Purple'}, { hex: DefaultTheme.colors.primary, name: 'Purple' },
{hex: '#0051a9', name: 'Blue'}, { hex: '#0051a9', name: 'Blue' },
{hex: '#000000', name: 'Black'}, { hex: '#000000', name: 'Black' },
{hex: '#863c3c', name: 'Red'}, { hex: '#863c3c', name: 'Red' },
{hex: '#1c6000', name: 'Kermit'}, { hex: '#1c6000', name: 'Kermit' },
] ]
export const colorShade = (color: any, amount: number) => { export const colorShade = (color: any, amount: number) => {
color = color.replace(/^#/, '') color = color.replace(/^#/, '')
if (color.length === 3) if (color.length === 3) {
color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2] color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2]
}
let [r, g, b] = color.match(/.{2}/g) let [r, g, b] = color.match(/.{2}/g)
;[r, g, b] = [ ;[r, g, b] = [

View File

@ -1,31 +1,31 @@
import {DataSource} from 'typeorm' import { DataSource } from 'typeorm'
import GymSet from './gym-set' import GymSet from './gym-set'
import {Sets1667185586014 as sets1667185586014} from './migrations/1667185586014-sets' import { Sets1667185586014 as sets1667185586014 } from './migrations/1667185586014-sets'
import {plans1667186124792} from './migrations/1667186124792-plans' import { plans1667186124792 } from './migrations/1667186124792-plans'
import {settings1667186130041} from './migrations/1667186130041-settings' import { settings1667186130041 } from './migrations/1667186130041-settings'
import {addSound1667186139844} from './migrations/1667186139844-add-sound' import { addSound1667186139844 } from './migrations/1667186139844-add-sound'
import {addHidden1667186159379} from './migrations/1667186159379-add-hidden' import { addHidden1667186159379 } from './migrations/1667186159379-add-hidden'
import {addNotify1667186166140} from './migrations/1667186166140-add-notify' import { addNotify1667186166140 } from './migrations/1667186166140-add-notify'
import {addImage1667186171548} from './migrations/1667186171548-add-image' import { addImage1667186171548 } from './migrations/1667186171548-add-image'
import {addImages1667186179488} from './migrations/1667186179488-add-images' import { addImages1667186179488 } from './migrations/1667186179488-add-images'
import {insertSettings1667186203827} from './migrations/1667186203827-insert-settings' import { insertSettings1667186203827 } from './migrations/1667186203827-insert-settings'
import {addSteps1667186211251} from './migrations/1667186211251-add-steps' import { addSteps1667186211251 } from './migrations/1667186211251-add-steps'
import {addSets1667186250618} from './migrations/1667186250618-add-sets' import { addSets1667186250618 } from './migrations/1667186250618-add-sets'
import {addMinutes1667186255650} from './migrations/1667186255650-add-minutes' import { addMinutes1667186255650 } from './migrations/1667186255650-add-minutes'
import {addSeconds1667186259174} from './migrations/1667186259174-add-seconds' import { addSeconds1667186259174 } from './migrations/1667186259174-add-seconds'
import {addShowUnit1667186265588} from './migrations/1667186265588-add-show-unit' import { addShowUnit1667186265588 } from './migrations/1667186265588-add-show-unit'
import {addColor1667186320954} from './migrations/1667186320954-add-color' import { addColor1667186320954 } from './migrations/1667186320954-add-color'
import {addSteps1667186348425} from './migrations/1667186348425-add-steps' import { addSteps1667186348425 } from './migrations/1667186348425-add-steps'
import {addDate1667186431804} from './migrations/1667186431804-add-date' import { addDate1667186431804 } from './migrations/1667186431804-add-date'
import {addShowDate1667186435051} from './migrations/1667186435051-add-show-date' import { addShowDate1667186435051 } from './migrations/1667186435051-add-show-date'
import {addTheme1667186439366} from './migrations/1667186439366-add-theme' import { addTheme1667186439366 } from './migrations/1667186439366-add-theme'
import {addShowSets1667186443614} from './migrations/1667186443614-add-show-sets' import { addShowSets1667186443614 } from './migrations/1667186443614-add-show-sets'
import {addSetsCreated1667186451005} from './migrations/1667186451005-add-sets-created' import { addSetsCreated1667186451005 } from './migrations/1667186451005-add-sets-created'
import {addNoSound1667186456118} from './migrations/1667186456118-add-no-sound' import { addNoSound1667186456118 } from './migrations/1667186456118-add-no-sound'
import {dropMigrations1667190214743} from './migrations/1667190214743-drop-migrations' import { dropMigrations1667190214743 } from './migrations/1667190214743-drop-migrations'
import {splitColor1669420187764} from './migrations/1669420187764-split-color' import { splitColor1669420187764 } from './migrations/1669420187764-split-color'
import {addBackup1678334268359} from './migrations/1678334268359-add-backup' import { addBackup1678334268359 } from './migrations/1678334268359-add-backup'
import {Plan} from './plan' import { Plan } from './plan'
import Settings from './settings' import Settings from './settings'
export const AppDataSource = new DataSource({ export const AppDataSource = new DataSource({

6
db.ts
View File

@ -1,6 +1,6 @@
import {AppDataSource} from './data-source' import { AppDataSource } from './data-source'
import GymSet from './gym-set' import GymSet from './gym-set'
import {Plan} from './plan' import { Plan } from './plan'
import Settings from './settings' import Settings from './settings'
export const setRepo = AppDataSource.manager.getRepository(GymSet) export const setRepo = AppDataSource.manager.getRepository(GymSet)
@ -9,7 +9,7 @@ export const settingsRepo = AppDataSource.manager.getRepository(Settings)
export const getNow = async (): Promise<string> => { export const getNow = async (): Promise<string> => {
const query = await AppDataSource.manager.query( const query = await AppDataSource.manager.query(
"SELECT STRFTIME('%Y-%m-%dT%H:%M:%S','now','localtime') AS now", 'SELECT STRFTIME(\'%Y-%m-%dT%H:%M:%S\',\'now\',\'localtime\') AS now',
) )
return query[0].now return query[0].now
} }

11
deno.json Normal file
View File

@ -0,0 +1,11 @@
{
"fmt": {
"useTabs": false,
"lineWidth": 80,
"semiColons": false,
"singleQuote": true,
"proseWrap": "preserve",
"include": ["src/"],
"exclude": ["src/testdata/", "data/fixtures/**/*.ts"]
}
}

View File

@ -1,4 +1,4 @@
import {Column, Entity, PrimaryGeneratedColumn} from 'typeorm' import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@Entity('sets') @Entity('sets')
export default class GymSet { export default class GymSet {

View File

@ -1,4 +1,4 @@
import {Item} from './Select' import { Item } from './Select'
import Settings from './settings' import Settings from './settings'
export default interface Input<T> { export default interface Input<T> {

View File

@ -1,4 +1,4 @@
import {NativeModules} from 'react-native' import { NativeModules } from 'react-native'
import 'react-native-gesture-handler/jestSetup' import 'react-native-gesture-handler/jestSetup'
NativeModules.RNViewShot = NativeModules.RNViewShot || { NativeModules.RNViewShot = NativeModules.RNViewShot || {

View File

@ -1,4 +1,4 @@
import {NavigationContainer} from '@react-navigation/native' import { NavigationContainer } from '@react-navigation/native'
import React from 'react' import React from 'react'
import { import {
DarkTheme, DarkTheme,
@ -6,14 +6,14 @@ import {
Provider as PaperProvider, Provider as PaperProvider,
} from 'react-native-paper' } from 'react-native-paper'
import MaterialIcon from 'react-native-vector-icons/MaterialIcons' import MaterialIcon from 'react-native-vector-icons/MaterialIcons'
import {ThemeContext} from './use-theme' import { ThemeContext } from './use-theme'
export const MockProviders = ({ export const MockProviders = ({
children, children,
}: { }: {
children: JSX.Element | JSX.Element[] children: JSX.Element | JSX.Element[]
}) => ( }) => (
<PaperProvider settings={{icon: props => <MaterialIcon {...props} />}}> <PaperProvider settings={{ icon: (props) => <MaterialIcon {...props} /> }}>
<ThemeContext.Provider <ThemeContext.Provider
value={{ value={{
theme: 'system', theme: 'system',
@ -22,7 +22,8 @@ export const MockProviders = ({
darkColor: DarkTheme.colors.primary, darkColor: DarkTheme.colors.primary,
setLightColor: jest.fn(), setLightColor: jest.fn(),
setDarkColor: jest.fn(), setDarkColor: jest.fn(),
}}> }}
>
<NavigationContainer>{children}</NavigationContainer> <NavigationContainer>{children}</NavigationContainer>
</ThemeContext.Provider> </ThemeContext.Provider>
</PaperProvider> </PaperProvider>

View File

@ -1,18 +1,18 @@
import {darkColors, lightColors} from './colors' import { darkColors, lightColors } from './colors'
export const themeOptions = [ export const themeOptions = [
{label: 'System', value: 'system'}, { label: 'System', value: 'system' },
{label: 'Dark', value: 'dark'}, { label: 'Dark', value: 'dark' },
{label: 'Light', value: 'light'}, { label: 'Light', value: 'light' },
] ]
export const lightOptions = lightColors.map(color => ({ export const lightOptions = lightColors.map((color) => ({
label: color.name, label: color.name,
value: color.hex, value: color.hex,
color: color.hex, color: color.hex,
})) }))
export const darkOptions = darkColors.map(color => ({ export const darkOptions = darkColors.map((color) => ({
label: color.name, label: color.name,
value: color.hex, value: color.hex,
color: color.hex, color: color.hex,

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
organize-imports-cli *.ts* tests/*.ts* && prettier --write *.ts* tests/*.ts* organize-imports-cli *.ts* tests/*.ts* && deno fmt *.ts* tests/*.ts*

View File

@ -1,5 +1,5 @@
import GymSet from './gym-set' import GymSet from './gym-set'
import {Plan} from './plan' import { Plan } from './plan'
export type PlanPageParams = { export type PlanPageParams = {
PlanList: {} PlanList: {}

View File

@ -1,4 +1,4 @@
import {Column, Entity, PrimaryGeneratedColumn} from 'typeorm' import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@Entity('plans') @Entity('plans')
export class Plan { export class Plan {

View File

@ -1,4 +1,4 @@
import {DrawerParamList} from './drawer-param-list' import { DrawerParamList } from './drawer-param-list'
export default interface Route { export default interface Route {
name: keyof DrawerParamList name: keyof DrawerParamList

View File

@ -1,4 +1,4 @@
import {Column, Entity, PrimaryColumn} from 'typeorm' import { Column, Entity, PrimaryColumn } from 'typeorm'
@Entity() @Entity()
export default class Settings { export default class Settings {

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import App from '../App' import App from '../App'
import Settings from '../settings' import Settings from '../settings'
@ -18,7 +18,7 @@ jest.mock('../data-source.ts', () => ({
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getAllByText} = render(<App />) const { getAllByText } = render(<App />)
const title = await waitFor(() => getAllByText('Home')) const title = await waitFor(() => getAllByText('Home'))
expect(title.length).toBeGreaterThan(0) expect(title.length).toBeGreaterThan(0)
}) })

View File

@ -1,8 +1,8 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {fireEvent, render, waitFor} from 'react-native-testing-library' import { fireEvent, render, waitFor } from 'react-native-testing-library'
import BestPage from '../BestPage' import BestPage from '../BestPage'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import Settings from '../settings' import Settings from '../settings'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
@ -34,17 +34,17 @@ jest.mock('../db.ts', () => ({
reps: 10, reps: 10,
image: 'https://picsum.photos/id/1/1000/600', image: 'https://picsum.photos/id/1/1000/600',
}, },
]), ])
), ),
}), }),
}, },
settingsRepo: { settingsRepo: {
findOne: () => Promise.resolve({images: true} as Settings), findOne: () => Promise.resolve({ images: true } as Settings),
}, },
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getByText} = render( const { getByText } = render(
<MockProviders> <MockProviders>
<BestPage /> <BestPage />
</MockProviders>, </MockProviders>,
@ -54,7 +54,7 @@ test('renders correctly', async () => {
}) })
test('searches', async () => { test('searches', async () => {
const {getByDisplayValue, getByPlaceholder} = render( const { getByDisplayValue, getByPlaceholder } = render(
<MockProviders> <MockProviders>
<BestPage /> <BestPage />
</MockProviders>, </MockProviders>,

View File

@ -1,11 +1,11 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import EditPlan from '../EditPlan' import EditPlan from '../EditPlan'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import {Plan} from '../plan' import { Plan } from '../plan'
import {PlanPageParams} from '../plan-page-params' import { PlanPageParams } from '../plan-page-params'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
setRepo: { setRepo: {
@ -15,10 +15,10 @@ jest.mock('../db.ts', () => ({
orderBy: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(),
getRawMany: jest.fn(() => getRawMany: jest.fn(() =>
Promise.resolve([ Promise.resolve([
{name: 'Bench press'}, { name: 'Bench press' },
{name: 'Bicep curls'}, { name: 'Bicep curls' },
{name: 'Rows'}, { name: 'Rows' },
]), ])
), ),
}), }),
}, },
@ -26,7 +26,7 @@ jest.mock('../db.ts', () => ({
test('renders correctly', async () => { test('renders correctly', async () => {
const Stack = createStackNavigator<PlanPageParams>() const Stack = createStackNavigator<PlanPageParams>()
const {getByText, getAllByText} = render( const { getByText, getAllByText } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
@ -37,7 +37,7 @@ test('renders correctly', async () => {
id: 1, id: 1,
} as Plan, } as Plan,
}} }}
name="EditPlan" name='EditPlan'
component={EditPlan} component={EditPlan}
/> />
</Stack.Navigator> </Stack.Navigator>

View File

@ -1,11 +1,11 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {fireEvent, render, waitFor} from 'react-native-testing-library' import { fireEvent, render, waitFor } from 'react-native-testing-library'
import EditSet from '../EditSet' import EditSet from '../EditSet'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import {HomePageParams} from '../home-page-params' import { HomePageParams } from '../home-page-params'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import SetList from '../SetList' import SetList from '../SetList'
import Settings from '../settings' import Settings from '../settings'
@ -27,7 +27,7 @@ jest.mock('../db.ts', () => ({
test('renders correctly', async () => { test('renders correctly', async () => {
const Stack = createStackNavigator<HomePageParams>() const Stack = createStackNavigator<HomePageParams>()
const {getByText, getAllByText} = render( const { getByText, getAllByText } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
@ -37,7 +37,7 @@ test('renders correctly', async () => {
id: 1, id: 1,
} as GymSet, } as GymSet,
}} }}
name="EditSet" name='EditSet'
component={EditSet} component={EditSet}
/> />
</Stack.Navigator> </Stack.Navigator>
@ -55,10 +55,10 @@ test('renders correctly', async () => {
test('saves', async () => { test('saves', async () => {
const Stack = createStackNavigator<HomePageParams>() const Stack = createStackNavigator<HomePageParams>()
const {getByText, getAllByText, getByTestId} = render( const { getByText, getAllByText, getByTestId } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen name="Sets" component={SetList} /> <Stack.Screen name='Sets' component={SetList} />
<Stack.Screen <Stack.Screen
initialParams={{ initialParams={{
set: { set: {
@ -66,7 +66,7 @@ test('saves', async () => {
id: 1, id: 1,
} as GymSet, } as GymSet,
}} }}
name="EditSet" name='EditSet'
component={EditSet} component={EditSet}
/> />
</Stack.Navigator> </Stack.Navigator>

View File

@ -1,10 +1,10 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {fireEvent, render, waitFor} from 'react-native-testing-library' import { fireEvent, render, waitFor } from 'react-native-testing-library'
import EditSets from '../EditSets' import EditSets from '../EditSets'
import {HomePageParams} from '../home-page-params' import { HomePageParams } from '../home-page-params'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
const mockGoBack = jest.fn() const mockGoBack = jest.fn()
@ -20,9 +20,9 @@ jest.mock('../db.ts', () => ({
setRepo: { setRepo: {
find: () => find: () =>
Promise.resolve([ Promise.resolve([
{name: 'Bench press', reps: 8, weight: 60, id: 1}, { name: 'Bench press', reps: 8, weight: 60, id: 1 },
{name: 'Bench press', reps: 6, weight: 70, id: 2}, { name: 'Bench press', reps: 6, weight: 70, id: 2 },
{name: 'Bench press', reps: 4, weight: 85, id: 3}, { name: 'Bench press', reps: 4, weight: 85, id: 3 },
]), ]),
update: jest.fn(() => Promise.resolve()), update: jest.fn(() => Promise.resolve()),
}, },
@ -38,12 +38,12 @@ jest.mock('../db.ts', () => ({
test('renders correctly', async () => { test('renders correctly', async () => {
const Stack = createStackNavigator<HomePageParams>() const Stack = createStackNavigator<HomePageParams>()
const {getByText, getAllByText} = render( const { getByText, getAllByText } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
initialParams={{ids: [1, 2, 3]}} initialParams={{ ids: [1, 2, 3] }}
name="EditSets" name='EditSets'
component={EditSets} component={EditSets}
/> />
</Stack.Navigator> </Stack.Navigator>
@ -60,12 +60,12 @@ test('renders correctly', async () => {
test('saves', async () => { test('saves', async () => {
const Stack = createStackNavigator<HomePageParams>() const Stack = createStackNavigator<HomePageParams>()
const {getByText, getAllByText} = render( const { getByText, getAllByText } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
initialParams={{ids: [1, 2, 3]}} initialParams={{ ids: [1, 2, 3] }}
name="EditSets" name='EditSets'
component={EditSets} component={EditSets}
/> />
</Stack.Navigator> </Stack.Navigator>

View File

@ -1,12 +1,12 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import EditWorkout from '../EditWorkout' import EditWorkout from '../EditWorkout'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import Settings from '../settings' import Settings from '../settings'
import {WorkoutsPageParams} from '../WorkoutsPage' import { WorkoutsPageParams } from '../WorkoutsPage'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
settingsRepo: { settingsRepo: {
@ -20,14 +20,14 @@ jest.mock('../db.ts', () => ({
test('renders correctly', async () => { test('renders correctly', async () => {
const Stack = createStackNavigator<WorkoutsPageParams>() const Stack = createStackNavigator<WorkoutsPageParams>()
const {getByText, getAllByText} = render( const { getByText, getAllByText } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
initialParams={{ initialParams={{
value: {name: 'Bench press'} as GymSet, value: { name: 'Bench press' } as GymSet,
}} }}
name="EditWorkout" name='EditWorkout'
component={EditWorkout} component={EditWorkout}
/> />
</Stack.Navigator> </Stack.Navigator>

View File

@ -1,21 +1,21 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import {Repository} from 'typeorm' import { Repository } from 'typeorm'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import HomePage from '../HomePage' import HomePage from '../HomePage'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import Settings from '../settings' import Settings from '../settings'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>, setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
settingsRepo: { settingsRepo: {
findOne: () => Promise.resolve({} as Settings), findOne: () => Promise.resolve({} as Settings),
}, },
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getByText} = render( const { getByText } = render(
<MockProviders> <MockProviders>
<HomePage /> <HomePage />
</MockProviders>, </MockProviders>,

View File

@ -1,8 +1,8 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {fireEvent, render, waitFor} from 'react-native-testing-library' import { fireEvent, render, waitFor } from 'react-native-testing-library'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import {Plan} from '../plan' import { Plan } from '../plan'
import PlanPage from '../PlanPage' import PlanPage from '../PlanPage'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
@ -26,7 +26,7 @@ jest.mock('../db.ts', () => ({
{ {
name: 'Rows', name: 'Rows',
}, },
]), ])
), ),
}), }),
}, },
@ -48,7 +48,7 @@ jest.mock('../db.ts', () => ({
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getByText} = render( const { getByText } = render(
<MockProviders> <MockProviders>
<PlanPage /> <PlanPage />
</MockProviders>, </MockProviders>,
@ -58,7 +58,7 @@ test('renders correctly', async () => {
}) })
test('adds', async () => { test('adds', async () => {
const {getByTestId, getByText} = render( const { getByTestId, getByText } = render(
<MockProviders> <MockProviders>
<PlanPage /> <PlanPage />
</MockProviders>, </MockProviders>,

View File

@ -1,21 +1,21 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import {Repository} from 'typeorm' import { Repository } from 'typeorm'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import PlanPage from '../PlanPage' import PlanPage from '../PlanPage'
import Settings from '../settings' import Settings from '../settings'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>, setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
settingsRepo: { settingsRepo: {
findOne: () => Promise.resolve({} as Settings), findOne: () => Promise.resolve({} as Settings),
}, },
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getByText} = render( const { getByText } = render(
<MockProviders> <MockProviders>
<PlanPage /> <PlanPage />
</MockProviders>, </MockProviders>,

View File

@ -1,21 +1,21 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import {Repository} from 'typeorm' import { Repository } from 'typeorm'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import Settings from '../settings' import Settings from '../settings'
import SettingsPage from '../SettingsPage' import SettingsPage from '../SettingsPage'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>, setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
settingsRepo: { settingsRepo: {
findOne: () => Promise.resolve({} as Settings), findOne: () => Promise.resolve({} as Settings),
}, },
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getByText, getAllByText} = render( const { getByText, getAllByText } = render(
<MockProviders> <MockProviders>
<SettingsPage /> <SettingsPage />
</MockProviders>, </MockProviders>,

View File

@ -1,11 +1,11 @@
import {createStackNavigator} from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {fireEvent, render, waitFor} from 'react-native-testing-library' import { fireEvent, render, waitFor } from 'react-native-testing-library'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import {Plan} from '../plan' import { Plan } from '../plan'
import {PlanPageParams} from '../plan-page-params' import { PlanPageParams } from '../plan-page-params'
import Settings from '../settings' import Settings from '../settings'
import StartPlan from '../StartPlan' import StartPlan from '../StartPlan'
@ -34,10 +34,10 @@ jest.mock('../data-source.ts', () => ({
manager: { manager: {
query: jest.fn(() => query: jest.fn(() =>
Promise.resolve([ Promise.resolve([
{name: 'Bench', total: 0}, { name: 'Bench', total: 0 },
{name: 'Rows', total: 0}, { name: 'Rows', total: 0 },
{name: 'Curls', total: 0}, { name: 'Curls', total: 0 },
]), ])
), ),
}, },
}, },
@ -45,18 +45,18 @@ jest.mock('../data-source.ts', () => ({
test('renders correctly', async () => { test('renders correctly', async () => {
const Stack = createStackNavigator<PlanPageParams>() const Stack = createStackNavigator<PlanPageParams>()
const {getByText, getAllByText} = render( const { getByText, getAllByText } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
initialParams={{ initialParams={{
first: {reps: 0, weight: 0} as GymSet, first: { reps: 0, weight: 0 } as GymSet,
plan: { plan: {
workouts: 'Bench,Rows,Curls', workouts: 'Bench,Rows,Curls',
days: 'Monday,Tuesday,Thursday', days: 'Monday,Tuesday,Thursday',
} as Plan, } as Plan,
}} }}
name="StartPlan" name='StartPlan'
component={StartPlan} component={StartPlan}
/> />
</Stack.Navigator> </Stack.Navigator>
@ -75,18 +75,18 @@ test('renders correctly', async () => {
test('saves', async () => { test('saves', async () => {
const Stack = createStackNavigator<PlanPageParams>() const Stack = createStackNavigator<PlanPageParams>()
const {getByText} = render( const { getByText } = render(
<MockProviders> <MockProviders>
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
initialParams={{ initialParams={{
first: {reps: 0, weight: 0} as GymSet, first: { reps: 0, weight: 0 } as GymSet,
plan: { plan: {
workouts: 'Bench,Rows,Curls', workouts: 'Bench,Rows,Curls',
days: 'Monday,Tuesday,Thursday', days: 'Monday,Tuesday,Thursday',
} as Plan, } as Plan,
}} }}
name="StartPlan" name='StartPlan'
component={StartPlan} component={StartPlan}
/> />
</Stack.Navigator> </Stack.Navigator>

View File

@ -1,21 +1,21 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import {Repository} from 'typeorm' import { Repository } from 'typeorm'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import Settings from '../settings' import Settings from '../settings'
import TimerPage from '../TimerPage' import TimerPage from '../TimerPage'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>, setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
settingsRepo: { settingsRepo: {
findOne: () => Promise.resolve({} as Settings), findOne: () => Promise.resolve({} as Settings),
}, },
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getByText} = render( const { getByText } = render(
<MockProviders> <MockProviders>
<TimerPage /> <TimerPage />
</MockProviders>, </MockProviders>,

View File

@ -1,8 +1,8 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {fireEvent, render, waitFor} from 'react-native-testing-library' import { fireEvent, render, waitFor } from 'react-native-testing-library'
import BestPage from '../BestPage' import BestPage from '../BestPage'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import Settings from '../settings' import Settings from '../settings'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
@ -35,7 +35,7 @@ jest.mock('../db.ts', () => ({
created: '2021-01-05T03:58:02.565Z', created: '2021-01-05T03:58:02.565Z',
weight: 28, weight: 28,
}, },
]), ])
), ),
getMany: jest.fn(() => getMany: jest.fn(() =>
Promise.resolve([ Promise.resolve([
@ -57,17 +57,17 @@ jest.mock('../db.ts', () => ({
reps: 10, reps: 10,
image: 'https://picsum.photos/id/1/1000/600', image: 'https://picsum.photos/id/1/1000/600',
}, },
]), ])
), ),
}), }),
}, },
settingsRepo: { settingsRepo: {
findOne: () => Promise.resolve({images: true} as Settings), findOne: () => Promise.resolve({ images: true } as Settings),
}, },
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getAllByText, getByText} = render( const { getAllByText, getByText } = render(
<MockProviders> <MockProviders>
<BestPage /> <BestPage />
</MockProviders>, </MockProviders>,
@ -81,7 +81,7 @@ test('renders correctly', async () => {
}) })
test('volume', async () => { test('volume', async () => {
const {getAllByText, getByText} = render( const { getAllByText, getByText } = render(
<MockProviders> <MockProviders>
<BestPage /> <BestPage />
</MockProviders>, </MockProviders>,
@ -97,7 +97,7 @@ test('volume', async () => {
}) })
test('one rep max', async () => { test('one rep max', async () => {
const {getAllByText, getByText} = render( const { getAllByText, getByText } = render(
<MockProviders> <MockProviders>
<BestPage /> <BestPage />
</MockProviders>, </MockProviders>,
@ -113,7 +113,7 @@ test('one rep max', async () => {
}) })
test('this week', async () => { test('this week', async () => {
const {getAllByText, getByText} = render( const { getAllByText, getByText } = render(
<MockProviders> <MockProviders>
<BestPage /> <BestPage />
</MockProviders>, </MockProviders>,
@ -127,7 +127,7 @@ test('this week', async () => {
}) })
test('this year', async () => { test('this year', async () => {
const {getAllByText, getByText} = render( const { getAllByText, getByText } = render(
<MockProviders> <MockProviders>
<BestPage /> <BestPage />
</MockProviders>, </MockProviders>,

View File

@ -1,21 +1,21 @@
import React from 'react' import React from 'react'
import 'react-native' import 'react-native'
import {render, waitFor} from 'react-native-testing-library' import { render, waitFor } from 'react-native-testing-library'
import {Repository} from 'typeorm' import { Repository } from 'typeorm'
import GymSet from '../gym-set' import GymSet from '../gym-set'
import {MockProviders} from '../mock-providers' import { MockProviders } from '../mock-providers'
import Settings from '../settings' import Settings from '../settings'
import WorkoutsPage from '../WorkoutsPage' import WorkoutsPage from '../WorkoutsPage'
jest.mock('../db.ts', () => ({ jest.mock('../db.ts', () => ({
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>, setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
settingsRepo: { settingsRepo: {
findOne: () => Promise.resolve({} as Settings), findOne: () => Promise.resolve({} as Settings),
}, },
})) }))
test('renders correctly', async () => { test('renders correctly', async () => {
const {getByText} = render( const { getByText } = render(
<MockProviders> <MockProviders>
<WorkoutsPage /> <WorkoutsPage />
</MockProviders>, </MockProviders>,

View File

@ -1,7 +1,7 @@
import {DeviceEventEmitter} from 'react-native' import { DeviceEventEmitter } from 'react-native'
export const TOAST = 'toast' export const TOAST = 'toast'
export function toast(value: string) { export function toast(value: string) {
DeviceEventEmitter.emit(TOAST, {value}) DeviceEventEmitter.emit(TOAST, { value })
} }

View File

@ -1,9 +1,9 @@
import {useColorScheme} from 'react-native' import { useColorScheme } from 'react-native'
import {useTheme} from './use-theme' import { useTheme } from './use-theme'
export default function useDark() { export default function useDark() {
const dark = useColorScheme() === 'dark' const dark = useColorScheme() === 'dark'
const {theme} = useTheme() const { theme } = useTheme()
if (theme === 'dark') return true if (theme === 'dark') return true
if (theme === 'light') return false if (theme === 'light') return false

View File

@ -1,5 +1,5 @@
import {createContext, useContext} from 'react' import { createContext, useContext } from 'react'
import {DarkTheme, DefaultTheme} from 'react-native-paper' import { DarkTheme, DefaultTheme } from 'react-native-paper'
export const ThemeContext = createContext<{ export const ThemeContext = createContext<{
theme: string theme: string

View File

@ -1,7 +1,7 @@
import {useFocusEffect} from '@react-navigation/native' import { useFocusEffect } from '@react-navigation/native'
import {useCallback, useState} from 'react' import { useCallback, useState } from 'react'
import {NativeEventEmitter} from 'react-native' import { NativeEventEmitter } from 'react-native'
import {TickEvent} from './TimerPage' import { TickEvent } from './TimerPage'
export default function useTimer() { export default function useTimer() {
const [minutes, setMinutes] = useState('00') const [minutes, setMinutes] = useState('00')
@ -13,7 +13,7 @@ export default function useTimer() {
setSeconds('00') setSeconds('00')
const emitter = new NativeEventEmitter() const emitter = new NativeEventEmitter()
const listener = emitter.addListener('tick', (event: TickEvent) => { const listener = emitter.addListener('tick', (event: TickEvent) => {
console.log(`${useTimer.name}.tick:`, {event}) console.log(`${useTimer.name}.tick:`, { event })
setMinutes(event.minutes) setMinutes(event.minutes)
setSeconds(event.seconds) setSeconds(event.seconds)
}) })
@ -21,5 +21,5 @@ export default function useTimer() {
}, []), }, []),
) )
return {minutes, seconds} return { minutes, seconds }
} }

View File

@ -1,6 +1,6 @@
import {PermissionsAndroid, Platform} from 'react-native' import { PermissionsAndroid, Platform } from 'react-native'
import {Dirs, FileSystem} from 'react-native-file-access' import { Dirs, FileSystem } from 'react-native-file-access'
import {toast} from './toast' import { toast } from './toast'
export const write = async (name: string, data: string) => { export const write = async (name: string, data: string) => {
const filePath = `${Dirs.DocumentDir}/${name}` const filePath = `${Dirs.DocumentDir}/${name}`
@ -14,7 +14,8 @@ export const write = async (name: string, data: string) => {
const granted = await permission() const granted = await permission()
if (!granted) return if (!granted) return
await FileSystem.writeFile(filePath, data) await FileSystem.writeFile(filePath, data)
if (Platform.OS === 'android') if (Platform.OS === 'android') {
await FileSystem.cpExternal(filePath, name, 'downloads') await FileSystem.cpExternal(filePath, name, 'downloads')
}
toast(`Downloaded ${name}`) toast(`Downloaded ${name}`)
} }