Use deno fmt instead of prettier
This commit is contained in:
parent
23ed95dcdb
commit
4303fe2cc4
|
@ -1,8 +0,0 @@
|
|||
module.exports = {
|
||||
arrowParens: 'avoid',
|
||||
bracketSameLine: true,
|
||||
bracketSpacing: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
semi: false,
|
||||
};
|
29
App.tsx
29
App.tsx
|
@ -3,8 +3,8 @@ import {
|
|||
DefaultTheme as NavigationDefaultTheme,
|
||||
NavigationContainer,
|
||||
} from '@react-navigation/native'
|
||||
import React, {useEffect, useMemo, useState} from 'react'
|
||||
import {DeviceEventEmitter, useColorScheme} from 'react-native'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { DeviceEventEmitter, useColorScheme } from 'react-native'
|
||||
import {
|
||||
DarkTheme as PaperDarkTheme,
|
||||
DefaultTheme as PaperDefaultTheme,
|
||||
|
@ -12,11 +12,11 @@ import {
|
|||
Snackbar,
|
||||
} from 'react-native-paper'
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons'
|
||||
import {AppDataSource} from './data-source'
|
||||
import {settingsRepo} from './db'
|
||||
import { AppDataSource } from './data-source'
|
||||
import { settingsRepo } from './db'
|
||||
import Routes from './Routes'
|
||||
import {TOAST} from './toast'
|
||||
import {ThemeContext} from './use-theme'
|
||||
import { TOAST } from './toast'
|
||||
import { ThemeContext } from './use-theme'
|
||||
|
||||
export const CombinedDefaultTheme = {
|
||||
...NavigationDefaultTheme,
|
||||
|
@ -53,7 +53,7 @@ const App = () => {
|
|||
useEffect(() => {
|
||||
;(async () => {
|
||||
if (!AppDataSource.isInitialized) await AppDataSource.initialize()
|
||||
const settings = await settingsRepo.findOne({where: {}})
|
||||
const settings = await settingsRepo.findOne({ where: {} })
|
||||
setTheme(settings.theme)
|
||||
if (settings.lightColor) setLightColor(settings.lightColor)
|
||||
if (settings.darkColor) setDarkColor(settings.darkColor)
|
||||
|
@ -61,7 +61,7 @@ const App = () => {
|
|||
})()
|
||||
const description = DeviceEventEmitter.addListener(
|
||||
TOAST,
|
||||
({value}: {value: string}) => {
|
||||
({ value }: { value: string }) => {
|
||||
setSnackbar(value)
|
||||
},
|
||||
)
|
||||
|
@ -72,13 +72,13 @@ const App = () => {
|
|||
const darkTheme = lightColor
|
||||
? {
|
||||
...CombinedDarkTheme,
|
||||
colors: {...CombinedDarkTheme.colors, primary: darkColor},
|
||||
colors: { ...CombinedDarkTheme.colors, primary: darkColor },
|
||||
}
|
||||
: CombinedDarkTheme
|
||||
const lightTheme = lightColor
|
||||
? {
|
||||
...CombinedDefaultTheme,
|
||||
colors: {...CombinedDefaultTheme.colors, primary: lightColor},
|
||||
colors: { ...CombinedDefaultTheme.colors, primary: lightColor },
|
||||
}
|
||||
: CombinedDefaultTheme
|
||||
let value = isDark ? darkTheme : lightTheme
|
||||
|
@ -99,7 +99,8 @@ const App = () => {
|
|||
return (
|
||||
<PaperProvider
|
||||
theme={paperTheme}
|
||||
settings={{icon: props => <MaterialIcon {...props} />}}>
|
||||
settings={{ icon: (props) => <MaterialIcon {...props} /> }}
|
||||
>
|
||||
<NavigationContainer theme={paperTheme}>
|
||||
{initialized && (
|
||||
<ThemeContext.Provider
|
||||
|
@ -110,7 +111,8 @@ const App = () => {
|
|||
setLightColor,
|
||||
darkColor,
|
||||
setDarkColor,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Routes />
|
||||
</ThemeContext.Provider>
|
||||
)}
|
||||
|
@ -120,7 +122,8 @@ const App = () => {
|
|||
duration={3000}
|
||||
onDismiss={() => setSnackbar('')}
|
||||
visible={!!snackbar}
|
||||
action={action}>
|
||||
action={action}
|
||||
>
|
||||
{snackbar}
|
||||
</Snackbar>
|
||||
</PaperProvider>
|
||||
|
|
16
AppFab.tsx
16
AppFab.tsx
|
@ -1,14 +1,14 @@
|
|||
import {ComponentProps, useMemo} from 'react'
|
||||
import {FAB, useTheme} from 'react-native-paper'
|
||||
import {CombinedDarkTheme, CombinedDefaultTheme} from './App'
|
||||
import {lightColors} from './colors'
|
||||
import { ComponentProps, useMemo } from 'react'
|
||||
import { FAB, useTheme } from 'react-native-paper'
|
||||
import { CombinedDarkTheme, CombinedDefaultTheme } from './App'
|
||||
import { lightColors } from './colors'
|
||||
|
||||
export default function AppFab(props: Partial<ComponentProps<typeof FAB>>) {
|
||||
const {colors} = useTheme()
|
||||
const { colors } = useTheme()
|
||||
|
||||
const fabColor = useMemo(
|
||||
() =>
|
||||
lightColors.map(color => color.hex).includes(colors.primary)
|
||||
lightColors.map((color) => color.hex).includes(colors.primary)
|
||||
? CombinedDarkTheme.colors.background
|
||||
: CombinedDefaultTheme.colors.background,
|
||||
[colors.primary],
|
||||
|
@ -16,8 +16,8 @@ export default function AppFab(props: Partial<ComponentProps<typeof FAB>>) {
|
|||
|
||||
return (
|
||||
<FAB
|
||||
icon="add"
|
||||
testID="add"
|
||||
icon='add'
|
||||
testID='add'
|
||||
color={fabColor}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
|
|
10
AppInput.tsx
10
AppInput.tsx
|
@ -1,7 +1,7 @@
|
|||
import React, {ComponentProps, Ref} from 'react'
|
||||
import {TextInput} from 'react-native-paper'
|
||||
import {CombinedDefaultTheme} from './App'
|
||||
import {MARGIN} from './constants'
|
||||
import React, { ComponentProps, Ref } from 'react'
|
||||
import { TextInput } from 'react-native-paper'
|
||||
import { CombinedDefaultTheme } from './App'
|
||||
import { MARGIN } from './constants'
|
||||
import useDark from './use-dark'
|
||||
|
||||
function AppInput(
|
||||
|
@ -14,7 +14,7 @@ function AppInput(
|
|||
return (
|
||||
<TextInput
|
||||
selectionColor={dark ? '#2A2A2A' : CombinedDefaultTheme.colors.border}
|
||||
style={{marginBottom: MARGIN, minWidth: 100}}
|
||||
style={{ marginBottom: MARGIN, minWidth: 100 }}
|
||||
selectTextOnFocus
|
||||
ref={props.innerRef}
|
||||
blurOnSubmit={false}
|
||||
|
|
50
BestList.tsx
50
BestList.tsx
|
@ -3,11 +3,11 @@ import {
|
|||
useFocusEffect,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useState} from 'react'
|
||||
import {FlatList, Image} from 'react-native'
|
||||
import {List} from 'react-native-paper'
|
||||
import {BestPageParams} from './BestPage'
|
||||
import {setRepo, settingsRepo} from './db'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { FlatList, Image } from 'react-native'
|
||||
import { List } from 'react-native-paper'
|
||||
import { BestPageParams } from './BestPage'
|
||||
import { setRepo, settingsRepo } from './db'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import GymSet from './gym-set'
|
||||
import Page from './Page'
|
||||
|
@ -21,7 +21,7 @@ export default function BestList() {
|
|||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
}, []),
|
||||
)
|
||||
|
||||
|
@ -30,19 +30,19 @@ export default function BestList() {
|
|||
.createQueryBuilder()
|
||||
.select()
|
||||
.addSelect('MAX(weight)', 'weight')
|
||||
.where('name LIKE :name', {name: `%${value.trim()}%`})
|
||||
.where('name LIKE :name', { name: `%${value.trim()}%` })
|
||||
.andWhere('NOT hidden')
|
||||
.groupBy('name')
|
||||
.getMany()
|
||||
console.log(`${BestList.name}.refresh:`, {length: weights.length})
|
||||
console.log(`${BestList.name}.refresh:`, { length: weights.length })
|
||||
let newBest: GymSet[] = []
|
||||
for (const set of weights) {
|
||||
const reps = await setRepo
|
||||
.createQueryBuilder()
|
||||
.select()
|
||||
.addSelect('MAX(reps)', 'reps')
|
||||
.where('name = :name', {name: set.name})
|
||||
.andWhere('weight = :weight', {weight: set.weight})
|
||||
.where('name = :name', { name: set.name })
|
||||
.andWhere('weight = :weight', { weight: set.weight })
|
||||
.andWhere('NOT hidden')
|
||||
.groupBy('name')
|
||||
.getMany()
|
||||
|
@ -65,32 +65,40 @@ export default function BestList() {
|
|||
[refresh],
|
||||
)
|
||||
|
||||
const renderItem = ({item}: {item: GymSet}) => (
|
||||
const renderItem = ({ item }: { item: GymSet }) => (
|
||||
<List.Item
|
||||
key={item.name}
|
||||
title={item.name}
|
||||
description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`}
|
||||
onPress={() => navigation.navigate('ViewBest', {best: item})}
|
||||
onPress={() => navigation.navigate('ViewBest', { best: item })}
|
||||
left={() =>
|
||||
(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 (
|
||||
<>
|
||||
<DrawerHeader name="Best" />
|
||||
<DrawerHeader name='Best' />
|
||||
<Page term={term} search={search}>
|
||||
{bests?.length === 0 ? (
|
||||
{bests?.length === 0
|
||||
? (
|
||||
<List.Item
|
||||
title="No exercises yet"
|
||||
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>
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import BestList from './BestList'
|
||||
import GymSet from './gym-set'
|
||||
import ViewBest from './ViewBest'
|
||||
|
@ -14,9 +14,10 @@ export type BestPageParams = {
|
|||
export default function BestPage() {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{headerShown: false, animationEnabled: false}}>
|
||||
<Stack.Screen name="BestList" component={BestList} />
|
||||
<Stack.Screen name="ViewBest" component={ViewBest} />
|
||||
screenOptions={{ headerShown: false, animationEnabled: false }}
|
||||
>
|
||||
<Stack.Screen name='BestList' component={BestList} />
|
||||
<Stack.Screen name='ViewBest' component={ViewBest} />
|
||||
</Stack.Navigator>
|
||||
)
|
||||
}
|
||||
|
|
28
Chart.tsx
28
Chart.tsx
|
@ -1,9 +1,9 @@
|
|||
import {useTheme} from '@react-navigation/native'
|
||||
import { useTheme } from '@react-navigation/native'
|
||||
import * as shape from 'd3-shape'
|
||||
import {View} from 'react-native'
|
||||
import {Grid, LineChart, XAxis, YAxis} from 'react-native-svg-charts'
|
||||
import {CombinedDarkTheme, CombinedDefaultTheme} from './App'
|
||||
import {MARGIN, PADDING} from './constants'
|
||||
import { View } from 'react-native'
|
||||
import { Grid, LineChart, XAxis, YAxis } from 'react-native-svg-charts'
|
||||
import { CombinedDarkTheme, CombinedDefaultTheme } from './App'
|
||||
import { MARGIN, PADDING } from './constants'
|
||||
import GymSet from './gym-set'
|
||||
import useDark from './use-dark'
|
||||
|
||||
|
@ -18,7 +18,7 @@ export default function Chart({
|
|||
xFormat: (value: any, index: number) => string
|
||||
yFormat: (value: any) => string
|
||||
}) {
|
||||
const {colors} = useTheme()
|
||||
const { colors } = useTheme()
|
||||
const dark = useDark()
|
||||
const axesSvg = {
|
||||
fontSize: 10,
|
||||
|
@ -26,7 +26,7 @@ export default function Chart({
|
|||
? CombinedDarkTheme.colors.text
|
||||
: CombinedDefaultTheme.colors.text,
|
||||
}
|
||||
const verticalContentInset = {top: 10, bottom: 10}
|
||||
const verticalContentInset = { top: 10, bottom: 10 }
|
||||
const xAxisHeight = 30
|
||||
|
||||
return (
|
||||
|
@ -36,29 +36,31 @@ export default function Chart({
|
|||
height: 300,
|
||||
padding: PADDING,
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<YAxis
|
||||
data={yData}
|
||||
style={{marginBottom: xAxisHeight}}
|
||||
style={{ marginBottom: xAxisHeight }}
|
||||
contentInset={verticalContentInset}
|
||||
svg={axesSvg}
|
||||
formatLabel={yFormat}
|
||||
/>
|
||||
<View style={{flex: 1, marginLeft: MARGIN}}>
|
||||
<View style={{ flex: 1, marginLeft: MARGIN }}>
|
||||
<LineChart
|
||||
style={{flex: 1}}
|
||||
style={{ flex: 1 }}
|
||||
data={yData}
|
||||
contentInset={verticalContentInset}
|
||||
curve={shape.curveBasis}
|
||||
svg={{
|
||||
stroke: colors.primary,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Grid />
|
||||
</LineChart>
|
||||
<XAxis
|
||||
data={xData}
|
||||
formatLabel={xFormat}
|
||||
contentInset={{left: 15, right: 16}}
|
||||
contentInset={{ left: 15, right: 16 }}
|
||||
svg={axesSvg}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -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({
|
||||
title,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {DrawerNavigationProp} from '@react-navigation/drawer'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {Appbar, IconButton} from 'react-native-paper'
|
||||
import {DrawerParamList} from './drawer-param-list'
|
||||
import { DrawerNavigationProp } from '@react-navigation/drawer'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { Appbar, IconButton } from 'react-native-paper'
|
||||
import { DrawerParamList } from './drawer-param-list'
|
||||
import useDark from './use-dark'
|
||||
|
||||
export default function DrawerHeader({
|
||||
|
@ -18,7 +18,7 @@ export default function DrawerHeader({
|
|||
<Appbar.Header>
|
||||
<IconButton
|
||||
color={dark ? 'white' : 'white'}
|
||||
icon="menu"
|
||||
icon='menu'
|
||||
onPress={navigation.openDrawer}
|
||||
/>
|
||||
<Appbar.Content title={name} />
|
||||
|
|
76
EditPlan.tsx
76
EditPlan.tsx
|
@ -4,22 +4,22 @@ import {
|
|||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {ScrollView, StyleSheet, View} from 'react-native'
|
||||
import {Button, IconButton, Text} from 'react-native-paper'
|
||||
import {getLast} from './best.service'
|
||||
import {MARGIN, PADDING} from './constants'
|
||||
import {planRepo, setRepo} from './db'
|
||||
import {defaultSet} from './gym-set'
|
||||
import {PlanPageParams} from './plan-page-params'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { ScrollView, StyleSheet, View } from 'react-native'
|
||||
import { Button, IconButton, Text } from 'react-native-paper'
|
||||
import { getLast } from './best.service'
|
||||
import { MARGIN, PADDING } from './constants'
|
||||
import { planRepo, setRepo } from './db'
|
||||
import { defaultSet } from './gym-set'
|
||||
import { PlanPageParams } from './plan-page-params'
|
||||
import StackHeader from './StackHeader'
|
||||
import Switch from './Switch'
|
||||
import {DAYS} from './time'
|
||||
import { DAYS } from './time'
|
||||
import useDark from './use-dark'
|
||||
|
||||
export default function EditPlan() {
|
||||
const {params} = useRoute<RouteProp<PlanPageParams, 'EditPlan'>>()
|
||||
const {plan} = params
|
||||
const { params } = useRoute<RouteProp<PlanPageParams, 'EditPlan'>>()
|
||||
const { plan } = params
|
||||
const [days, setDays] = useState<string[]>(
|
||||
plan.days ? plan.days.split(',') : [],
|
||||
)
|
||||
|
@ -37,18 +37,18 @@ export default function EditPlan() {
|
|||
.distinct(true)
|
||||
.orderBy('name')
|
||||
.getRawMany()
|
||||
.then(values => {
|
||||
console.log(EditPlan.name, {values})
|
||||
setNames(values.map(value => value.name))
|
||||
.then((values) => {
|
||||
console.log(EditPlan.name, { values })
|
||||
setNames(values.map((value) => value.name))
|
||||
})
|
||||
}, [])
|
||||
|
||||
const save = useCallback(async () => {
|
||||
console.log(`${EditPlan.name}.save`, {days, workouts, plan})
|
||||
console.log(`${EditPlan.name}.save`, { days, workouts, plan })
|
||||
if (!days || !workouts) return
|
||||
const newWorkouts = workouts.filter(workout => workout).join(',')
|
||||
const newDays = days.filter(day => day).join(',')
|
||||
await planRepo.save({days: newDays, workouts: newWorkouts, id: plan.id})
|
||||
const newWorkouts = workouts.filter((workout) => workout).join(',')
|
||||
const newDays = days.filter((day) => day).join(',')
|
||||
await planRepo.save({ days: newDays, workouts: newWorkouts, id: plan.id })
|
||||
navigation.goBack()
|
||||
}, [days, workouts, plan, navigation])
|
||||
|
||||
|
@ -57,7 +57,7 @@ export default function EditPlan() {
|
|||
if (on) {
|
||||
setWorkouts([...workouts, name])
|
||||
} else {
|
||||
setWorkouts(workouts.filter(workout => workout !== name))
|
||||
setWorkouts(workouts.filter((workout) => workout !== name))
|
||||
}
|
||||
},
|
||||
[setWorkouts, workouts],
|
||||
|
@ -68,7 +68,7 @@ export default function EditPlan() {
|
|||
if (on) {
|
||||
setDays([...days, day])
|
||||
} else {
|
||||
setDays(days.filter(d => d !== day))
|
||||
setDays(days.filter((d) => d !== day))
|
||||
}
|
||||
},
|
||||
[setDays, days],
|
||||
|
@ -77,39 +77,42 @@ export default function EditPlan() {
|
|||
return (
|
||||
<>
|
||||
<StackHeader
|
||||
title={typeof plan.id === 'number' ? 'Edit plan' : 'Add plan'}>
|
||||
title={typeof plan.id === 'number' ? 'Edit plan' : 'Add plan'}
|
||||
>
|
||||
<IconButton
|
||||
color={dark ? 'white' : 'white'}
|
||||
onPress={async () => {
|
||||
let first = await getLast(workouts[0])
|
||||
if (!first) first = {...defaultSet, name: workouts[0]}
|
||||
if (!first) first = { ...defaultSet, name: workouts[0] }
|
||||
delete first.id
|
||||
navigation.navigate('StartPlan', {plan: params.plan, first})
|
||||
navigation.navigate('StartPlan', { plan: params.plan, first })
|
||||
}}
|
||||
icon="play-arrow"
|
||||
icon='play-arrow'
|
||||
/>
|
||||
</StackHeader>
|
||||
<View style={{padding: PADDING, flex: 1}}>
|
||||
<ScrollView style={{flex: 1}}>
|
||||
<View style={{ padding: PADDING, flex: 1 }}>
|
||||
<ScrollView style={{ flex: 1 }}>
|
||||
<Text style={styles.title}>Days</Text>
|
||||
{DAYS.map(day => (
|
||||
{DAYS.map((day) => (
|
||||
<Switch
|
||||
key={day}
|
||||
onChange={value => toggleDay(value, day)}
|
||||
onChange={(value) => toggleDay(value, day)}
|
||||
value={days.includes(day)}
|
||||
title={day}
|
||||
/>
|
||||
))}
|
||||
<Text style={[styles.title, {marginTop: MARGIN}]}>Workouts</Text>
|
||||
{names.length === 0 ? (
|
||||
<Text style={[styles.title, { marginTop: MARGIN }]}>Workouts</Text>
|
||||
{names.length === 0
|
||||
? (
|
||||
<View>
|
||||
<Text>No workouts found.</Text>
|
||||
</View>
|
||||
) : (
|
||||
names.map(name => (
|
||||
)
|
||||
: (
|
||||
names.map((name) => (
|
||||
<Switch
|
||||
key={name}
|
||||
onChange={value => toggleWorkout(value, name)}
|
||||
onChange={(value) => toggleWorkout(value, name)}
|
||||
value={workouts.includes(name)}
|
||||
title={name}
|
||||
/>
|
||||
|
@ -120,9 +123,10 @@ export default function EditPlan() {
|
|||
<Button
|
||||
disabled={workouts.length === 0 && days.length === 0}
|
||||
style={styles.button}
|
||||
mode="contained"
|
||||
icon="save"
|
||||
onPress={save}>
|
||||
mode='contained'
|
||||
icon='save'
|
||||
onPress={save}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</View>
|
||||
|
|
94
EditSet.tsx
94
EditSet.tsx
|
@ -1,28 +1,28 @@
|
|||
import {DateTimePickerAndroid} from '@react-native-community/datetimepicker'
|
||||
import { DateTimePickerAndroid } from '@react-native-community/datetimepicker'
|
||||
import {
|
||||
RouteProp,
|
||||
useFocusEffect,
|
||||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native'
|
||||
import {format} from 'date-fns'
|
||||
import {useCallback, useRef, useState} from 'react'
|
||||
import {NativeModules, TextInput, View} from 'react-native'
|
||||
import { format } from 'date-fns'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { NativeModules, TextInput, View } from 'react-native'
|
||||
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 ConfirmDialog from './ConfirmDialog'
|
||||
import {MARGIN, PADDING} from './constants'
|
||||
import {getNow, setRepo, settingsRepo} from './db'
|
||||
import { MARGIN, PADDING } from './constants'
|
||||
import { getNow, setRepo, settingsRepo } from './db'
|
||||
import GymSet from './gym-set'
|
||||
import {HomePageParams} from './home-page-params'
|
||||
import { HomePageParams } from './home-page-params'
|
||||
import Settings from './settings'
|
||||
import StackHeader from './StackHeader'
|
||||
import {toast} from './toast'
|
||||
import { toast } from './toast'
|
||||
|
||||
export default function EditSet() {
|
||||
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>()
|
||||
const {set} = params
|
||||
const { params } = useRoute<RouteProp<HomePageParams, 'EditSet'>>()
|
||||
const { set } = params
|
||||
const navigation = useNavigation()
|
||||
const [settings, setSettings] = useState<Settings>({} as Settings)
|
||||
const [name, setName] = useState(set.name)
|
||||
|
@ -46,16 +46,16 @@ export default function EditSet() {
|
|||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
}, []),
|
||||
)
|
||||
|
||||
const startTimer = useCallback(
|
||||
async (value: string) => {
|
||||
if (!settings.alarm) return
|
||||
const first = await setRepo.findOne({where: {name: value}})
|
||||
const milliseconds =
|
||||
(first?.minutes ?? 3) * 60 * 1000 + (first?.seconds ?? 0) * 1000
|
||||
const first = await setRepo.findOne({ where: { name: value } })
|
||||
const milliseconds = (first?.minutes ?? 3) * 60 * 1000 +
|
||||
(first?.seconds ?? 0) * 1000
|
||||
if (milliseconds) NativeModules.AlarmModule.timer(milliseconds)
|
||||
},
|
||||
[settings],
|
||||
|
@ -64,25 +64,27 @@ export default function EditSet() {
|
|||
const added = useCallback(
|
||||
async (value: GymSet) => {
|
||||
startTimer(value.name)
|
||||
console.log(`${EditSet.name}.add`, {set: value})
|
||||
console.log(`${EditSet.name}.add`, { set: value })
|
||||
if (!settings.notify) return
|
||||
if (
|
||||
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],
|
||||
)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
console.log(`${EditSet.name}.handleSubmit:`, {set, uri: newImage, name})
|
||||
console.log(`${EditSet.name}.handleSubmit:`, { set, uri: newImage, name })
|
||||
if (!name) return
|
||||
let image = newImage
|
||||
if (!newImage && !removeImage)
|
||||
image = await setRepo.findOne({where: {name}}).then(s => s?.image)
|
||||
if (!newImage && !removeImage) {
|
||||
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 saved = await setRepo.save({
|
||||
id: set.id,
|
||||
|
@ -102,7 +104,7 @@ export default function EditSet() {
|
|||
}
|
||||
|
||||
const changeImage = useCallback(async () => {
|
||||
const {fileCopyUri} = await DocumentPicker.pickSingle({
|
||||
const { fileCopyUri } = await DocumentPicker.pickSingle({
|
||||
type: DocumentPicker.types.images,
|
||||
copyTo: 'documentDirectory',
|
||||
})
|
||||
|
@ -137,9 +139,9 @@ export default function EditSet() {
|
|||
title={typeof set.id === 'number' ? 'Edit set' : 'Add set'}
|
||||
/>
|
||||
|
||||
<View style={{padding: PADDING, flex: 1}}>
|
||||
<View style={{ padding: PADDING, flex: 1 }}>
|
||||
<AppInput
|
||||
label="Name"
|
||||
label='Name'
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
autoCorrect={false}
|
||||
|
@ -148,20 +150,20 @@ export default function EditSet() {
|
|||
/>
|
||||
|
||||
<AppInput
|
||||
label="Reps"
|
||||
keyboardType="numeric"
|
||||
label='Reps'
|
||||
keyboardType='numeric'
|
||||
value={reps}
|
||||
onChangeText={setReps}
|
||||
onSubmitEditing={() => weightRef.current?.focus()}
|
||||
selection={selection}
|
||||
onSelectionChange={e => setSelection(e.nativeEvent.selection)}
|
||||
onSelectionChange={(e) => setSelection(e.nativeEvent.selection)}
|
||||
autoFocus={!!name}
|
||||
innerRef={repsRef}
|
||||
/>
|
||||
|
||||
<AppInput
|
||||
label="Weight"
|
||||
keyboardType="numeric"
|
||||
label='Weight'
|
||||
keyboardType='numeric'
|
||||
value={weight}
|
||||
onChangeText={setWeight}
|
||||
onSubmitEditing={handleSubmit}
|
||||
|
@ -170,8 +172,8 @@ export default function EditSet() {
|
|||
|
||||
{settings.showUnit && (
|
||||
<AppInput
|
||||
autoCapitalize="none"
|
||||
label="Unit"
|
||||
autoCapitalize='none'
|
||||
label='Unit'
|
||||
value={unit}
|
||||
onChangeText={setUnit}
|
||||
innerRef={unitRef}
|
||||
|
@ -180,7 +182,7 @@ export default function EditSet() {
|
|||
|
||||
{typeof set.id === 'number' && settings.showDate && (
|
||||
<AppInput
|
||||
label="Created"
|
||||
label='Created'
|
||||
value={format(created, settings.date || 'P')}
|
||||
onPressOut={pickDate}
|
||||
/>
|
||||
|
@ -188,18 +190,20 @@ export default function EditSet() {
|
|||
|
||||
{settings.images && newImage && (
|
||||
<TouchableRipple
|
||||
style={{marginBottom: MARGIN}}
|
||||
style={{ marginBottom: MARGIN }}
|
||||
onPress={changeImage}
|
||||
onLongPress={() => setShowRemove(true)}>
|
||||
<Card.Cover source={{uri: newImage}} />
|
||||
onLongPress={() => setShowRemove(true)}
|
||||
>
|
||||
<Card.Cover source={{ uri: newImage }} />
|
||||
</TouchableRipple>
|
||||
)}
|
||||
|
||||
{settings.images && !newImage && (
|
||||
<Button
|
||||
style={{marginBottom: MARGIN}}
|
||||
style={{ marginBottom: MARGIN }}
|
||||
onPress={changeImage}
|
||||
icon="add-photo-alternate">
|
||||
icon='add-photo-alternate'
|
||||
>
|
||||
Image
|
||||
</Button>
|
||||
)}
|
||||
|
@ -207,18 +211,20 @@ export default function EditSet() {
|
|||
|
||||
<Button
|
||||
disabled={!name}
|
||||
mode="contained"
|
||||
icon="save"
|
||||
style={{margin: MARGIN}}
|
||||
onPress={handleSubmit}>
|
||||
mode='contained'
|
||||
icon='save'
|
||||
style={{ margin: MARGIN }}
|
||||
onPress={handleSubmit}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
||||
<ConfirmDialog
|
||||
title="Remove image"
|
||||
title='Remove image'
|
||||
onOk={handleRemove}
|
||||
show={showRemove}
|
||||
setShow={setShowRemove}>
|
||||
setShow={setShowRemove}
|
||||
>
|
||||
Are you sure you want to remove the image?
|
||||
</ConfirmDialog>
|
||||
</>
|
||||
|
|
70
EditSets.tsx
70
EditSets.tsx
|
@ -4,23 +4,23 @@ import {
|
|||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { View } from 'react-native'
|
||||
import DocumentPicker from 'react-native-document-picker'
|
||||
import {Button, Card, TouchableRipple} from 'react-native-paper'
|
||||
import {In} from 'typeorm'
|
||||
import { Button, Card, TouchableRipple } from 'react-native-paper'
|
||||
import { In } from 'typeorm'
|
||||
import AppInput from './AppInput'
|
||||
import ConfirmDialog from './ConfirmDialog'
|
||||
import {MARGIN, PADDING} from './constants'
|
||||
import {setRepo, settingsRepo} from './db'
|
||||
import { MARGIN, PADDING } from './constants'
|
||||
import { setRepo, settingsRepo } from './db'
|
||||
import GymSet from './gym-set'
|
||||
import {HomePageParams} from './home-page-params'
|
||||
import { HomePageParams } from './home-page-params'
|
||||
import Settings from './settings'
|
||||
import StackHeader from './StackHeader'
|
||||
|
||||
export default function EditSets() {
|
||||
const {params} = useRoute<RouteProp<HomePageParams, 'EditSets'>>()
|
||||
const {ids} = params
|
||||
const { params } = useRoute<RouteProp<HomePageParams, 'EditSets'>>()
|
||||
const { ids } = params
|
||||
const navigation = useNavigation()
|
||||
const [settings, setSettings] = useState<Settings>({} as Settings)
|
||||
const [name, setName] = useState('')
|
||||
|
@ -41,18 +41,18 @@ export default function EditSets() {
|
|||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
setRepo.find({where: {id: In(ids)}}).then(sets => {
|
||||
setNames(sets.map(set => set.name).join(', '))
|
||||
setOldReps(sets.map(set => set.reps).join(', '))
|
||||
setWeights(sets.map(set => set.weight).join(', '))
|
||||
setUnits(sets.map(set => set.unit).join(', '))
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
setRepo.find({ where: { id: In(ids) } }).then((sets) => {
|
||||
setNames(sets.map((set) => set.name).join(', '))
|
||||
setOldReps(sets.map((set) => set.reps).join(', '))
|
||||
setWeights(sets.map((set) => set.weight).join(', '))
|
||||
setUnits(sets.map((set) => set.unit).join(', '))
|
||||
})
|
||||
}, [ids]),
|
||||
)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
console.log(`${EditSets.name}.handleSubmit:`, {uri: newImage, name})
|
||||
console.log(`${EditSets.name}.handleSubmit:`, { uri: newImage, name })
|
||||
const update: Partial<GymSet> = {}
|
||||
if (name) update.name = name
|
||||
if (reps) update.reps = Number(reps)
|
||||
|
@ -64,7 +64,7 @@ export default function EditSets() {
|
|||
}
|
||||
|
||||
const changeImage = useCallback(async () => {
|
||||
const {fileCopyUri} = await DocumentPicker.pickSingle({
|
||||
const { fileCopyUri } = await DocumentPicker.pickSingle({
|
||||
type: DocumentPicker.types.images,
|
||||
copyTo: 'documentDirectory',
|
||||
})
|
||||
|
@ -80,7 +80,7 @@ export default function EditSets() {
|
|||
<>
|
||||
<StackHeader title={`Edit ${ids.length} sets`} />
|
||||
|
||||
<View style={{padding: PADDING, flex: 1}}>
|
||||
<View style={{ padding: PADDING, flex: 1 }}>
|
||||
<AppInput
|
||||
label={`Names: ${names}`}
|
||||
value={name}
|
||||
|
@ -91,17 +91,17 @@ export default function EditSets() {
|
|||
|
||||
<AppInput
|
||||
label={`Reps: ${oldReps}`}
|
||||
keyboardType="numeric"
|
||||
keyboardType='numeric'
|
||||
value={reps}
|
||||
onChangeText={setReps}
|
||||
selection={selection}
|
||||
onSelectionChange={e => setSelection(e.nativeEvent.selection)}
|
||||
onSelectionChange={(e) => setSelection(e.nativeEvent.selection)}
|
||||
autoFocus={!!name}
|
||||
/>
|
||||
|
||||
<AppInput
|
||||
label={`Weights: ${weights}`}
|
||||
keyboardType="numeric"
|
||||
keyboardType='numeric'
|
||||
value={weight}
|
||||
onChangeText={setWeight}
|
||||
onSubmitEditing={handleSubmit}
|
||||
|
@ -109,7 +109,7 @@ export default function EditSets() {
|
|||
|
||||
{settings.showUnit && (
|
||||
<AppInput
|
||||
autoCapitalize="none"
|
||||
autoCapitalize='none'
|
||||
label={`Units: ${units}`}
|
||||
value={unit}
|
||||
onChangeText={setUnit}
|
||||
|
@ -118,35 +118,39 @@ export default function EditSets() {
|
|||
|
||||
{settings.images && newImage && (
|
||||
<TouchableRipple
|
||||
style={{marginBottom: MARGIN}}
|
||||
style={{ marginBottom: MARGIN }}
|
||||
onPress={changeImage}
|
||||
onLongPress={() => setShowRemove(true)}>
|
||||
<Card.Cover source={{uri: newImage}} />
|
||||
onLongPress={() => setShowRemove(true)}
|
||||
>
|
||||
<Card.Cover source={{ uri: newImage }} />
|
||||
</TouchableRipple>
|
||||
)}
|
||||
<ConfirmDialog
|
||||
title="Remove image"
|
||||
title='Remove image'
|
||||
onOk={handleRemove}
|
||||
show={showRemove}
|
||||
setShow={setShowRemove}>
|
||||
setShow={setShowRemove}
|
||||
>
|
||||
Are you sure you want to remove the image?
|
||||
</ConfirmDialog>
|
||||
|
||||
{settings.images && !newImage && (
|
||||
<Button
|
||||
style={{marginBottom: MARGIN}}
|
||||
style={{ marginBottom: MARGIN }}
|
||||
onPress={changeImage}
|
||||
icon="add-photo-alternate">
|
||||
icon='add-photo-alternate'
|
||||
>
|
||||
Image
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="save"
|
||||
style={{margin: MARGIN}}
|
||||
onPress={handleSubmit}>
|
||||
mode='contained'
|
||||
icon='save'
|
||||
style={{ margin: MARGIN }}
|
||||
onPress={handleSubmit}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</>
|
||||
|
|
|
@ -4,21 +4,21 @@ import {
|
|||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useRef, useState} from 'react'
|
||||
import {ScrollView, TextInput, View} from 'react-native'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { ScrollView, TextInput, View } from 'react-native'
|
||||
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 ConfirmDialog from './ConfirmDialog'
|
||||
import {MARGIN, PADDING} from './constants'
|
||||
import {getNow, planRepo, setRepo, settingsRepo} from './db'
|
||||
import {defaultSet} from './gym-set'
|
||||
import { MARGIN, PADDING } from './constants'
|
||||
import { getNow, planRepo, setRepo, settingsRepo } from './db'
|
||||
import { defaultSet } from './gym-set'
|
||||
import Settings from './settings'
|
||||
import StackHeader from './StackHeader'
|
||||
import {WorkoutsPageParams} from './WorkoutsPage'
|
||||
import { WorkoutsPageParams } from './WorkoutsPage'
|
||||
|
||||
export default function EditWorkout() {
|
||||
const {params} = useRoute<RouteProp<WorkoutsPageParams, 'EditWorkout'>>()
|
||||
const { params } = useRoute<RouteProp<WorkoutsPageParams, 'EditWorkout'>>()
|
||||
const [removeImage, setRemoveImage] = useState(false)
|
||||
const [showRemove, setShowRemove] = useState(false)
|
||||
const [name, setName] = useState(params.value.name)
|
||||
|
@ -40,13 +40,13 @@ export default function EditWorkout() {
|
|||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
}, []),
|
||||
)
|
||||
|
||||
const update = async () => {
|
||||
await setRepo.update(
|
||||
{name: params.value.name},
|
||||
{ name: params.value.name },
|
||||
{
|
||||
name: name || params.value.name,
|
||||
sets: Number(sets),
|
||||
|
@ -87,7 +87,7 @@ export default function EditWorkout() {
|
|||
}
|
||||
|
||||
const changeImage = useCallback(async () => {
|
||||
const {fileCopyUri} = await DocumentPicker.pickSingle({
|
||||
const { fileCopyUri } = await DocumentPicker.pickSingle({
|
||||
type: DocumentPicker.types.images,
|
||||
copyTo: 'documentDirectory',
|
||||
})
|
||||
|
@ -108,11 +108,11 @@ export default function EditWorkout() {
|
|||
return (
|
||||
<>
|
||||
<StackHeader title={params.value.name ? 'Edit workout' : 'Add workout'} />
|
||||
<View style={{padding: PADDING, flex: 1}}>
|
||||
<ScrollView style={{flex: 1}}>
|
||||
<View style={{ padding: PADDING, flex: 1 }}>
|
||||
<ScrollView style={{ flex: 1 }}>
|
||||
<AppInput
|
||||
autoFocus
|
||||
label="Name"
|
||||
label='Name'
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
onSubmitEditing={submitName}
|
||||
|
@ -123,7 +123,7 @@ export default function EditWorkout() {
|
|||
selectTextOnFocus={false}
|
||||
value={steps}
|
||||
onChangeText={setSteps}
|
||||
label="Steps"
|
||||
label='Steps'
|
||||
multiline
|
||||
onSubmitEditing={() => setsRef.current?.focus()}
|
||||
/>
|
||||
|
@ -132,8 +132,8 @@ export default function EditWorkout() {
|
|||
innerRef={setsRef}
|
||||
value={sets}
|
||||
onChangeText={setSets}
|
||||
label="Sets per workout"
|
||||
keyboardType="numeric"
|
||||
label='Sets per workout'
|
||||
keyboardType='numeric'
|
||||
onSubmitEditing={() => minutesRef.current?.focus()}
|
||||
/>
|
||||
{settings?.alarm && (
|
||||
|
@ -143,44 +143,47 @@ export default function EditWorkout() {
|
|||
onSubmitEditing={() => secondsRef.current?.focus()}
|
||||
value={minutes}
|
||||
onChangeText={setMinutes}
|
||||
label="Rest minutes"
|
||||
keyboardType="numeric"
|
||||
label='Rest minutes'
|
||||
keyboardType='numeric'
|
||||
/>
|
||||
<AppInput
|
||||
innerRef={secondsRef}
|
||||
value={seconds}
|
||||
onChangeText={setSeconds}
|
||||
label="Rest seconds"
|
||||
keyboardType="numeric"
|
||||
label='Rest seconds'
|
||||
keyboardType='numeric'
|
||||
blurOnSubmit
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{settings?.images && uri && (
|
||||
<TouchableRipple
|
||||
style={{marginBottom: MARGIN}}
|
||||
style={{ marginBottom: MARGIN }}
|
||||
onPress={changeImage}
|
||||
onLongPress={() => setShowRemove(true)}>
|
||||
<Card.Cover source={{uri}} />
|
||||
onLongPress={() => setShowRemove(true)}
|
||||
>
|
||||
<Card.Cover source={{ uri }} />
|
||||
</TouchableRipple>
|
||||
)}
|
||||
{settings?.images && !uri && (
|
||||
<Button
|
||||
style={{marginBottom: MARGIN}}
|
||||
style={{ marginBottom: MARGIN }}
|
||||
onPress={changeImage}
|
||||
icon="add-photo-alternate">
|
||||
icon='add-photo-alternate'
|
||||
>
|
||||
Image
|
||||
</Button>
|
||||
)}
|
||||
</ScrollView>
|
||||
<Button disabled={!name} mode="contained" icon="save" onPress={save}>
|
||||
<Button disabled={!name} mode='contained' icon='save' onPress={save}>
|
||||
Save
|
||||
</Button>
|
||||
<ConfirmDialog
|
||||
title="Remove image"
|
||||
title='Remove image'
|
||||
onOk={handleRemove}
|
||||
show={showRemove}
|
||||
setShow={setShowRemove}>
|
||||
setShow={setShowRemove}
|
||||
>
|
||||
Are you sure you want to remove the image?
|
||||
</ConfirmDialog>
|
||||
</View>
|
||||
|
|
13
HomePage.tsx
13
HomePage.tsx
|
@ -1,7 +1,7 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import EditSet from './EditSet'
|
||||
import EditSets from './EditSets'
|
||||
import {HomePageParams} from './home-page-params'
|
||||
import { HomePageParams } from './home-page-params'
|
||||
import SetList from './SetList'
|
||||
|
||||
const Stack = createStackNavigator<HomePageParams>()
|
||||
|
@ -9,10 +9,11 @@ const Stack = createStackNavigator<HomePageParams>()
|
|||
export default function HomePage() {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{headerShown: false, animationEnabled: false}}>
|
||||
<Stack.Screen name="Sets" component={SetList} />
|
||||
<Stack.Screen name="EditSet" component={EditSet} />
|
||||
<Stack.Screen name="EditSets" component={EditSets} />
|
||||
screenOptions={{ headerShown: false, animationEnabled: false }}
|
||||
>
|
||||
<Stack.Screen name='Sets' component={SetList} />
|
||||
<Stack.Screen name='EditSet' component={EditSet} />
|
||||
<Stack.Screen name='EditSets' component={EditSets} />
|
||||
</Stack.Navigator>
|
||||
)
|
||||
}
|
||||
|
|
38
ListMenu.tsx
38
ListMenu.tsx
|
@ -1,5 +1,5 @@
|
|||
import {useState} from 'react'
|
||||
import {Divider, IconButton, Menu} from 'react-native-paper'
|
||||
import { useState } from 'react'
|
||||
import { Divider, IconButton, Menu } from 'react-native-paper'
|
||||
import ConfirmDialog from './ConfirmDialog'
|
||||
import useDark from './use-dark'
|
||||
|
||||
|
@ -55,45 +55,45 @@ export default function ListMenu({
|
|||
<IconButton
|
||||
color={dark ? 'white' : 'white'}
|
||||
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
|
||||
icon="clear"
|
||||
title="Clear"
|
||||
icon='clear'
|
||||
title='Clear'
|
||||
onPress={clear}
|
||||
disabled={ids?.length === 0}
|
||||
/>
|
||||
<Menu.Item
|
||||
icon="edit"
|
||||
title="Edit"
|
||||
icon='edit'
|
||||
title='Edit'
|
||||
onPress={edit}
|
||||
disabled={ids?.length === 0}
|
||||
/>
|
||||
<Menu.Item
|
||||
icon="content-copy"
|
||||
title="Copy"
|
||||
icon='content-copy'
|
||||
title='Copy'
|
||||
onPress={copy}
|
||||
disabled={ids?.length === 0}
|
||||
/>
|
||||
<Divider />
|
||||
<Menu.Item
|
||||
icon="delete"
|
||||
icon='delete'
|
||||
onPress={() => setShowRemove(true)}
|
||||
title="Delete"
|
||||
title='Delete'
|
||||
/>
|
||||
<ConfirmDialog
|
||||
title={ids?.length === 0 ? 'Delete all' : 'Delete selected'}
|
||||
show={showRemove}
|
||||
setShow={setShowRemove}
|
||||
onOk={remove}
|
||||
onCancel={() => setShowMenu(false)}>
|
||||
{ids?.length === 0 ? (
|
||||
<>This irreversibly deletes records from the app. Are you sure?</>
|
||||
) : (
|
||||
<>This will delete {ids?.length} record(s). Are you sure?</>
|
||||
)}
|
||||
onCancel={() => setShowMenu(false)}
|
||||
>
|
||||
{ids?.length === 0
|
||||
? <>This irreversibly deletes records from the app. Are you sure?</>
|
||||
: <>This will delete {ids?.length} record(s). Are you sure?</>}
|
||||
</ConfirmDialog>
|
||||
</Menu>
|
||||
)
|
||||
|
|
12
Page.tsx
12
Page.tsx
|
@ -1,7 +1,7 @@
|
|||
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
|
||||
import {Searchbar} from 'react-native-paper'
|
||||
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
|
||||
import { Searchbar } from 'react-native-paper'
|
||||
import AppFab from './AppFab'
|
||||
import {PADDING} from './constants'
|
||||
import { PADDING } from './constants'
|
||||
|
||||
export default function Page({
|
||||
onAdd,
|
||||
|
@ -19,11 +19,11 @@ export default function Page({
|
|||
return (
|
||||
<View style={[styles.view, style]}>
|
||||
<Searchbar
|
||||
placeholder="Search"
|
||||
placeholder='Search'
|
||||
value={term}
|
||||
onChangeText={search}
|
||||
icon="search"
|
||||
clearIcon="clear"
|
||||
icon='search'
|
||||
clearIcon='clear'
|
||||
/>
|
||||
{children}
|
||||
{onAdd && <AppFab onPress={onAdd} />}
|
||||
|
|
41
PlanItem.tsx
41
PlanItem.tsx
|
@ -3,15 +3,15 @@ import {
|
|||
useFocusEffect,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {Text} from 'react-native'
|
||||
import {List} from 'react-native-paper'
|
||||
import {getLast} from './best.service'
|
||||
import {DARK_RIPPLE, LIGHT_RIPPLE} from './constants'
|
||||
import {defaultSet} from './gym-set'
|
||||
import {Plan} from './plan'
|
||||
import {PlanPageParams} from './plan-page-params'
|
||||
import {DAYS} from './time'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { Text } from 'react-native'
|
||||
import { List } from 'react-native-paper'
|
||||
import { getLast } from './best.service'
|
||||
import { DARK_RIPPLE, LIGHT_RIPPLE } from './constants'
|
||||
import { defaultSet } from './gym-set'
|
||||
import { Plan } from './plan'
|
||||
import { PlanPageParams } from './plan-page-params'
|
||||
import { DAYS } from './time'
|
||||
import useDark from './use-dark'
|
||||
|
||||
export default function PlanItem({
|
||||
|
@ -38,12 +38,13 @@ export default function PlanItem({
|
|||
const start = useCallback(async () => {
|
||||
const workout = item.workouts.split(',')[0]
|
||||
let first = await getLast(workout)
|
||||
if (!first) first = {...defaultSet, name: workout}
|
||||
if (!first) first = { ...defaultSet, name: workout }
|
||||
delete first.id
|
||||
if (ids.length === 0)
|
||||
return navigation.navigate('StartPlan', {plan: item, first})
|
||||
const removing = ids.find(id => id === item.id)
|
||||
if (removing) setIds(ids.filter(id => id !== item.id))
|
||||
if (ids.length === 0) {
|
||||
return navigation.navigate('StartPlan', { plan: item, first })
|
||||
}
|
||||
const removing = ids.find((id) => id === item.id)
|
||||
if (removing) setIds(ids.filter((id) => id !== item.id))
|
||||
else setIds([...ids, item.id])
|
||||
}, [ids, setIds, item, navigation])
|
||||
|
||||
|
@ -56,11 +57,15 @@ export default function PlanItem({
|
|||
() =>
|
||||
days.map((day, index) => (
|
||||
<Text key={day}>
|
||||
{day === today ? (
|
||||
<Text style={{fontWeight: 'bold', textDecorationLine: 'underline'}}>
|
||||
{day === today
|
||||
? (
|
||||
<Text
|
||||
style={{ fontWeight: 'bold', textDecorationLine: 'underline' }}
|
||||
>
|
||||
{day}
|
||||
</Text>
|
||||
) : (
|
||||
)
|
||||
: (
|
||||
day
|
||||
)}
|
||||
{index === days.length - 1 ? '' : ', '}
|
||||
|
@ -86,7 +91,7 @@ export default function PlanItem({
|
|||
title={title}
|
||||
description={description}
|
||||
onLongPress={longPress}
|
||||
style={{backgroundColor}}
|
||||
style={{ backgroundColor }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
46
PlanList.tsx
46
PlanList.tsx
|
@ -3,16 +3,16 @@ import {
|
|||
useFocusEffect,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useState} from 'react'
|
||||
import {FlatList} from 'react-native'
|
||||
import {List} from 'react-native-paper'
|
||||
import {Like} from 'typeorm'
|
||||
import {planRepo} from './db'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { FlatList } from 'react-native'
|
||||
import { List } from 'react-native-paper'
|
||||
import { Like } from 'typeorm'
|
||||
import { planRepo } from './db'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import ListMenu from './ListMenu'
|
||||
import Page from './Page'
|
||||
import {Plan} from './plan'
|
||||
import {PlanPageParams} from './plan-page-params'
|
||||
import { Plan } from './plan'
|
||||
import { PlanPageParams } from './plan-page-params'
|
||||
import PlanItem from './PlanItem'
|
||||
|
||||
export default function PlanList() {
|
||||
|
@ -25,8 +25,8 @@ export default function PlanList() {
|
|||
planRepo
|
||||
.find({
|
||||
where: [
|
||||
{days: Like(`%${value.trim()}%`)},
|
||||
{workouts: Like(`%${value.trim()}%`)},
|
||||
{ days: Like(`%${value.trim()}%`) },
|
||||
{ workouts: Like(`%${value.trim()}%`) },
|
||||
],
|
||||
})
|
||||
.then(setPlans)
|
||||
|
@ -47,27 +47,27 @@ export default function PlanList() {
|
|||
)
|
||||
|
||||
const renderItem = useCallback(
|
||||
({item}: {item: Plan}) => (
|
||||
({ item }: { item: Plan }) => (
|
||||
<PlanItem ids={ids} setIds={setIds} item={item} key={item.id} />
|
||||
),
|
||||
[ids],
|
||||
)
|
||||
|
||||
const onAdd = () =>
|
||||
navigation.navigate('EditPlan', {plan: {days: '', workouts: ''}})
|
||||
navigation.navigate('EditPlan', { plan: { days: '', workouts: '' } })
|
||||
|
||||
const edit = useCallback(async () => {
|
||||
const plan = await planRepo.findOne({where: {id: ids.pop()}})
|
||||
navigation.navigate('EditPlan', {plan})
|
||||
const plan = await planRepo.findOne({ where: { id: ids.pop() } })
|
||||
navigation.navigate('EditPlan', { plan })
|
||||
setIds([])
|
||||
}, [ids, navigation])
|
||||
|
||||
const copy = useCallback(async () => {
|
||||
const plan = await planRepo.findOne({
|
||||
where: {id: ids.pop()},
|
||||
where: { id: ids.pop() },
|
||||
})
|
||||
delete plan.id
|
||||
navigation.navigate('EditPlan', {plan})
|
||||
navigation.navigate('EditPlan', { plan })
|
||||
setIds([])
|
||||
}, [ids, navigation])
|
||||
|
||||
|
@ -82,7 +82,7 @@ export default function PlanList() {
|
|||
}, [ids, refresh, term])
|
||||
|
||||
const select = useCallback(() => {
|
||||
setIds(plans.map(plan => plan.id))
|
||||
setIds(plans.map((plan) => plan.id))
|
||||
}, [plans])
|
||||
|
||||
return (
|
||||
|
@ -98,17 +98,19 @@ export default function PlanList() {
|
|||
/>
|
||||
</DrawerHeader>
|
||||
<Page onAdd={onAdd} term={term} search={search}>
|
||||
{plans?.length === 0 ? (
|
||||
{plans?.length === 0
|
||||
? (
|
||||
<List.Item
|
||||
title="No plans yet"
|
||||
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}}
|
||||
style={{ flex: 1 }}
|
||||
data={plans}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={set => set.id?.toString() || ''}
|
||||
keyExtractor={(set) => set.id?.toString() || ''}
|
||||
/>
|
||||
)}
|
||||
</Page>
|
||||
|
|
15
PlanPage.tsx
15
PlanPage.tsx
|
@ -1,7 +1,7 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import EditPlan from './EditPlan'
|
||||
import EditSet from './EditSet'
|
||||
import {PlanPageParams} from './plan-page-params'
|
||||
import { PlanPageParams } from './plan-page-params'
|
||||
import PlanList from './PlanList'
|
||||
import StartPlan from './StartPlan'
|
||||
|
||||
|
@ -10,11 +10,12 @@ const Stack = createStackNavigator<PlanPageParams>()
|
|||
export default function PlanPage() {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{headerShown: false, animationEnabled: false}}>
|
||||
<Stack.Screen name="PlanList" component={PlanList} />
|
||||
<Stack.Screen name="EditPlan" component={EditPlan} />
|
||||
<Stack.Screen name="StartPlan" component={StartPlan} />
|
||||
<Stack.Screen name="EditSet" component={EditSet} />
|
||||
screenOptions={{ headerShown: false, animationEnabled: false }}
|
||||
>
|
||||
<Stack.Screen name='PlanList' component={PlanList} />
|
||||
<Stack.Screen name='EditPlan' component={EditPlan} />
|
||||
<Stack.Screen name='StartPlan' component={StartPlan} />
|
||||
<Stack.Screen name='EditSet' component={EditSet} />
|
||||
</Stack.Navigator>
|
||||
)
|
||||
}
|
||||
|
|
33
Routes.tsx
33
Routes.tsx
|
@ -1,7 +1,7 @@
|
|||
import {createDrawerNavigator} from '@react-navigation/drawer'
|
||||
import {IconButton} from 'react-native-paper'
|
||||
import { createDrawerNavigator } from '@react-navigation/drawer'
|
||||
import { IconButton } from 'react-native-paper'
|
||||
import BestPage from './BestPage'
|
||||
import {DrawerParamList} from './drawer-param-list'
|
||||
import { DrawerParamList } from './drawer-param-list'
|
||||
import HomePage from './HomePage'
|
||||
import PlanPage from './PlanPage'
|
||||
import SettingsPage from './SettingsPage'
|
||||
|
@ -20,36 +20,37 @@ export default function Routes() {
|
|||
headerTintColor: dark ? 'white' : 'black',
|
||||
swipeEdgeWidth: 1000,
|
||||
headerShown: false,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Drawer.Screen
|
||||
name="Home"
|
||||
name='Home'
|
||||
component={HomePage}
|
||||
options={{drawerIcon: () => <IconButton icon="home" />}}
|
||||
options={{ drawerIcon: () => <IconButton icon='home' /> }}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="Plans"
|
||||
name='Plans'
|
||||
component={PlanPage}
|
||||
options={{drawerIcon: () => <IconButton icon="event" />}}
|
||||
options={{ drawerIcon: () => <IconButton icon='event' /> }}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="Best"
|
||||
name='Best'
|
||||
component={BestPage}
|
||||
options={{drawerIcon: () => <IconButton icon="insights" />}}
|
||||
options={{ drawerIcon: () => <IconButton icon='insights' /> }}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="Workouts"
|
||||
name='Workouts'
|
||||
component={WorkoutsPage}
|
||||
options={{drawerIcon: () => <IconButton icon="fitness-center" />}}
|
||||
options={{ drawerIcon: () => <IconButton icon='fitness-center' /> }}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="Timer"
|
||||
name='Timer'
|
||||
component={TimerPage}
|
||||
options={{drawerIcon: () => <IconButton icon="access-time" />}}
|
||||
options={{ drawerIcon: () => <IconButton icon='access-time' /> }}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="Settings"
|
||||
name='Settings'
|
||||
component={SettingsPage}
|
||||
options={{drawerIcon: () => <IconButton icon="settings" />}}
|
||||
options={{ drawerIcon: () => <IconButton icon='settings' /> }}
|
||||
/>
|
||||
</Drawer.Navigator>
|
||||
)
|
||||
|
|
27
Select.tsx
27
Select.tsx
|
@ -1,7 +1,7 @@
|
|||
import React, {useCallback, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {Button, Menu, Subheading, useTheme} from 'react-native-paper'
|
||||
import {ITEM_PADDING} from './constants'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { Button, Menu, Subheading, useTheme } from 'react-native-paper'
|
||||
import { ITEM_PADDING } from './constants'
|
||||
|
||||
export interface Item {
|
||||
value: string
|
||||
|
@ -21,10 +21,10 @@ function Select({
|
|||
label?: string
|
||||
}) {
|
||||
const [show, setShow] = useState(false)
|
||||
const {colors} = useTheme()
|
||||
const { colors } = useTheme()
|
||||
|
||||
const selected = useMemo(
|
||||
() => items.find(item => item.value === value) || items[0],
|
||||
() => items.find((item) => item.value === value) || items[0],
|
||||
[items, value],
|
||||
)
|
||||
|
||||
|
@ -42,8 +42,9 @@ function Select({
|
|||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingLeft: ITEM_PADDING,
|
||||
}}>
|
||||
{label && <Subheading style={{width: 100}}>{label}</Subheading>}
|
||||
}}
|
||||
>
|
||||
{label && <Subheading style={{ width: 100 }}>{label}</Subheading>}
|
||||
<Menu
|
||||
visible={show}
|
||||
onDismiss={() => setShow(false)}
|
||||
|
@ -52,14 +53,16 @@ function Select({
|
|||
onPress={() => setShow(true)}
|
||||
style={{
|
||||
alignSelf: 'flex-start',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{selected?.label}
|
||||
</Button>
|
||||
}>
|
||||
{items.map(item => (
|
||||
}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<Menu.Item
|
||||
key={item.value}
|
||||
titleStyle={{color: item.color || colors.text}}
|
||||
titleStyle={{ color: item.color || colors.text }}
|
||||
title={item.label}
|
||||
onPress={() => handlePress(item.value)}
|
||||
/>
|
||||
|
|
33
SetItem.tsx
33
SetItem.tsx
|
@ -1,11 +1,11 @@
|
|||
import {NavigationProp, useNavigation} from '@react-navigation/native'
|
||||
import {format} from 'date-fns'
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {Image} from 'react-native'
|
||||
import {List, Text} from 'react-native-paper'
|
||||
import {DARK_RIPPLE, LIGHT_RIPPLE} from './constants'
|
||||
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
||||
import { format } from 'date-fns'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { Image } from 'react-native'
|
||||
import { List, Text } from 'react-native-paper'
|
||||
import { DARK_RIPPLE, LIGHT_RIPPLE } from './constants'
|
||||
import GymSet from './gym-set'
|
||||
import {HomePageParams} from './home-page-params'
|
||||
import { HomePageParams } from './home-page-params'
|
||||
import Settings from './settings'
|
||||
import useDark from './use-dark'
|
||||
|
||||
|
@ -30,9 +30,9 @@ export default function SetItem({
|
|||
}, [ids.length, item.id, setIds])
|
||||
|
||||
const press = useCallback(() => {
|
||||
if (ids.length === 0) return navigation.navigate('EditSet', {set: item})
|
||||
const removing = ids.find(id => id === item.id)
|
||||
if (removing) setIds(ids.filter(id => id !== item.id))
|
||||
if (ids.length === 0) return navigation.navigate('EditSet', { set: item })
|
||||
const removing = ids.find((id) => id === item.id)
|
||||
if (removing) setIds(ids.filter((id) => id !== item.id))
|
||||
else setIds([...ids, item.id])
|
||||
}, [ids, item, navigation, setIds])
|
||||
|
||||
|
@ -49,13 +49,15 @@ export default function SetItem({
|
|||
title={item.name}
|
||||
description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`}
|
||||
onLongPress={longPress}
|
||||
style={{backgroundColor}}
|
||||
style={{ backgroundColor }}
|
||||
left={() =>
|
||||
settings.images &&
|
||||
item.image && (
|
||||
<Image source={{uri: item.image}} style={{height: 75, width: 75}} />
|
||||
)
|
||||
}
|
||||
<Image
|
||||
source={{ uri: item.image }}
|
||||
style={{ height: 75, width: 75 }}
|
||||
/>
|
||||
)}
|
||||
right={() => (
|
||||
<>
|
||||
{settings.showDate && (
|
||||
|
@ -63,7 +65,8 @@ export default function SetItem({
|
|||
style={{
|
||||
alignSelf: 'center',
|
||||
color: dark ? '#909090ff' : '#717171ff',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{format(new Date(item.created), settings.date || 'P')}
|
||||
</Text>
|
||||
)}
|
||||
|
|
54
SetList.tsx
54
SetList.tsx
|
@ -3,14 +3,14 @@ import {
|
|||
useFocusEffect,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useState} from 'react'
|
||||
import {FlatList} from 'react-native'
|
||||
import {List} from 'react-native-paper'
|
||||
import {Like} from 'typeorm'
|
||||
import {getNow, setRepo, settingsRepo} from './db'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { FlatList } from 'react-native'
|
||||
import { List } from 'react-native-paper'
|
||||
import { Like } from 'typeorm'
|
||||
import { getNow, setRepo, settingsRepo } from './db'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import GymSet, {defaultSet} from './gym-set'
|
||||
import {HomePageParams} from './home-page-params'
|
||||
import GymSet, { defaultSet } from './gym-set'
|
||||
import { HomePageParams } from './home-page-params'
|
||||
import ListMenu from './ListMenu'
|
||||
import Page from './Page'
|
||||
import SetItem from './SetItem'
|
||||
|
@ -29,12 +29,12 @@ export default function SetList() {
|
|||
|
||||
const refresh = useCallback(async (value: string) => {
|
||||
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,
|
||||
skip: 0,
|
||||
order: {created: 'DESC'},
|
||||
order: { created: 'DESC' },
|
||||
})
|
||||
console.log(`${SetList.name}.refresh:`, {value, limit})
|
||||
console.log(`${SetList.name}.refresh:`, { value, limit })
|
||||
setSets(newSets)
|
||||
setOffset(0)
|
||||
setEnd(false)
|
||||
|
@ -43,12 +43,12 @@ export default function SetList() {
|
|||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
refresh(term)
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
}, [refresh, term]),
|
||||
)
|
||||
|
||||
const renderItem = useCallback(
|
||||
({item}: {item: GymSet}) => (
|
||||
({ item }: { item: GymSet }) => (
|
||||
<SetItem
|
||||
settings={settings}
|
||||
item={item}
|
||||
|
@ -64,12 +64,12 @@ export default function SetList() {
|
|||
const next = useCallback(async () => {
|
||||
if (end) return
|
||||
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({
|
||||
where: {name: Like(`%${term}%`), hidden: 0 as any},
|
||||
where: { name: Like(`%${term}%`), hidden: 0 as any },
|
||||
take: limit,
|
||||
skip: newOffset,
|
||||
order: {created: 'DESC'},
|
||||
order: { created: 'DESC' },
|
||||
})
|
||||
if (newSets.length === 0) return setEnd(true)
|
||||
if (!sets) return
|
||||
|
@ -81,10 +81,10 @@ export default function SetList() {
|
|||
const onAdd = useCallback(async () => {
|
||||
const now = await getNow()
|
||||
let set = sets[0]
|
||||
if (!set) set = {...defaultSet}
|
||||
if (!set) set = { ...defaultSet }
|
||||
set.created = now
|
||||
delete set.id
|
||||
navigation.navigate('EditSet', {set})
|
||||
navigation.navigate('EditSet', { set })
|
||||
}, [navigation, sets])
|
||||
|
||||
const search = useCallback(
|
||||
|
@ -96,17 +96,17 @@ export default function SetList() {
|
|||
)
|
||||
|
||||
const edit = useCallback(() => {
|
||||
navigation.navigate('EditSets', {ids})
|
||||
navigation.navigate('EditSets', { ids })
|
||||
setIds([])
|
||||
}, [ids, navigation])
|
||||
|
||||
const copy = useCallback(async () => {
|
||||
const set = await setRepo.findOne({
|
||||
where: {id: ids.pop()},
|
||||
where: { id: ids.pop() },
|
||||
})
|
||||
delete set.id
|
||||
delete set.created
|
||||
navigation.navigate('EditSet', {set})
|
||||
navigation.navigate('EditSet', { set })
|
||||
setIds([])
|
||||
}, [ids, navigation])
|
||||
|
||||
|
@ -121,7 +121,7 @@ export default function SetList() {
|
|||
}, [ids, refresh, term])
|
||||
|
||||
const select = useCallback(() => {
|
||||
setIds(sets.map(set => set.id))
|
||||
setIds(sets.map((set) => set.id))
|
||||
}, [sets])
|
||||
|
||||
return (
|
||||
|
@ -138,16 +138,18 @@ export default function SetList() {
|
|||
</DrawerHeader>
|
||||
|
||||
<Page onAdd={onAdd} term={term} search={search}>
|
||||
{sets?.length === 0 ? (
|
||||
{sets?.length === 0
|
||||
? (
|
||||
<List.Item
|
||||
title="No sets yet"
|
||||
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}}
|
||||
style={{ flex: 1 }}
|
||||
renderItem={renderItem}
|
||||
onEndReached={next}
|
||||
/>
|
||||
|
|
122
SettingsPage.tsx
122
SettingsPage.tsx
|
@ -1,25 +1,25 @@
|
|||
import {NavigationProp, useNavigation} from '@react-navigation/native'
|
||||
import {format} from 'date-fns'
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {useForm} from 'react-hook-form'
|
||||
import {NativeModules, ScrollView, View} from 'react-native'
|
||||
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
||||
import { format } from 'date-fns'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { NativeModules, ScrollView, View } from 'react-native'
|
||||
import DocumentPicker from 'react-native-document-picker'
|
||||
import {Dirs, FileSystem} from 'react-native-file-access'
|
||||
import {Button, Subheading} from 'react-native-paper'
|
||||
import { Dirs, FileSystem } from 'react-native-file-access'
|
||||
import { Button, Subheading } from 'react-native-paper'
|
||||
import ConfirmDialog from './ConfirmDialog'
|
||||
import {ITEM_PADDING, MARGIN} from './constants'
|
||||
import {AppDataSource} from './data-source'
|
||||
import {setRepo, settingsRepo} from './db'
|
||||
import {DrawerParamList} from './drawer-param-list'
|
||||
import { ITEM_PADDING, MARGIN } from './constants'
|
||||
import { AppDataSource } from './data-source'
|
||||
import { setRepo, settingsRepo } from './db'
|
||||
import { DrawerParamList } from './drawer-param-list'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import Input from './input'
|
||||
import {darkOptions, lightOptions, themeOptions} from './options'
|
||||
import { darkOptions, lightOptions, themeOptions } from './options'
|
||||
import Page from './Page'
|
||||
import Select from './Select'
|
||||
import Settings from './settings'
|
||||
import Switch from './Switch'
|
||||
import {toast} from './toast'
|
||||
import {useTheme} from './use-theme'
|
||||
import { toast } from './toast'
|
||||
import { useTheme } from './use-theme'
|
||||
|
||||
const twelveHours = [
|
||||
'dd/LL/yyyy',
|
||||
|
@ -45,20 +45,26 @@ export default function SettingsPage() {
|
|||
const [term, setTerm] = useState('')
|
||||
const [formatOptions, setFormatOptions] = useState<string[]>(twelveHours)
|
||||
const [importing, setImporting] = useState(false)
|
||||
const {reset} = useNavigation<NavigationProp<DrawerParamList>>()
|
||||
const { reset } = useNavigation<NavigationProp<DrawerParamList>>()
|
||||
|
||||
const {watch, setValue} = useForm<Settings>({
|
||||
defaultValues: () => settingsRepo.findOne({where: {}}),
|
||||
const { watch, setValue } = useForm<Settings>({
|
||||
defaultValues: () => settingsRepo.findOne({ where: {} }),
|
||||
})
|
||||
const settings = watch()
|
||||
|
||||
const {theme, setTheme, lightColor, setLightColor, darkColor, setDarkColor} =
|
||||
useTheme()
|
||||
const {
|
||||
theme,
|
||||
setTheme,
|
||||
lightColor,
|
||||
setLightColor,
|
||||
darkColor,
|
||||
setDarkColor,
|
||||
} = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
NativeModules.SettingsModule.ignoringBattery(setIgnoring)
|
||||
NativeModules.SettingsModule.is24().then((is24: boolean) => {
|
||||
console.log(`${SettingsPage.name}.focus:`, {is24})
|
||||
console.log(`${SettingsPage.name}.focus:`, { is24 })
|
||||
if (is24) setFormatOptions(twentyFours)
|
||||
else setFormatOptions(twelveHours)
|
||||
})
|
||||
|
@ -68,7 +74,7 @@ export default function SettingsPage() {
|
|||
return settingsRepo
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({[key]: value})
|
||||
.set({ [key]: value })
|
||||
.printSql()
|
||||
.execute()
|
||||
}, [])
|
||||
|
@ -80,7 +86,7 @@ export default function SettingsPage() {
|
|||
}, [settings.sound])
|
||||
|
||||
const changeSound = useCallback(async () => {
|
||||
const {fileCopyUri} = await DocumentPicker.pickSingle({
|
||||
const { fileCopyUri } = await DocumentPicker.pickSingle({
|
||||
type: DocumentPicker.types.audio,
|
||||
copyTo: 'documentDirectory',
|
||||
})
|
||||
|
@ -92,21 +98,21 @@ export default function SettingsPage() {
|
|||
|
||||
const switches: Input<boolean>[] = useMemo(
|
||||
() => [
|
||||
{name: 'Rest timers', value: settings.alarm, key: 'alarm'},
|
||||
{name: 'Vibrate', value: settings.vibrate, key: 'vibrate'},
|
||||
{name: 'Disable sound', value: settings.noSound, key: 'noSound'},
|
||||
{name: 'Notifications', value: settings.notify, key: 'notify'},
|
||||
{name: 'Show images', value: settings.images, key: 'images'},
|
||||
{name: 'Show unit', value: settings.showUnit, key: 'showUnit'},
|
||||
{name: 'Show steps', value: settings.steps, key: 'steps'},
|
||||
{name: 'Show date', value: settings.showDate, key: 'showDate'},
|
||||
{name: 'Automatic backup', value: settings.backup, key: 'backup'},
|
||||
{ name: 'Rest timers', value: settings.alarm, key: 'alarm' },
|
||||
{ name: 'Vibrate', value: settings.vibrate, key: 'vibrate' },
|
||||
{ name: 'Disable sound', value: settings.noSound, key: 'noSound' },
|
||||
{ name: 'Notifications', value: settings.notify, key: 'notify' },
|
||||
{ name: 'Show images', value: settings.images, key: 'images' },
|
||||
{ name: 'Show unit', value: settings.showUnit, key: 'showUnit' },
|
||||
{ name: 'Show steps', value: settings.steps, key: 'steps' },
|
||||
{ name: 'Show date', value: settings.showDate, key: 'showDate' },
|
||||
{ name: 'Automatic backup', value: settings.backup, key: 'backup' },
|
||||
],
|
||||
[settings],
|
||||
)
|
||||
|
||||
const filter = useCallback(
|
||||
({name}) => name.toLowerCase().includes(term.toLowerCase()),
|
||||
({ name }) => name.toLowerCase().includes(term.toLowerCase()),
|
||||
[term],
|
||||
)
|
||||
|
||||
|
@ -168,7 +174,7 @@ export default function SettingsPage() {
|
|||
<Switch
|
||||
key={item.name}
|
||||
value={item.value}
|
||||
onChange={value => changeBoolean(item.key, value)}
|
||||
onChange={(value) => changeBoolean(item.key, value)}
|
||||
title={item.name}
|
||||
/>
|
||||
),
|
||||
|
@ -176,7 +182,7 @@ export default function SettingsPage() {
|
|||
)
|
||||
|
||||
const switchesMarkup = useMemo(
|
||||
() => switches.filter(filter).map(s => renderSwitch(s)),
|
||||
() => switches.filter(filter).map((s) => renderSwitch(s)),
|
||||
[filter, switches, renderSwitch],
|
||||
)
|
||||
|
||||
|
@ -211,7 +217,7 @@ export default function SettingsPage() {
|
|||
const selects: Input<string>[] = useMemo(() => {
|
||||
const today = new Date()
|
||||
return [
|
||||
{name: 'Theme', value: theme, items: themeOptions, key: 'theme'},
|
||||
{ name: 'Theme', value: theme, items: themeOptions, key: 'theme' },
|
||||
{
|
||||
name: 'Dark color',
|
||||
value: darkColor,
|
||||
|
@ -227,7 +233,7 @@ export default function SettingsPage() {
|
|||
{
|
||||
name: 'Date format',
|
||||
value: settings.date,
|
||||
items: formatOptions.map(option => ({
|
||||
items: formatOptions.map((option) => ({
|
||||
label: format(today, option),
|
||||
value: option,
|
||||
})),
|
||||
|
@ -241,7 +247,7 @@ export default function SettingsPage() {
|
|||
<Select
|
||||
key={item.name}
|
||||
value={item.value}
|
||||
onChange={value => changeString(item.key, value)}
|
||||
onChange={(value) => changeString(item.key, value)}
|
||||
label={item.name}
|
||||
items={item.items}
|
||||
/>
|
||||
|
@ -260,17 +266,17 @@ export default function SettingsPage() {
|
|||
const file = await DocumentPicker.pickSingle()
|
||||
await FileSystem.cp(file.uri, Dirs.DatabaseDir + '/massive.db')
|
||||
await AppDataSource.initialize()
|
||||
await setRepo.createQueryBuilder().update().set({image: null}).execute()
|
||||
await setRepo.createQueryBuilder().update().set({ image: null }).execute()
|
||||
await update('sound', null)
|
||||
const {alarm, backup} = await settingsRepo.findOne({where: {}})
|
||||
console.log({backup})
|
||||
const { alarm, backup } = await settingsRepo.findOne({ where: {} })
|
||||
console.log({ backup })
|
||||
const directory = await DocumentPicker.pickDirectory()
|
||||
if (backup) NativeModules.BackupModule.start(directory.uri)
|
||||
else NativeModules.BackupModule.stop()
|
||||
NativeModules.SettingsModule.ignoringBattery(
|
||||
async (isIgnoring: boolean) => {
|
||||
if (alarm && !isIgnoring) NativeModules.SettingsModule.ignoreBattery()
|
||||
reset({index: 0, routes: [{name: 'Settings'}]})
|
||||
reset({ index: 0, routes: [{ name: 'Settings' }] })
|
||||
},
|
||||
)
|
||||
}, [reset, update])
|
||||
|
@ -287,13 +293,14 @@ export default function SettingsPage() {
|
|||
name: 'Alarm sound',
|
||||
element: (
|
||||
<View
|
||||
key="alarm-sound"
|
||||
key='alarm-sound'
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingLeft: ITEM_PADDING,
|
||||
}}>
|
||||
<Subheading style={{width: 100}}>Alarm sound</Subheading>
|
||||
}}
|
||||
>
|
||||
<Subheading style={{ width: 100 }}>Alarm sound</Subheading>
|
||||
<Button onPress={changeSound}>{soundString || 'Default'}</Button>
|
||||
</View>
|
||||
),
|
||||
|
@ -302,9 +309,10 @@ export default function SettingsPage() {
|
|||
name: 'Export database',
|
||||
element: (
|
||||
<Button
|
||||
key="export-db"
|
||||
style={{alignSelf: 'flex-start'}}
|
||||
onPress={exportDatabase}>
|
||||
key='export-db'
|
||||
style={{ alignSelf: 'flex-start' }}
|
||||
onPress={exportDatabase}
|
||||
>
|
||||
Export database
|
||||
</Button>
|
||||
),
|
||||
|
@ -313,9 +321,10 @@ export default function SettingsPage() {
|
|||
name: 'Import database',
|
||||
element: (
|
||||
<Button
|
||||
key="import-db"
|
||||
style={{alignSelf: 'flex-start'}}
|
||||
onPress={() => setImporting(true)}>
|
||||
key='import-db'
|
||||
style={{ alignSelf: 'flex-start' }}
|
||||
onPress={() => setImporting(true)}
|
||||
>
|
||||
Import database
|
||||
</Button>
|
||||
),
|
||||
|
@ -325,16 +334,16 @@ export default function SettingsPage() {
|
|||
)
|
||||
|
||||
const buttonsMarkup = useMemo(
|
||||
() => buttons.filter(filter).map(b => b.element),
|
||||
() => buttons.filter(filter).map((b) => b.element),
|
||||
[buttons, filter],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<DrawerHeader name="Settings" />
|
||||
<DrawerHeader name='Settings' />
|
||||
|
||||
<Page term={term} search={setTerm} style={{flexGrow: 1}}>
|
||||
<ScrollView style={{marginTop: MARGIN, flex: 1}}>
|
||||
<Page term={term} search={setTerm} style={{ flexGrow: 1 }}>
|
||||
<ScrollView style={{ marginTop: MARGIN, flex: 1 }}>
|
||||
{switchesMarkup}
|
||||
{selectsMarkup}
|
||||
{buttonsMarkup}
|
||||
|
@ -342,10 +351,11 @@ export default function SettingsPage() {
|
|||
</Page>
|
||||
|
||||
<ConfirmDialog
|
||||
title="Are you sure?"
|
||||
title='Are you sure?'
|
||||
onOk={confirmImport}
|
||||
setShow={setImporting}
|
||||
show={importing}>
|
||||
show={importing}
|
||||
>
|
||||
Importing a database overwrites your current data. This action cannot be
|
||||
reversed!
|
||||
</ConfirmDialog>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {useNavigation} from '@react-navigation/native'
|
||||
import {Appbar, IconButton} from 'react-native-paper'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { Appbar, IconButton } from 'react-native-paper'
|
||||
import useDark from './use-dark'
|
||||
|
||||
export default function StackHeader({
|
||||
|
@ -16,7 +16,7 @@ export default function StackHeader({
|
|||
<Appbar.Header>
|
||||
<IconButton
|
||||
color={dark ? 'white' : 'white'}
|
||||
icon="arrow-back"
|
||||
icon='arrow-back'
|
||||
onPress={navigation.goBack}
|
||||
/>
|
||||
<Appbar.Content title={title} />
|
||||
|
|
|
@ -5,25 +5,25 @@ import {
|
|||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useMemo, useRef, useState} from 'react'
|
||||
import {FlatList, NativeModules, TextInput, View} from 'react-native'
|
||||
import {Button, IconButton, ProgressBar} from 'react-native-paper'
|
||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { FlatList, NativeModules, TextInput, View } from 'react-native'
|
||||
import { Button, IconButton, ProgressBar } from 'react-native-paper'
|
||||
import AppInput from './AppInput'
|
||||
import {getBestSet, getLast} from './best.service'
|
||||
import {PADDING} from './constants'
|
||||
import { getBestSet, getLast } from './best.service'
|
||||
import { PADDING } from './constants'
|
||||
import CountMany from './count-many'
|
||||
import {AppDataSource} from './data-source'
|
||||
import {getNow, setRepo, settingsRepo} from './db'
|
||||
import { AppDataSource } from './data-source'
|
||||
import { getNow, setRepo, settingsRepo } from './db'
|
||||
import GymSet from './gym-set'
|
||||
import {PlanPageParams} from './plan-page-params'
|
||||
import { PlanPageParams } from './plan-page-params'
|
||||
import Settings from './settings'
|
||||
import StackHeader from './StackHeader'
|
||||
import StartPlanItem from './StartPlanItem'
|
||||
import {toast} from './toast'
|
||||
import { toast } from './toast'
|
||||
import useDark from './use-dark'
|
||||
|
||||
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 [weight, setWeight] = useState(params.first?.weight.toString() || '0')
|
||||
const [unit, setUnit] = useState<string>(params.first?.unit || 'kg')
|
||||
|
@ -58,7 +58,7 @@ export default function StartPlan() {
|
|||
OFFSET 1
|
||||
`
|
||||
const newCounts = await AppDataSource.manager.query(select)
|
||||
console.log(`${StartPlan.name}.focus:`, {newCounts})
|
||||
console.log(`${StartPlan.name}.focus:`, { newCounts })
|
||||
setCounts(newCounts)
|
||||
}, [workouts])
|
||||
|
||||
|
@ -67,11 +67,11 @@ export default function StartPlan() {
|
|||
setSelected(index)
|
||||
if (!counts && !newCounts) return
|
||||
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)
|
||||
if (!last) return
|
||||
delete last.id
|
||||
console.log(`${StartPlan.name}.select:`, {last})
|
||||
console.log(`${StartPlan.name}.select:`, { last })
|
||||
setReps(last.reps.toString())
|
||||
setWeight(last.weight.toString())
|
||||
setUnit(last.unit)
|
||||
|
@ -81,7 +81,7 @@ export default function StartPlan() {
|
|||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
refresh()
|
||||
}, [refresh]),
|
||||
)
|
||||
|
@ -104,11 +104,12 @@ export default function StartPlan() {
|
|||
if (
|
||||
settings.notify &&
|
||||
(+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
|
||||
const milliseconds =
|
||||
Number(best.minutes) * 60 * 1000 + Number(best.seconds) * 1000
|
||||
const milliseconds = Number(best.minutes) * 60 * 1000 +
|
||||
Number(best.seconds) * 1000
|
||||
NativeModules.AlarmModule.timer(milliseconds)
|
||||
}
|
||||
|
||||
|
@ -117,25 +118,25 @@ export default function StartPlan() {
|
|||
<StackHeader title={params.plan.days.replace(/,/g, ', ')}>
|
||||
<IconButton
|
||||
color={dark ? 'white' : 'white'}
|
||||
onPress={() => navigation.navigate('EditPlan', {plan: params.plan})}
|
||||
icon="edit"
|
||||
onPress={() => navigation.navigate('EditPlan', { plan: params.plan })}
|
||||
icon='edit'
|
||||
/>
|
||||
</StackHeader>
|
||||
<View style={{padding: PADDING, flex: 1, flexDirection: 'column'}}>
|
||||
<View style={{flex: 1}}>
|
||||
<View style={{ padding: PADDING, flex: 1, flexDirection: 'column' }}>
|
||||
<View style={{ flex: 1 }}>
|
||||
<AppInput
|
||||
label="Reps"
|
||||
keyboardType="numeric"
|
||||
label='Reps'
|
||||
keyboardType='numeric'
|
||||
value={reps}
|
||||
onChangeText={setReps}
|
||||
onSubmitEditing={() => weightRef.current?.focus()}
|
||||
selection={selection}
|
||||
onSelectionChange={e => setSelection(e.nativeEvent.selection)}
|
||||
onSelectionChange={(e) => setSelection(e.nativeEvent.selection)}
|
||||
innerRef={repsRef}
|
||||
/>
|
||||
<AppInput
|
||||
label="Weight"
|
||||
keyboardType="numeric"
|
||||
label='Weight'
|
||||
keyboardType='numeric'
|
||||
value={weight}
|
||||
onChangeText={setWeight}
|
||||
onSubmitEditing={handleSubmit}
|
||||
|
@ -144,8 +145,8 @@ export default function StartPlan() {
|
|||
/>
|
||||
{settings?.showUnit && (
|
||||
<AppInput
|
||||
autoCapitalize="none"
|
||||
label="Unit"
|
||||
autoCapitalize='none'
|
||||
label='Unit'
|
||||
value={unit}
|
||||
onChangeText={setUnit}
|
||||
innerRef={unitRef}
|
||||
|
@ -154,7 +155,7 @@ export default function StartPlan() {
|
|||
{counts && (
|
||||
<FlatList
|
||||
data={counts}
|
||||
renderItem={props => (
|
||||
renderItem={(props) => (
|
||||
<View>
|
||||
<StartPlanItem
|
||||
{...props}
|
||||
|
@ -170,7 +171,7 @@ export default function StartPlan() {
|
|||
/>
|
||||
)}
|
||||
</View>
|
||||
<Button mode="contained" icon="save" onPress={handleSubmit}>
|
||||
<Button mode='contained' icon='save' onPress={handleSubmit}>
|
||||
Save
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {NavigationProp, useNavigation} from '@react-navigation/native'
|
||||
import React, {useCallback, useState} from 'react'
|
||||
import {GestureResponderEvent, ListRenderItemInfo, View} from 'react-native'
|
||||
import {List, Menu, RadioButton, useTheme} from 'react-native-paper'
|
||||
import {Like} from 'typeorm'
|
||||
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { GestureResponderEvent, ListRenderItemInfo, View } from 'react-native'
|
||||
import { List, Menu, RadioButton, useTheme } from 'react-native-paper'
|
||||
import { Like } from 'typeorm'
|
||||
import CountMany from './count-many'
|
||||
import {getNow, setRepo} from './db'
|
||||
import {PlanPageParams} from './plan-page-params'
|
||||
import {toast} from './toast'
|
||||
import { getNow, setRepo } from './db'
|
||||
import { PlanPageParams } from './plan-page-params'
|
||||
import { toast } from './toast'
|
||||
|
||||
interface Props extends ListRenderItemInfo<CountMany> {
|
||||
onSelect: (index: number) => void
|
||||
|
@ -15,11 +15,11 @@ interface Props extends ListRenderItemInfo<CountMany> {
|
|||
}
|
||||
|
||||
export default function StartPlanItem(props: Props) {
|
||||
const {index, item, onSelect, selected, onUndo} = props
|
||||
const {colors} = useTheme()
|
||||
const [anchor, setAnchor] = useState({x: 0, y: 0})
|
||||
const { index, item, onSelect, selected, onUndo } = props
|
||||
const { colors } = useTheme()
|
||||
const [anchor, setAnchor] = useState({ x: 0, y: 0 })
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
const {navigate} = useNavigation<NavigationProp<PlanPageParams>>()
|
||||
const { navigate } = useNavigation<NavigationProp<PlanPageParams>>()
|
||||
|
||||
const undo = useCallback(async () => {
|
||||
const now = await getNow()
|
||||
|
@ -30,7 +30,7 @@ export default function StartPlanItem(props: Props) {
|
|||
hidden: 0 as any,
|
||||
created: Like(`${created}%`),
|
||||
},
|
||||
order: {created: 'desc'},
|
||||
order: { created: 'desc' },
|
||||
})
|
||||
setShowMenu(false)
|
||||
if (!first) return toast('Nothing to undo.')
|
||||
|
@ -40,7 +40,7 @@ export default function StartPlanItem(props: Props) {
|
|||
|
||||
const longPress = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
setAnchor({x: e.nativeEvent.pageX, y: e.nativeEvent.pageY})
|
||||
setAnchor({ x: e.nativeEvent.pageX, y: e.nativeEvent.pageY })
|
||||
setShowMenu(true)
|
||||
},
|
||||
[setShowMenu, setAnchor],
|
||||
|
@ -55,25 +55,26 @@ export default function StartPlanItem(props: Props) {
|
|||
hidden: 0 as any,
|
||||
created: Like(`${created}%`),
|
||||
},
|
||||
order: {created: 'desc'},
|
||||
order: { created: 'desc' },
|
||||
})
|
||||
setShowMenu(false)
|
||||
if (!first) return toast('Nothing to edit.')
|
||||
navigate('EditSet', {set: first})
|
||||
navigate('EditSet', { set: first })
|
||||
}
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
onLongPress={longPress}
|
||||
title={item.name}
|
||||
description={
|
||||
item.sets ? `${item.total} / ${item.sets}` : item.total.toString()
|
||||
}
|
||||
description={item.sets
|
||||
? `${item.total} / ${item.sets}`
|
||||
: item.total.toString()}
|
||||
onPress={() => onSelect(index)}
|
||||
left={() => (
|
||||
<View style={{alignItems: 'center', justifyContent: 'center'}}>
|
||||
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
|
||||
<RadioButton
|
||||
onPress={() => onSelect(index)}
|
||||
onPress={() =>
|
||||
onSelect(index)}
|
||||
value={index.toString()}
|
||||
status={selected === index ? 'checked' : 'unchecked'}
|
||||
color={colors.primary}
|
||||
|
@ -85,13 +86,15 @@ export default function StartPlanItem(props: Props) {
|
|||
style={{
|
||||
width: '25%',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
anchor={anchor}
|
||||
visible={showMenu}
|
||||
onDismiss={() => setShowMenu(false)}>
|
||||
<Menu.Item icon="edit" onPress={edit} title="Edit" />
|
||||
<Menu.Item icon="undo" onPress={undo} title="Undo" />
|
||||
onDismiss={() => setShowMenu(false)}
|
||||
>
|
||||
<Menu.Item icon='edit' onPress={edit} title='Edit' />
|
||||
<Menu.Item icon='undo' onPress={undo} title='Undo' />
|
||||
</Menu>
|
||||
</View>
|
||||
)}
|
||||
|
|
15
Switch.tsx
15
Switch.tsx
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
import {Platform, Pressable} from 'react-native'
|
||||
import {Switch as PaperSwitch, Text, useTheme} from 'react-native-paper'
|
||||
import {MARGIN} from './constants'
|
||||
import { Platform, Pressable } from 'react-native'
|
||||
import { Switch as PaperSwitch, Text, useTheme } from 'react-native-paper'
|
||||
import { MARGIN } from './constants'
|
||||
|
||||
function Switch({
|
||||
value,
|
||||
|
@ -12,7 +12,7 @@ function Switch({
|
|||
onChange: (value: boolean) => void
|
||||
title: string
|
||||
}) {
|
||||
const {colors} = useTheme()
|
||||
const { colors } = useTheme()
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
|
@ -22,13 +22,14 @@ function Switch({
|
|||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
marginBottom: Platform.OS === 'ios' ? MARGIN : null,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<PaperSwitch
|
||||
color={colors.primary}
|
||||
style={{marginRight: MARGIN}}
|
||||
style={{ marginRight: MARGIN }}
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
trackColor={{true: colors.primary + '80', false: colors.disabled}}
|
||||
trackColor={{ true: colors.primary + '80', false: colors.disabled }}
|
||||
/>
|
||||
<Text>{title}</Text>
|
||||
</Pressable>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import React, {useCallback, useMemo, useState} from 'react'
|
||||
import {Dimensions, NativeModules, View} from 'react-native'
|
||||
import {Button, Text, useTheme} from 'react-native-paper'
|
||||
import {ProgressCircle} from 'react-native-svg-charts'
|
||||
import { useFocusEffect } from '@react-navigation/native'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Dimensions, NativeModules, View } from 'react-native'
|
||||
import { Button, Text, useTheme } from 'react-native-paper'
|
||||
import { ProgressCircle } from 'react-native-svg-charts'
|
||||
import AppFab from './AppFab'
|
||||
import {MARGIN, PADDING} from './constants'
|
||||
import {settingsRepo} from './db'
|
||||
import { MARGIN, PADDING } from './constants'
|
||||
import { settingsRepo } from './db'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import Settings from './settings'
|
||||
import useTimer from './use-timer'
|
||||
|
@ -16,13 +16,13 @@ export interface TickEvent {
|
|||
}
|
||||
|
||||
export default function TimerPage() {
|
||||
const {minutes, seconds} = useTimer()
|
||||
const { minutes, seconds } = useTimer()
|
||||
const [settings, setSettings] = useState<Settings>()
|
||||
const {colors} = useTheme()
|
||||
const { colors } = useTheme()
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
}, []),
|
||||
)
|
||||
|
||||
|
@ -45,19 +45,20 @@ export default function TimerPage() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DrawerHeader name="Timer" />
|
||||
<View style={{flexGrow: 1, padding: PADDING}}>
|
||||
<DrawerHeader name='Timer' />
|
||||
<View style={{ flexGrow: 1, padding: PADDING }}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text style={{fontSize: 70, position: 'absolute'}}>
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 70, position: 'absolute' }}>
|
||||
{minutes}:{seconds}
|
||||
</Text>
|
||||
<ProgressCircle
|
||||
style={{height: 300, width: 300, marginBottom: MARGIN}}
|
||||
style={{ height: 300, width: 300, marginBottom: MARGIN }}
|
||||
progress={progress}
|
||||
strokeWidth={10}
|
||||
progressColor={colors.primary}
|
||||
|
@ -65,10 +66,10 @@ export default function TimerPage() {
|
|||
/>
|
||||
</View>
|
||||
</View>
|
||||
<Button onPress={add} style={{position: 'absolute', top: '82%', left}}>
|
||||
<Button onPress={add} style={{ position: 'absolute', top: '82%', left }}>
|
||||
Add 1 min
|
||||
</Button>
|
||||
<AppFab icon="stop" onPress={stop} />
|
||||
<AppFab icon='stop' onPress={stop} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
86
ViewBest.tsx
86
ViewBest.tsx
|
@ -1,25 +1,25 @@
|
|||
import {RouteProp, useRoute} from '@react-navigation/native'
|
||||
import {format} from 'date-fns'
|
||||
import {useEffect, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {FileSystem} from 'react-native-file-access'
|
||||
import {IconButton, List} from 'react-native-paper'
|
||||
import { RouteProp, useRoute } from '@react-navigation/native'
|
||||
import { format } from 'date-fns'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { FileSystem } from 'react-native-file-access'
|
||||
import { IconButton, List } from 'react-native-paper'
|
||||
import Share from 'react-native-share'
|
||||
import {captureScreen} from 'react-native-view-shot'
|
||||
import {BestPageParams} from './BestPage'
|
||||
import { captureScreen } from 'react-native-view-shot'
|
||||
import { BestPageParams } from './BestPage'
|
||||
import Chart from './Chart'
|
||||
import {PADDING} from './constants'
|
||||
import {setRepo} from './db'
|
||||
import { PADDING } from './constants'
|
||||
import { setRepo } from './db'
|
||||
import GymSet from './gym-set'
|
||||
import {Metrics} from './metrics'
|
||||
import {Periods} from './periods'
|
||||
import { Metrics } from './metrics'
|
||||
import { Periods } from './periods'
|
||||
import Select from './Select'
|
||||
import StackHeader from './StackHeader'
|
||||
import useDark from './use-dark'
|
||||
import Volume from './volume'
|
||||
|
||||
export default function ViewBest() {
|
||||
const {params} = useRoute<RouteProp<BestPageParams, 'ViewBest'>>()
|
||||
const { params } = useRoute<RouteProp<BestPageParams, 'ViewBest'>>()
|
||||
const [weights, setWeights] = useState<GymSet[]>()
|
||||
const [volumes, setVolumes] = useState<Volume[]>()
|
||||
const [metric, setMetric] = useState(Metrics.Weight)
|
||||
|
@ -34,11 +34,11 @@ export default function ViewBest() {
|
|||
if (period === Periods.Yearly) group = '%Y-%m'
|
||||
const builder = setRepo
|
||||
.createQueryBuilder()
|
||||
.select("STRFTIME('%Y-%m-%d', created)", 'created')
|
||||
.select('STRFTIME(\'%Y-%m-%d\', created)', 'created')
|
||||
.addSelect('unit')
|
||||
.where('name = :name', {name: params.best.name})
|
||||
.where('name = :name', { name: params.best.name })
|
||||
.andWhere('NOT hidden')
|
||||
.andWhere("DATE(created) >= DATE('now', 'weekday 0', :difference)", {
|
||||
.andWhere('DATE(created) >= DATE(\'now\', \'weekday 0\', :difference)', {
|
||||
difference,
|
||||
})
|
||||
.groupBy('name')
|
||||
|
@ -64,8 +64,8 @@ export default function ViewBest() {
|
|||
'weight',
|
||||
)
|
||||
.getRawMany()
|
||||
.then(newWeights => {
|
||||
console.log({weights: newWeights})
|
||||
.then((newWeights) => {
|
||||
console.log({ weights: newWeights })
|
||||
setWeights(newWeights)
|
||||
})
|
||||
}
|
||||
|
@ -76,32 +76,31 @@ export default function ViewBest() {
|
|||
(metric === Metrics.Volume && volumes?.length === 0) ||
|
||||
(metric === Metrics.Weight && weights?.length === 0) ||
|
||||
(metric === Metrics.OneRepMax && weights?.length === 0)
|
||||
)
|
||||
return <List.Item title="No data yet." />
|
||||
if (metric === Metrics.Volume && volumes?.length && weights?.length)
|
||||
) {
|
||||
return <List.Item title='No data yet.' />
|
||||
}
|
||||
if (metric === Metrics.Volume && volumes?.length && weights?.length) {
|
||||
return (
|
||||
<Chart
|
||||
yData={volumes.map(v => v.value)}
|
||||
yData={volumes.map((v) => v.value)}
|
||||
yFormat={(value: number) =>
|
||||
`${value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}${
|
||||
volumes[0].unit || 'kg'
|
||||
}`
|
||||
}
|
||||
}`}
|
||||
xData={weights}
|
||||
xFormat={(_value, index) =>
|
||||
format(new Date(weights[index].created), 'd/M')
|
||||
}
|
||||
format(new Date(weights[index].created), 'd/M')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Chart
|
||||
yData={weights?.map(set => set.weight) || []}
|
||||
yFormat={value => `${value}${weights?.[0].unit}`}
|
||||
yData={weights?.map((set) => set.weight) || []}
|
||||
yFormat={(value) => `${value}${weights?.[0].unit}`}
|
||||
xData={weights || []}
|
||||
xFormat={(_value, index) =>
|
||||
format(new Date(weights?.[index].created), 'd/M')
|
||||
}
|
||||
format(new Date(weights?.[index].created), 'd/M')}
|
||||
/>
|
||||
)
|
||||
}, [volumes, weights, metric])
|
||||
|
@ -112,40 +111,39 @@ export default function ViewBest() {
|
|||
<IconButton
|
||||
color={dark ? 'white' : 'white'}
|
||||
onPress={() =>
|
||||
captureScreen().then(async uri => {
|
||||
captureScreen().then(async (uri) => {
|
||||
const base64 = await FileSystem.readFile(uri, 'base64')
|
||||
const url = `data:image/jpeg;base64,${base64}`
|
||||
Share.open({
|
||||
type: 'image/jpeg',
|
||||
url,
|
||||
})
|
||||
})
|
||||
}
|
||||
icon="share"
|
||||
})}
|
||||
icon='share'
|
||||
/>
|
||||
</StackHeader>
|
||||
<View style={{padding: PADDING}}>
|
||||
<View style={{ padding: PADDING }}>
|
||||
<Select
|
||||
label="Metric"
|
||||
label='Metric'
|
||||
items={[
|
||||
{value: Metrics.Volume, label: Metrics.Volume},
|
||||
{value: Metrics.OneRepMax, label: Metrics.OneRepMax},
|
||||
{ value: Metrics.Volume, label: Metrics.Volume },
|
||||
{ value: Metrics.OneRepMax, label: Metrics.OneRepMax },
|
||||
{
|
||||
label: Metrics.Weight,
|
||||
value: Metrics.Weight,
|
||||
},
|
||||
]}
|
||||
onChange={value => setMetric(value as Metrics)}
|
||||
onChange={(value) => setMetric(value as Metrics)}
|
||||
value={metric}
|
||||
/>
|
||||
<Select
|
||||
label="Period"
|
||||
label='Period'
|
||||
items={[
|
||||
{value: Periods.Weekly, label: Periods.Weekly},
|
||||
{value: Periods.Monthly, label: Periods.Monthly},
|
||||
{value: Periods.Yearly, label: Periods.Yearly},
|
||||
{ value: Periods.Weekly, label: Periods.Weekly },
|
||||
{ value: Periods.Monthly, label: Periods.Monthly },
|
||||
{ value: Periods.Yearly, label: Periods.Yearly },
|
||||
]}
|
||||
onChange={value => setPeriod(value as Periods)}
|
||||
onChange={(value) => setPeriod(value as Periods)}
|
||||
value={period}
|
||||
/>
|
||||
{charts}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {NavigationProp, useNavigation} from '@react-navigation/native'
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {GestureResponderEvent, Image} from 'react-native'
|
||||
import {List, Menu, Text} from 'react-native-paper'
|
||||
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { GestureResponderEvent, Image } from 'react-native'
|
||||
import { List, Menu, Text } from 'react-native-paper'
|
||||
import ConfirmDialog from './ConfirmDialog'
|
||||
import {setRepo} from './db'
|
||||
import { setRepo } from './db'
|
||||
import GymSet from './gym-set'
|
||||
import {WorkoutsPageParams} from './WorkoutsPage'
|
||||
import { WorkoutsPageParams } from './WorkoutsPage'
|
||||
|
||||
export default function WorkoutItem({
|
||||
item,
|
||||
|
@ -17,19 +17,19 @@ export default function WorkoutItem({
|
|||
images: boolean
|
||||
}) {
|
||||
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 navigation = useNavigation<NavigationProp<WorkoutsPageParams>>()
|
||||
|
||||
const remove = useCallback(async () => {
|
||||
await setRepo.delete({name: item.name})
|
||||
await setRepo.delete({ name: item.name })
|
||||
setShowMenu(false)
|
||||
onRemove()
|
||||
}, [setShowMenu, onRemove, item.name])
|
||||
|
||||
const longPress = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
setAnchor({x: e.nativeEvent.pageX, y: e.nativeEvent.pageY})
|
||||
setAnchor({ x: e.nativeEvent.pageX, y: e.nativeEvent.pageY })
|
||||
setShowMenu(true)
|
||||
},
|
||||
[setShowMenu, setAnchor],
|
||||
|
@ -43,32 +43,36 @@ export default function WorkoutItem({
|
|||
return (
|
||||
<>
|
||||
<List.Item
|
||||
onPress={() => navigation.navigate('EditWorkout', {value: item})}
|
||||
onPress={() => navigation.navigate('EditWorkout', { value: item })}
|
||||
title={item.name}
|
||||
description={description}
|
||||
onLongPress={longPress}
|
||||
left={() =>
|
||||
images &&
|
||||
item.image && (
|
||||
<Image source={{uri: item.image}} style={{height: 75, width: 75}} />
|
||||
)
|
||||
}
|
||||
<Image
|
||||
source={{ uri: item.image }}
|
||||
style={{ height: 75, width: 75 }}
|
||||
/>
|
||||
)}
|
||||
right={() => (
|
||||
<Text
|
||||
style={{
|
||||
alignSelf: 'center',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
anchor={anchor}
|
||||
visible={showMenu}
|
||||
onDismiss={() => setShowMenu(false)}>
|
||||
onDismiss={() => setShowMenu(false)}
|
||||
>
|
||||
<Menu.Item
|
||||
icon="delete"
|
||||
icon='delete'
|
||||
onPress={() => {
|
||||
setShowRemove(item.name)
|
||||
setShowMenu(false)
|
||||
}}
|
||||
title="Delete"
|
||||
title='Delete'
|
||||
/>
|
||||
</Menu>
|
||||
</Text>
|
||||
|
@ -77,8 +81,9 @@ export default function WorkoutItem({
|
|||
<ConfirmDialog
|
||||
title={`Delete ${showRemove}`}
|
||||
show={!!showRemove}
|
||||
setShow={show => (show ? null : setShowRemove(''))}
|
||||
onOk={remove}>
|
||||
setShow={(show) => (show ? null : setShowRemove(''))}
|
||||
onOk={remove}
|
||||
>
|
||||
This irreversibly deletes ALL sets related to this workout. Are you
|
||||
sure?
|
||||
</ConfirmDialog>
|
||||
|
|
|
@ -3,17 +3,17 @@ import {
|
|||
useFocusEffect,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native'
|
||||
import {useCallback, useState} from 'react'
|
||||
import {FlatList} from 'react-native'
|
||||
import {List} from 'react-native-paper'
|
||||
import {setRepo, settingsRepo} from './db'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { FlatList } from 'react-native'
|
||||
import { List } from 'react-native-paper'
|
||||
import { setRepo, settingsRepo } from './db'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import GymSet from './gym-set'
|
||||
import Page from './Page'
|
||||
import SetList from './SetList'
|
||||
import Settings from './settings'
|
||||
import WorkoutItem from './WorkoutItem'
|
||||
import {WorkoutsPageParams} from './WorkoutsPage'
|
||||
import { WorkoutsPageParams } from './WorkoutsPage'
|
||||
|
||||
const limit = 15
|
||||
|
||||
|
@ -29,12 +29,12 @@ export default function WorkoutList() {
|
|||
const newWorkouts = await setRepo
|
||||
.createQueryBuilder()
|
||||
.select()
|
||||
.where('name LIKE :name', {name: `%${value.trim()}%`})
|
||||
.where('name LIKE :name', { name: `%${value.trim()}%` })
|
||||
.groupBy('name')
|
||||
.orderBy('name')
|
||||
.limit(limit)
|
||||
.getMany()
|
||||
console.log(`${WorkoutList.name}`, {newWorkout: newWorkouts[0]})
|
||||
console.log(`${WorkoutList.name}`, { newWorkout: newWorkouts[0] })
|
||||
setWorkouts(newWorkouts)
|
||||
setOffset(0)
|
||||
setEnd(false)
|
||||
|
@ -43,12 +43,12 @@ export default function WorkoutList() {
|
|||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
refresh(term)
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
settingsRepo.findOne({ where: {} }).then(setSettings)
|
||||
}, [refresh, term]),
|
||||
)
|
||||
|
||||
const renderItem = useCallback(
|
||||
({item}: {item: GymSet}) => (
|
||||
({ item }: { item: GymSet }) => (
|
||||
<WorkoutItem
|
||||
images={settings?.images}
|
||||
item={item}
|
||||
|
@ -71,7 +71,7 @@ export default function WorkoutList() {
|
|||
const newWorkouts = await setRepo
|
||||
.createQueryBuilder()
|
||||
.select()
|
||||
.where('name LIKE :name', {name: `%${term.trim()}%`})
|
||||
.where('name LIKE :name', { name: `%${term.trim()}%` })
|
||||
.groupBy('name')
|
||||
.orderBy('name')
|
||||
.limit(limit)
|
||||
|
@ -100,19 +100,21 @@ export default function WorkoutList() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DrawerHeader name="Workouts" />
|
||||
<DrawerHeader name='Workouts' />
|
||||
<Page onAdd={onAdd} term={term} search={search}>
|
||||
{workouts?.length === 0 ? (
|
||||
{workouts?.length === 0
|
||||
? (
|
||||
<List.Item
|
||||
title="No workouts yet."
|
||||
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}}
|
||||
style={{ flex: 1 }}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={w => w.name}
|
||||
keyExtractor={(w) => w.name}
|
||||
onEndReached={next}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import EditWorkout from './EditWorkout'
|
||||
import GymSet from './gym-set'
|
||||
import WorkoutList from './WorkoutList'
|
||||
|
@ -15,9 +15,10 @@ const Stack = createStackNavigator<WorkoutsPageParams>()
|
|||
export default function WorkoutsPage() {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{headerShown: false, animationEnabled: false}}>
|
||||
<Stack.Screen name="WorkoutList" component={WorkoutList} />
|
||||
<Stack.Screen name="EditWorkout" component={EditWorkout} />
|
||||
screenOptions={{ headerShown: false, animationEnabled: false }}
|
||||
>
|
||||
<Stack.Screen name='WorkoutList' component={WorkoutList} />
|
||||
<Stack.Screen name='EditWorkout' component={EditWorkout} />
|
||||
</Stack.Navigator>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {setRepo} from './db'
|
||||
import { setRepo } from './db'
|
||||
import GymSet from './gym-set'
|
||||
|
||||
export const getBestSet = async (name: string): Promise<GymSet> => {
|
||||
|
@ -6,7 +6,7 @@ export const getBestSet = async (name: string): Promise<GymSet> => {
|
|||
.createQueryBuilder()
|
||||
.select()
|
||||
.addSelect('MAX(weight)', 'weight')
|
||||
.where('name = :name', {name})
|
||||
.where('name = :name', { name })
|
||||
.groupBy('name')
|
||||
.addGroupBy('reps')
|
||||
.orderBy('weight', 'DESC')
|
||||
|
@ -17,10 +17,10 @@ export const getBestSet = async (name: string): Promise<GymSet> => {
|
|||
export const getLast = async (name: string): Promise<GymSet> => {
|
||||
return setRepo
|
||||
.createQueryBuilder()
|
||||
.where('name = :name', {name})
|
||||
.where('name = :name', { name })
|
||||
.andWhere('reps >= 5')
|
||||
.andWhere("strftime('%Y-%m-%d', 'now', 'localtime') > created")
|
||||
.groupBy("STRFTIME('%Y-%m-%d', created)")
|
||||
.andWhere('strftime(\'%Y-%m-%d\', \'now\', \'localtime\') > created')
|
||||
.groupBy('STRFTIME(\'%Y-%m-%d\', created)')
|
||||
.orderBy('created', 'DESC')
|
||||
.select('reps')
|
||||
.addSelect('MAX(weight) as weight')
|
||||
|
|
25
colors.ts
25
colors.ts
|
@ -1,25 +1,26 @@
|
|||
import {DarkTheme, DefaultTheme} from 'react-native-paper'
|
||||
import { DarkTheme, DefaultTheme } from 'react-native-paper'
|
||||
|
||||
export const lightColors = [
|
||||
{hex: DarkTheme.colors.primary, name: 'Purple'},
|
||||
{hex: '#B3E5FC', name: 'Blue'},
|
||||
{hex: '#FA8072', name: 'Salmon'},
|
||||
{hex: '#FFC0CB', name: 'Pink'},
|
||||
{hex: '#E9DCC9', name: 'Linen'},
|
||||
{ hex: DarkTheme.colors.primary, name: 'Purple' },
|
||||
{ hex: '#B3E5FC', name: 'Blue' },
|
||||
{ hex: '#FA8072', name: 'Salmon' },
|
||||
{ hex: '#FFC0CB', name: 'Pink' },
|
||||
{ hex: '#E9DCC9', name: 'Linen' },
|
||||
]
|
||||
|
||||
export const darkColors = [
|
||||
{hex: DefaultTheme.colors.primary, name: 'Purple'},
|
||||
{hex: '#0051a9', name: 'Blue'},
|
||||
{hex: '#000000', name: 'Black'},
|
||||
{hex: '#863c3c', name: 'Red'},
|
||||
{hex: '#1c6000', name: 'Kermit'},
|
||||
{ hex: DefaultTheme.colors.primary, name: 'Purple' },
|
||||
{ hex: '#0051a9', name: 'Blue' },
|
||||
{ hex: '#000000', name: 'Black' },
|
||||
{ hex: '#863c3c', name: 'Red' },
|
||||
{ hex: '#1c6000', name: 'Kermit' },
|
||||
]
|
||||
|
||||
export const colorShade = (color: any, amount: number) => {
|
||||
color = color.replace(/^#/, '')
|
||||
if (color.length === 3)
|
||||
if (color.length === 3) {
|
||||
color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2]
|
||||
}
|
||||
|
||||
let [r, g, b] = color.match(/.{2}/g)
|
||||
;[r, g, b] = [
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
import {DataSource} from 'typeorm'
|
||||
import { DataSource } from 'typeorm'
|
||||
import GymSet from './gym-set'
|
||||
import {Sets1667185586014 as sets1667185586014} from './migrations/1667185586014-sets'
|
||||
import {plans1667186124792} from './migrations/1667186124792-plans'
|
||||
import {settings1667186130041} from './migrations/1667186130041-settings'
|
||||
import {addSound1667186139844} from './migrations/1667186139844-add-sound'
|
||||
import {addHidden1667186159379} from './migrations/1667186159379-add-hidden'
|
||||
import {addNotify1667186166140} from './migrations/1667186166140-add-notify'
|
||||
import {addImage1667186171548} from './migrations/1667186171548-add-image'
|
||||
import {addImages1667186179488} from './migrations/1667186179488-add-images'
|
||||
import {insertSettings1667186203827} from './migrations/1667186203827-insert-settings'
|
||||
import {addSteps1667186211251} from './migrations/1667186211251-add-steps'
|
||||
import {addSets1667186250618} from './migrations/1667186250618-add-sets'
|
||||
import {addMinutes1667186255650} from './migrations/1667186255650-add-minutes'
|
||||
import {addSeconds1667186259174} from './migrations/1667186259174-add-seconds'
|
||||
import {addShowUnit1667186265588} from './migrations/1667186265588-add-show-unit'
|
||||
import {addColor1667186320954} from './migrations/1667186320954-add-color'
|
||||
import {addSteps1667186348425} from './migrations/1667186348425-add-steps'
|
||||
import {addDate1667186431804} from './migrations/1667186431804-add-date'
|
||||
import {addShowDate1667186435051} from './migrations/1667186435051-add-show-date'
|
||||
import {addTheme1667186439366} from './migrations/1667186439366-add-theme'
|
||||
import {addShowSets1667186443614} from './migrations/1667186443614-add-show-sets'
|
||||
import {addSetsCreated1667186451005} from './migrations/1667186451005-add-sets-created'
|
||||
import {addNoSound1667186456118} from './migrations/1667186456118-add-no-sound'
|
||||
import {dropMigrations1667190214743} from './migrations/1667190214743-drop-migrations'
|
||||
import {splitColor1669420187764} from './migrations/1669420187764-split-color'
|
||||
import {addBackup1678334268359} from './migrations/1678334268359-add-backup'
|
||||
import {Plan} from './plan'
|
||||
import { Sets1667185586014 as sets1667185586014 } from './migrations/1667185586014-sets'
|
||||
import { plans1667186124792 } from './migrations/1667186124792-plans'
|
||||
import { settings1667186130041 } from './migrations/1667186130041-settings'
|
||||
import { addSound1667186139844 } from './migrations/1667186139844-add-sound'
|
||||
import { addHidden1667186159379 } from './migrations/1667186159379-add-hidden'
|
||||
import { addNotify1667186166140 } from './migrations/1667186166140-add-notify'
|
||||
import { addImage1667186171548 } from './migrations/1667186171548-add-image'
|
||||
import { addImages1667186179488 } from './migrations/1667186179488-add-images'
|
||||
import { insertSettings1667186203827 } from './migrations/1667186203827-insert-settings'
|
||||
import { addSteps1667186211251 } from './migrations/1667186211251-add-steps'
|
||||
import { addSets1667186250618 } from './migrations/1667186250618-add-sets'
|
||||
import { addMinutes1667186255650 } from './migrations/1667186255650-add-minutes'
|
||||
import { addSeconds1667186259174 } from './migrations/1667186259174-add-seconds'
|
||||
import { addShowUnit1667186265588 } from './migrations/1667186265588-add-show-unit'
|
||||
import { addColor1667186320954 } from './migrations/1667186320954-add-color'
|
||||
import { addSteps1667186348425 } from './migrations/1667186348425-add-steps'
|
||||
import { addDate1667186431804 } from './migrations/1667186431804-add-date'
|
||||
import { addShowDate1667186435051 } from './migrations/1667186435051-add-show-date'
|
||||
import { addTheme1667186439366 } from './migrations/1667186439366-add-theme'
|
||||
import { addShowSets1667186443614 } from './migrations/1667186443614-add-show-sets'
|
||||
import { addSetsCreated1667186451005 } from './migrations/1667186451005-add-sets-created'
|
||||
import { addNoSound1667186456118 } from './migrations/1667186456118-add-no-sound'
|
||||
import { dropMigrations1667190214743 } from './migrations/1667190214743-drop-migrations'
|
||||
import { splitColor1669420187764 } from './migrations/1669420187764-split-color'
|
||||
import { addBackup1678334268359 } from './migrations/1678334268359-add-backup'
|
||||
import { Plan } from './plan'
|
||||
import Settings from './settings'
|
||||
|
||||
export const AppDataSource = new DataSource({
|
||||
|
|
6
db.ts
6
db.ts
|
@ -1,6 +1,6 @@
|
|||
import {AppDataSource} from './data-source'
|
||||
import { AppDataSource } from './data-source'
|
||||
import GymSet from './gym-set'
|
||||
import {Plan} from './plan'
|
||||
import { Plan } from './plan'
|
||||
import Settings from './settings'
|
||||
|
||||
export const setRepo = AppDataSource.manager.getRepository(GymSet)
|
||||
|
@ -9,7 +9,7 @@ export const settingsRepo = AppDataSource.manager.getRepository(Settings)
|
|||
|
||||
export const getNow = async (): Promise<string> => {
|
||||
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
|
||||
}
|
||||
|
|
11
deno.json
Normal file
11
deno.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"fmt": {
|
||||
"useTabs": false,
|
||||
"lineWidth": 80,
|
||||
"semiColons": false,
|
||||
"singleQuote": true,
|
||||
"proseWrap": "preserve",
|
||||
"include": ["src/"],
|
||||
"exclude": ["src/testdata/", "data/fixtures/**/*.ts"]
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {Column, Entity, PrimaryGeneratedColumn} from 'typeorm'
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity('sets')
|
||||
export default class GymSet {
|
||||
|
|
2
input.ts
2
input.ts
|
@ -1,4 +1,4 @@
|
|||
import {Item} from './Select'
|
||||
import { Item } from './Select'
|
||||
import Settings from './settings'
|
||||
|
||||
export default interface Input<T> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {NativeModules} from 'react-native'
|
||||
import { NativeModules } from 'react-native'
|
||||
import 'react-native-gesture-handler/jestSetup'
|
||||
|
||||
NativeModules.RNViewShot = NativeModules.RNViewShot || {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {NavigationContainer} from '@react-navigation/native'
|
||||
import { NavigationContainer } from '@react-navigation/native'
|
||||
import React from 'react'
|
||||
import {
|
||||
DarkTheme,
|
||||
|
@ -6,14 +6,14 @@ import {
|
|||
Provider as PaperProvider,
|
||||
} from 'react-native-paper'
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons'
|
||||
import {ThemeContext} from './use-theme'
|
||||
import { ThemeContext } from './use-theme'
|
||||
|
||||
export const MockProviders = ({
|
||||
children,
|
||||
}: {
|
||||
children: JSX.Element | JSX.Element[]
|
||||
}) => (
|
||||
<PaperProvider settings={{icon: props => <MaterialIcon {...props} />}}>
|
||||
<PaperProvider settings={{ icon: (props) => <MaterialIcon {...props} /> }}>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
theme: 'system',
|
||||
|
@ -22,7 +22,8 @@ export const MockProviders = ({
|
|||
darkColor: DarkTheme.colors.primary,
|
||||
setLightColor: jest.fn(),
|
||||
setDarkColor: jest.fn(),
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<NavigationContainer>{children}</NavigationContainer>
|
||||
</ThemeContext.Provider>
|
||||
</PaperProvider>
|
||||
|
|
12
options.ts
12
options.ts
|
@ -1,18 +1,18 @@
|
|||
import {darkColors, lightColors} from './colors'
|
||||
import { darkColors, lightColors } from './colors'
|
||||
|
||||
export const themeOptions = [
|
||||
{label: 'System', value: 'system'},
|
||||
{label: 'Dark', value: 'dark'},
|
||||
{label: 'Light', value: 'light'},
|
||||
{ label: 'System', value: 'system' },
|
||||
{ label: 'Dark', value: 'dark' },
|
||||
{ label: 'Light', value: 'light' },
|
||||
]
|
||||
|
||||
export const lightOptions = lightColors.map(color => ({
|
||||
export const lightOptions = lightColors.map((color) => ({
|
||||
label: color.name,
|
||||
value: color.hex,
|
||||
color: color.hex,
|
||||
}))
|
||||
|
||||
export const darkOptions = darkColors.map(color => ({
|
||||
export const darkOptions = darkColors.map((color) => ({
|
||||
label: color.name,
|
||||
value: color.hex,
|
||||
color: color.hex,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
organize-imports-cli *.ts* tests/*.ts* && prettier --write *.ts* tests/*.ts*
|
||||
organize-imports-cli *.ts* tests/*.ts* && deno fmt *.ts* tests/*.ts*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import GymSet from './gym-set'
|
||||
import {Plan} from './plan'
|
||||
import { Plan } from './plan'
|
||||
|
||||
export type PlanPageParams = {
|
||||
PlanList: {}
|
||||
|
|
2
plan.ts
2
plan.ts
|
@ -1,4 +1,4 @@
|
|||
import {Column, Entity, PrimaryGeneratedColumn} from 'typeorm'
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity('plans')
|
||||
export class Plan {
|
||||
|
|
2
route.ts
2
route.ts
|
@ -1,4 +1,4 @@
|
|||
import {DrawerParamList} from './drawer-param-list'
|
||||
import { DrawerParamList } from './drawer-param-list'
|
||||
|
||||
export default interface Route {
|
||||
name: keyof DrawerParamList
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Column, Entity, PrimaryColumn} from 'typeorm'
|
||||
import { Column, Entity, PrimaryColumn } from 'typeorm'
|
||||
|
||||
@Entity()
|
||||
export default class Settings {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import App from '../App'
|
||||
import Settings from '../settings'
|
||||
|
||||
|
@ -18,7 +18,7 @@ jest.mock('../data-source.ts', () => ({
|
|||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getAllByText} = render(<App />)
|
||||
const { getAllByText } = render(<App />)
|
||||
const title = await waitFor(() => getAllByText('Home'))
|
||||
expect(title.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react'
|
||||
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 {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import Settings from '../settings'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
|
@ -34,17 +34,17 @@ jest.mock('../db.ts', () => ({
|
|||
reps: 10,
|
||||
image: 'https://picsum.photos/id/1/1000/600',
|
||||
},
|
||||
]),
|
||||
])
|
||||
),
|
||||
}),
|
||||
},
|
||||
settingsRepo: {
|
||||
findOne: () => Promise.resolve({images: true} as Settings),
|
||||
findOne: () => Promise.resolve({ images: true } as Settings),
|
||||
},
|
||||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getByText} = render(
|
||||
const { getByText } = render(
|
||||
<MockProviders>
|
||||
<BestPage />
|
||||
</MockProviders>,
|
||||
|
@ -54,7 +54,7 @@ test('renders correctly', async () => {
|
|||
})
|
||||
|
||||
test('searches', async () => {
|
||||
const {getByDisplayValue, getByPlaceholder} = render(
|
||||
const { getByDisplayValue, getByPlaceholder } = render(
|
||||
<MockProviders>
|
||||
<BestPage />
|
||||
</MockProviders>,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import EditPlan from '../EditPlan'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import {Plan} from '../plan'
|
||||
import {PlanPageParams} from '../plan-page-params'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import { Plan } from '../plan'
|
||||
import { PlanPageParams } from '../plan-page-params'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
setRepo: {
|
||||
|
@ -15,10 +15,10 @@ jest.mock('../db.ts', () => ({
|
|||
orderBy: jest.fn().mockReturnThis(),
|
||||
getRawMany: jest.fn(() =>
|
||||
Promise.resolve([
|
||||
{name: 'Bench press'},
|
||||
{name: 'Bicep curls'},
|
||||
{name: 'Rows'},
|
||||
]),
|
||||
{ name: 'Bench press' },
|
||||
{ name: 'Bicep curls' },
|
||||
{ name: 'Rows' },
|
||||
])
|
||||
),
|
||||
}),
|
||||
},
|
||||
|
@ -26,7 +26,7 @@ jest.mock('../db.ts', () => ({
|
|||
|
||||
test('renders correctly', async () => {
|
||||
const Stack = createStackNavigator<PlanPageParams>()
|
||||
const {getByText, getAllByText} = render(
|
||||
const { getByText, getAllByText } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
|
@ -37,7 +37,7 @@ test('renders correctly', async () => {
|
|||
id: 1,
|
||||
} as Plan,
|
||||
}}
|
||||
name="EditPlan"
|
||||
name='EditPlan'
|
||||
component={EditPlan}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import React from 'react'
|
||||
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 GymSet from '../gym-set'
|
||||
import {HomePageParams} from '../home-page-params'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { HomePageParams } from '../home-page-params'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import SetList from '../SetList'
|
||||
import Settings from '../settings'
|
||||
|
||||
|
@ -27,7 +27,7 @@ jest.mock('../db.ts', () => ({
|
|||
|
||||
test('renders correctly', async () => {
|
||||
const Stack = createStackNavigator<HomePageParams>()
|
||||
const {getByText, getAllByText} = render(
|
||||
const { getByText, getAllByText } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
|
@ -37,7 +37,7 @@ test('renders correctly', async () => {
|
|||
id: 1,
|
||||
} as GymSet,
|
||||
}}
|
||||
name="EditSet"
|
||||
name='EditSet'
|
||||
component={EditSet}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
@ -55,10 +55,10 @@ test('renders correctly', async () => {
|
|||
|
||||
test('saves', async () => {
|
||||
const Stack = createStackNavigator<HomePageParams>()
|
||||
const {getByText, getAllByText, getByTestId} = render(
|
||||
const { getByText, getAllByText, getByTestId } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen name="Sets" component={SetList} />
|
||||
<Stack.Screen name='Sets' component={SetList} />
|
||||
<Stack.Screen
|
||||
initialParams={{
|
||||
set: {
|
||||
|
@ -66,7 +66,7 @@ test('saves', async () => {
|
|||
id: 1,
|
||||
} as GymSet,
|
||||
}}
|
||||
name="EditSet"
|
||||
name='EditSet'
|
||||
component={EditSet}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import React from 'react'
|
||||
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 {HomePageParams} from '../home-page-params'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { HomePageParams } from '../home-page-params'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
|
||||
const mockGoBack = jest.fn()
|
||||
|
||||
|
@ -20,9 +20,9 @@ jest.mock('../db.ts', () => ({
|
|||
setRepo: {
|
||||
find: () =>
|
||||
Promise.resolve([
|
||||
{name: 'Bench press', reps: 8, weight: 60, id: 1},
|
||||
{name: 'Bench press', reps: 6, weight: 70, id: 2},
|
||||
{name: 'Bench press', reps: 4, weight: 85, id: 3},
|
||||
{ name: 'Bench press', reps: 8, weight: 60, id: 1 },
|
||||
{ name: 'Bench press', reps: 6, weight: 70, id: 2 },
|
||||
{ name: 'Bench press', reps: 4, weight: 85, id: 3 },
|
||||
]),
|
||||
update: jest.fn(() => Promise.resolve()),
|
||||
},
|
||||
|
@ -38,12 +38,12 @@ jest.mock('../db.ts', () => ({
|
|||
|
||||
test('renders correctly', async () => {
|
||||
const Stack = createStackNavigator<HomePageParams>()
|
||||
const {getByText, getAllByText} = render(
|
||||
const { getByText, getAllByText } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
initialParams={{ids: [1, 2, 3]}}
|
||||
name="EditSets"
|
||||
initialParams={{ ids: [1, 2, 3] }}
|
||||
name='EditSets'
|
||||
component={EditSets}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
@ -60,12 +60,12 @@ test('renders correctly', async () => {
|
|||
|
||||
test('saves', async () => {
|
||||
const Stack = createStackNavigator<HomePageParams>()
|
||||
const {getByText, getAllByText} = render(
|
||||
const { getByText, getAllByText } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
initialParams={{ids: [1, 2, 3]}}
|
||||
name="EditSets"
|
||||
initialParams={{ ids: [1, 2, 3] }}
|
||||
name='EditSets'
|
||||
component={EditSets}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import EditWorkout from '../EditWorkout'
|
||||
import GymSet from '../gym-set'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import Settings from '../settings'
|
||||
import {WorkoutsPageParams} from '../WorkoutsPage'
|
||||
import { WorkoutsPageParams } from '../WorkoutsPage'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
settingsRepo: {
|
||||
|
@ -20,14 +20,14 @@ jest.mock('../db.ts', () => ({
|
|||
|
||||
test('renders correctly', async () => {
|
||||
const Stack = createStackNavigator<WorkoutsPageParams>()
|
||||
const {getByText, getAllByText} = render(
|
||||
const { getByText, getAllByText } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
initialParams={{
|
||||
value: {name: 'Bench press'} as GymSet,
|
||||
value: { name: 'Bench press' } as GymSet,
|
||||
}}
|
||||
name="EditWorkout"
|
||||
name='EditWorkout'
|
||||
component={EditWorkout}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import {Repository} from 'typeorm'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import { Repository } from 'typeorm'
|
||||
import GymSet from '../gym-set'
|
||||
import HomePage from '../HomePage'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import Settings from '../settings'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>,
|
||||
setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
|
||||
settingsRepo: {
|
||||
findOne: () => Promise.resolve({} as Settings),
|
||||
},
|
||||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getByText} = render(
|
||||
const { getByText } = render(
|
||||
<MockProviders>
|
||||
<HomePage />
|
||||
</MockProviders>,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {fireEvent, render, waitFor} from 'react-native-testing-library'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import {Plan} from '../plan'
|
||||
import { fireEvent, render, waitFor } from 'react-native-testing-library'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import { Plan } from '../plan'
|
||||
import PlanPage from '../PlanPage'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
|
@ -26,7 +26,7 @@ jest.mock('../db.ts', () => ({
|
|||
{
|
||||
name: 'Rows',
|
||||
},
|
||||
]),
|
||||
])
|
||||
),
|
||||
}),
|
||||
},
|
||||
|
@ -48,7 +48,7 @@ jest.mock('../db.ts', () => ({
|
|||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getByText} = render(
|
||||
const { getByText } = render(
|
||||
<MockProviders>
|
||||
<PlanPage />
|
||||
</MockProviders>,
|
||||
|
@ -58,7 +58,7 @@ test('renders correctly', async () => {
|
|||
})
|
||||
|
||||
test('adds', async () => {
|
||||
const {getByTestId, getByText} = render(
|
||||
const { getByTestId, getByText } = render(
|
||||
<MockProviders>
|
||||
<PlanPage />
|
||||
</MockProviders>,
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import {Repository} from 'typeorm'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import { Repository } from 'typeorm'
|
||||
import GymSet from '../gym-set'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import PlanPage from '../PlanPage'
|
||||
import Settings from '../settings'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>,
|
||||
setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
|
||||
settingsRepo: {
|
||||
findOne: () => Promise.resolve({} as Settings),
|
||||
},
|
||||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getByText} = render(
|
||||
const { getByText } = render(
|
||||
<MockProviders>
|
||||
<PlanPage />
|
||||
</MockProviders>,
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import {Repository} from 'typeorm'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import { Repository } from 'typeorm'
|
||||
import GymSet from '../gym-set'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import Settings from '../settings'
|
||||
import SettingsPage from '../SettingsPage'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>,
|
||||
setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
|
||||
settingsRepo: {
|
||||
findOne: () => Promise.resolve({} as Settings),
|
||||
},
|
||||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getByText, getAllByText} = render(
|
||||
const { getByText, getAllByText } = render(
|
||||
<MockProviders>
|
||||
<SettingsPage />
|
||||
</MockProviders>,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {createStackNavigator} from '@react-navigation/stack'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import React from 'react'
|
||||
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 {MockProviders} from '../mock-providers'
|
||||
import {Plan} from '../plan'
|
||||
import {PlanPageParams} from '../plan-page-params'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import { Plan } from '../plan'
|
||||
import { PlanPageParams } from '../plan-page-params'
|
||||
import Settings from '../settings'
|
||||
import StartPlan from '../StartPlan'
|
||||
|
||||
|
@ -34,10 +34,10 @@ jest.mock('../data-source.ts', () => ({
|
|||
manager: {
|
||||
query: jest.fn(() =>
|
||||
Promise.resolve([
|
||||
{name: 'Bench', total: 0},
|
||||
{name: 'Rows', total: 0},
|
||||
{name: 'Curls', total: 0},
|
||||
]),
|
||||
{ name: 'Bench', total: 0 },
|
||||
{ name: 'Rows', total: 0 },
|
||||
{ name: 'Curls', total: 0 },
|
||||
])
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -45,18 +45,18 @@ jest.mock('../data-source.ts', () => ({
|
|||
|
||||
test('renders correctly', async () => {
|
||||
const Stack = createStackNavigator<PlanPageParams>()
|
||||
const {getByText, getAllByText} = render(
|
||||
const { getByText, getAllByText } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
initialParams={{
|
||||
first: {reps: 0, weight: 0} as GymSet,
|
||||
first: { reps: 0, weight: 0 } as GymSet,
|
||||
plan: {
|
||||
workouts: 'Bench,Rows,Curls',
|
||||
days: 'Monday,Tuesday,Thursday',
|
||||
} as Plan,
|
||||
}}
|
||||
name="StartPlan"
|
||||
name='StartPlan'
|
||||
component={StartPlan}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
@ -75,18 +75,18 @@ test('renders correctly', async () => {
|
|||
|
||||
test('saves', async () => {
|
||||
const Stack = createStackNavigator<PlanPageParams>()
|
||||
const {getByText} = render(
|
||||
const { getByText } = render(
|
||||
<MockProviders>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
initialParams={{
|
||||
first: {reps: 0, weight: 0} as GymSet,
|
||||
first: { reps: 0, weight: 0 } as GymSet,
|
||||
plan: {
|
||||
workouts: 'Bench,Rows,Curls',
|
||||
days: 'Monday,Tuesday,Thursday',
|
||||
} as Plan,
|
||||
}}
|
||||
name="StartPlan"
|
||||
name='StartPlan'
|
||||
component={StartPlan}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import {Repository} from 'typeorm'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import { Repository } from 'typeorm'
|
||||
import GymSet from '../gym-set'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import Settings from '../settings'
|
||||
import TimerPage from '../TimerPage'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>,
|
||||
setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
|
||||
settingsRepo: {
|
||||
findOne: () => Promise.resolve({} as Settings),
|
||||
},
|
||||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getByText} = render(
|
||||
const { getByText } = render(
|
||||
<MockProviders>
|
||||
<TimerPage />
|
||||
</MockProviders>,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react'
|
||||
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 {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import Settings from '../settings'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
|
@ -35,7 +35,7 @@ jest.mock('../db.ts', () => ({
|
|||
created: '2021-01-05T03:58:02.565Z',
|
||||
weight: 28,
|
||||
},
|
||||
]),
|
||||
])
|
||||
),
|
||||
getMany: jest.fn(() =>
|
||||
Promise.resolve([
|
||||
|
@ -57,17 +57,17 @@ jest.mock('../db.ts', () => ({
|
|||
reps: 10,
|
||||
image: 'https://picsum.photos/id/1/1000/600',
|
||||
},
|
||||
]),
|
||||
])
|
||||
),
|
||||
}),
|
||||
},
|
||||
settingsRepo: {
|
||||
findOne: () => Promise.resolve({images: true} as Settings),
|
||||
findOne: () => Promise.resolve({ images: true } as Settings),
|
||||
},
|
||||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getAllByText, getByText} = render(
|
||||
const { getAllByText, getByText } = render(
|
||||
<MockProviders>
|
||||
<BestPage />
|
||||
</MockProviders>,
|
||||
|
@ -81,7 +81,7 @@ test('renders correctly', async () => {
|
|||
})
|
||||
|
||||
test('volume', async () => {
|
||||
const {getAllByText, getByText} = render(
|
||||
const { getAllByText, getByText } = render(
|
||||
<MockProviders>
|
||||
<BestPage />
|
||||
</MockProviders>,
|
||||
|
@ -97,7 +97,7 @@ test('volume', async () => {
|
|||
})
|
||||
|
||||
test('one rep max', async () => {
|
||||
const {getAllByText, getByText} = render(
|
||||
const { getAllByText, getByText } = render(
|
||||
<MockProviders>
|
||||
<BestPage />
|
||||
</MockProviders>,
|
||||
|
@ -113,7 +113,7 @@ test('one rep max', async () => {
|
|||
})
|
||||
|
||||
test('this week', async () => {
|
||||
const {getAllByText, getByText} = render(
|
||||
const { getAllByText, getByText } = render(
|
||||
<MockProviders>
|
||||
<BestPage />
|
||||
</MockProviders>,
|
||||
|
@ -127,7 +127,7 @@ test('this week', async () => {
|
|||
})
|
||||
|
||||
test('this year', async () => {
|
||||
const {getAllByText, getByText} = render(
|
||||
const { getAllByText, getByText } = render(
|
||||
<MockProviders>
|
||||
<BestPage />
|
||||
</MockProviders>,
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from 'react'
|
||||
import 'react-native'
|
||||
import {render, waitFor} from 'react-native-testing-library'
|
||||
import {Repository} from 'typeorm'
|
||||
import { render, waitFor } from 'react-native-testing-library'
|
||||
import { Repository } from 'typeorm'
|
||||
import GymSet from '../gym-set'
|
||||
import {MockProviders} from '../mock-providers'
|
||||
import { MockProviders } from '../mock-providers'
|
||||
import Settings from '../settings'
|
||||
import WorkoutsPage from '../WorkoutsPage'
|
||||
|
||||
jest.mock('../db.ts', () => ({
|
||||
setRepo: {find: () => Promise.resolve([])} as Repository<GymSet>,
|
||||
setRepo: { find: () => Promise.resolve([]) } as Repository<GymSet>,
|
||||
settingsRepo: {
|
||||
findOne: () => Promise.resolve({} as Settings),
|
||||
},
|
||||
}))
|
||||
|
||||
test('renders correctly', async () => {
|
||||
const {getByText} = render(
|
||||
const { getByText } = render(
|
||||
<MockProviders>
|
||||
<WorkoutsPage />
|
||||
</MockProviders>,
|
||||
|
|
4
toast.ts
4
toast.ts
|
@ -1,7 +1,7 @@
|
|||
import {DeviceEventEmitter} from 'react-native'
|
||||
import { DeviceEventEmitter } from 'react-native'
|
||||
|
||||
export const TOAST = 'toast'
|
||||
|
||||
export function toast(value: string) {
|
||||
DeviceEventEmitter.emit(TOAST, {value})
|
||||
DeviceEventEmitter.emit(TOAST, { value })
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {useColorScheme} from 'react-native'
|
||||
import {useTheme} from './use-theme'
|
||||
import { useColorScheme } from 'react-native'
|
||||
import { useTheme } from './use-theme'
|
||||
|
||||
export default function useDark() {
|
||||
const dark = useColorScheme() === 'dark'
|
||||
const {theme} = useTheme()
|
||||
const { theme } = useTheme()
|
||||
|
||||
if (theme === 'dark') return true
|
||||
if (theme === 'light') return false
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {createContext, useContext} from 'react'
|
||||
import {DarkTheme, DefaultTheme} from 'react-native-paper'
|
||||
import { createContext, useContext } from 'react'
|
||||
import { DarkTheme, DefaultTheme } from 'react-native-paper'
|
||||
|
||||
export const ThemeContext = createContext<{
|
||||
theme: string
|
||||
|
|
12
use-timer.ts
12
use-timer.ts
|
@ -1,7 +1,7 @@
|
|||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {useCallback, useState} from 'react'
|
||||
import {NativeEventEmitter} from 'react-native'
|
||||
import {TickEvent} from './TimerPage'
|
||||
import { useFocusEffect } from '@react-navigation/native'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { NativeEventEmitter } from 'react-native'
|
||||
import { TickEvent } from './TimerPage'
|
||||
|
||||
export default function useTimer() {
|
||||
const [minutes, setMinutes] = useState('00')
|
||||
|
@ -13,7 +13,7 @@ export default function useTimer() {
|
|||
setSeconds('00')
|
||||
const emitter = new NativeEventEmitter()
|
||||
const listener = emitter.addListener('tick', (event: TickEvent) => {
|
||||
console.log(`${useTimer.name}.tick:`, {event})
|
||||
console.log(`${useTimer.name}.tick:`, { event })
|
||||
setMinutes(event.minutes)
|
||||
setSeconds(event.seconds)
|
||||
})
|
||||
|
@ -21,5 +21,5 @@ export default function useTimer() {
|
|||
}, []),
|
||||
)
|
||||
|
||||
return {minutes, seconds}
|
||||
return { minutes, seconds }
|
||||
}
|
||||
|
|
9
write.ts
9
write.ts
|
@ -1,6 +1,6 @@
|
|||
import {PermissionsAndroid, Platform} from 'react-native'
|
||||
import {Dirs, FileSystem} from 'react-native-file-access'
|
||||
import {toast} from './toast'
|
||||
import { PermissionsAndroid, Platform } from 'react-native'
|
||||
import { Dirs, FileSystem } from 'react-native-file-access'
|
||||
import { toast } from './toast'
|
||||
|
||||
export const write = async (name: string, data: string) => {
|
||||
const filePath = `${Dirs.DocumentDir}/${name}`
|
||||
|
@ -14,7 +14,8 @@ export const write = async (name: string, data: string) => {
|
|||
const granted = await permission()
|
||||
if (!granted) return
|
||||
await FileSystem.writeFile(filePath, data)
|
||||
if (Platform.OS === 'android')
|
||||
if (Platform.OS === 'android') {
|
||||
await FileSystem.cpExternal(filePath, name, 'downloads')
|
||||
}
|
||||
toast(`Downloaded ${name}`)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user