Refactor MassiveSnack
Instead of using a context for the whole app use DeviceEventEmitter with root state. This will probably improve performance, since I think the react context was re-rendering the entire DOM tree.
This commit is contained in:
parent
ace327ecad
commit
49b5eb48c6
34
App.tsx
34
App.tsx
|
@ -4,19 +4,20 @@ import {
|
|||
NavigationContainer,
|
||||
} from '@react-navigation/native'
|
||||
import {useEffect, useMemo, useState} from 'react'
|
||||
import {useColorScheme} from 'react-native'
|
||||
import {DeviceEventEmitter, useColorScheme} from 'react-native'
|
||||
import {
|
||||
DarkTheme as PaperDarkTheme,
|
||||
DefaultTheme as PaperDefaultTheme,
|
||||
Provider as PaperProvider,
|
||||
Snackbar,
|
||||
} from 'react-native-paper'
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons'
|
||||
import {lightColors} from './colors'
|
||||
import {AppDataSource} from './data-source'
|
||||
import {settingsRepo} from './db'
|
||||
import MassiveSnack from './MassiveSnack'
|
||||
import Routes from './Routes'
|
||||
import Settings from './settings'
|
||||
import {TOAST} from './toast'
|
||||
import {defaultSettings, SettingsContext} from './use-settings'
|
||||
|
||||
export const CombinedDefaultTheme = {
|
||||
|
@ -48,6 +49,7 @@ const App = () => {
|
|||
? CombinedDarkTheme.colors.primary
|
||||
: CombinedDefaultTheme.colors.primary,
|
||||
})
|
||||
const [snackbar, setSnackbar] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
AppDataSource.initialize().then(async () => {
|
||||
|
@ -56,6 +58,10 @@ const App = () => {
|
|||
setSettings(gotSettings)
|
||||
setInitialized(true)
|
||||
})
|
||||
DeviceEventEmitter.addListener(TOAST, ({value}: {value: string}) => {
|
||||
console.log(`${Routes.name}.toast:`, {value})
|
||||
setSnackbar(value)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const theme = useMemo(() => {
|
||||
|
@ -87,14 +93,24 @@ const App = () => {
|
|||
theme={theme}
|
||||
settings={{icon: props => <MaterialIcon {...props} />}}>
|
||||
<NavigationContainer theme={theme}>
|
||||
<MassiveSnack>
|
||||
{initialized && (
|
||||
<SettingsContext.Provider value={settingsContext}>
|
||||
<Routes />
|
||||
</SettingsContext.Provider>
|
||||
)}
|
||||
</MassiveSnack>
|
||||
{initialized && (
|
||||
<SettingsContext.Provider value={settingsContext}>
|
||||
<Routes />
|
||||
</SettingsContext.Provider>
|
||||
)}
|
||||
</NavigationContainer>
|
||||
|
||||
<Snackbar
|
||||
duration={3000}
|
||||
onDismiss={() => setSnackbar('')}
|
||||
visible={!!snackbar}
|
||||
action={{
|
||||
label: 'Close',
|
||||
onPress: () => setSnackbar(''),
|
||||
color: theme.colors.primary,
|
||||
}}>
|
||||
{snackbar}
|
||||
</Snackbar>
|
||||
</PaperProvider>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import {AppDataSource} from './data-source'
|
|||
import {planRepo} from './db'
|
||||
import {DrawerParamList} from './drawer-param-list'
|
||||
import GymSet from './gym-set'
|
||||
import {useSnackbar} from './MassiveSnack'
|
||||
import {Plan} from './plan'
|
||||
import {toast} from './toast'
|
||||
import useDark from './use-dark'
|
||||
import {write} from './write'
|
||||
|
||||
|
@ -20,7 +20,6 @@ const setRepo = AppDataSource.manager.getRepository(GymSet)
|
|||
export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
const [showRemove, setShowRemove] = useState(false)
|
||||
const {toast} = useSnackbar()
|
||||
const {reset} = useNavigation<NavigationProp<DrawerParamList>>()
|
||||
const dark = useDark()
|
||||
|
||||
|
@ -65,7 +64,7 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
|||
console.log(`${DrawerMenu.name}.uploadSets:`, file.length)
|
||||
const lines = file.split('\n')
|
||||
console.log(lines[0])
|
||||
if (!setFields.includes(lines[0])) return toast('Invalid csv.', 3000)
|
||||
if (!setFields.includes(lines[0])) return toast('Invalid csv.')
|
||||
const values = lines
|
||||
.slice(1)
|
||||
.filter(line => line)
|
||||
|
@ -92,21 +91,22 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
|||
sets: +sets,
|
||||
minutes: +minutes,
|
||||
seconds: +seconds,
|
||||
image: '',
|
||||
}
|
||||
return set
|
||||
})
|
||||
console.log(`${DrawerMenu.name}.uploadSets:`, {values})
|
||||
await setRepo.insert(values)
|
||||
toast('Data imported.', 3000)
|
||||
toast('Data imported.')
|
||||
reset({index: 0, routes: [{name}]})
|
||||
}, [reset, name, toast])
|
||||
}, [reset, name])
|
||||
|
||||
const uploadPlans = useCallback(async () => {
|
||||
const result = await DocumentPicker.pickSingle()
|
||||
const file = await FileSystem.readFile(result.uri)
|
||||
console.log(`${DrawerMenu.name}.uploadPlans:`, file.length)
|
||||
const lines = file.split('\n')
|
||||
if (lines[0] != planFields) return toast('Invalid csv.', 3000)
|
||||
if (lines[0] !== planFields) return toast('Invalid csv.')
|
||||
const values = file
|
||||
.split('\n')
|
||||
.slice(1)
|
||||
|
@ -122,8 +122,8 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
|||
return plan
|
||||
})
|
||||
await planRepo.insert(values)
|
||||
toast('Data imported.', 3000)
|
||||
}, [toast])
|
||||
toast('Data imported.')
|
||||
}, [])
|
||||
|
||||
const upload = useCallback(async () => {
|
||||
setShowMenu(false)
|
||||
|
@ -137,9 +137,9 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) {
|
|||
setShowRemove(false)
|
||||
if (name === 'Home') await setRepo.delete({})
|
||||
else if (name === 'Plans') await planRepo.delete({})
|
||||
toast('All data has been deleted.', 4000)
|
||||
toast('All data has been deleted.')
|
||||
reset({index: 0, routes: [{name}]})
|
||||
}, [reset, name, toast])
|
||||
}, [reset, name])
|
||||
|
||||
if (name === 'Home' || name === 'Plans')
|
||||
return (
|
||||
|
|
12
EditSet.tsx
12
EditSet.tsx
|
@ -2,19 +2,18 @@ import {RouteProp, useNavigation, useRoute} from '@react-navigation/native'
|
|||
import {useCallback} from 'react'
|
||||
import {NativeModules, View} from 'react-native'
|
||||
import {PADDING} from './constants'
|
||||
import {getNow, setRepo} from './db'
|
||||
import {setRepo} from './db'
|
||||
import GymSet from './gym-set'
|
||||
import {HomePageParams} from './home-page-params'
|
||||
import {useSnackbar} from './MassiveSnack'
|
||||
import SetForm from './SetForm'
|
||||
import StackHeader from './StackHeader'
|
||||
import {toast} from './toast'
|
||||
import {useSettings} from './use-settings'
|
||||
|
||||
export default function EditSet() {
|
||||
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>()
|
||||
const {set} = params
|
||||
const navigation = useNavigation()
|
||||
const {toast} = useSnackbar()
|
||||
const {settings} = useSettings()
|
||||
|
||||
const startTimer = useCallback(
|
||||
|
@ -35,9 +34,6 @@ export default function EditSet() {
|
|||
const add = useCallback(
|
||||
async (value: GymSet) => {
|
||||
startTimer(value.name)
|
||||
const [{now}] = await getNow()
|
||||
value.created = now
|
||||
value.hidden = false
|
||||
console.log(`${EditSet.name}.add`, {set: value})
|
||||
const result = await setRepo.save(value)
|
||||
console.log({result})
|
||||
|
@ -46,9 +42,9 @@ export default function EditSet() {
|
|||
value.weight > set.weight ||
|
||||
(value.reps > set.reps && value.weight === set.weight)
|
||||
)
|
||||
toast("Great work King! That's a new record.", 3000)
|
||||
toast("Great work King! That's a new record.")
|
||||
},
|
||||
[startTimer, set, toast, settings],
|
||||
[startTimer, set, settings],
|
||||
)
|
||||
|
||||
const save = useCallback(
|
||||
|
|
|
@ -7,8 +7,8 @@ import ConfirmDialog from './ConfirmDialog'
|
|||
import {MARGIN, PADDING} from './constants'
|
||||
import {getNow, planRepo, setRepo} from './db'
|
||||
import MassiveInput from './MassiveInput'
|
||||
import {useSnackbar} from './MassiveSnack'
|
||||
import StackHeader from './StackHeader'
|
||||
import {toast} from './toast'
|
||||
import {useSettings} from './use-settings'
|
||||
import {WorkoutsPageParams} from './WorkoutsPage'
|
||||
|
||||
|
@ -26,7 +26,6 @@ export default function EditWorkout() {
|
|||
params.value.seconds?.toString() ?? '30',
|
||||
)
|
||||
const [sets, setSets] = useState(params.value.sets?.toString() ?? '3')
|
||||
const {toast} = useSnackbar()
|
||||
const navigation = useNavigation()
|
||||
const setsRef = useRef<TextInput>(null)
|
||||
const stepsRef = useRef<TextInput>(null)
|
||||
|
@ -94,13 +93,13 @@ export default function EditWorkout() {
|
|||
const handleName = (value: string) => {
|
||||
setName(value.replace(/,|'/g, ''))
|
||||
if (value.match(/,|'/))
|
||||
toast('Commas and single quotes would break CSV exports', 6000)
|
||||
toast('Commas and single quotes would break CSV exports')
|
||||
}
|
||||
|
||||
const handleSteps = (value: string) => {
|
||||
setSteps(value.replace(/,|'/g, ''))
|
||||
if (value.match(/,|'/))
|
||||
toast('Commas and single quotes would break CSV exports', 6000)
|
||||
toast('Commas and single quotes would break CSV exports')
|
||||
}
|
||||
|
||||
const submitName = () => {
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
import {createContext, useContext, useState} from 'react'
|
||||
import {Snackbar} from 'react-native-paper'
|
||||
import {CombinedDarkTheme, CombinedDefaultTheme} from './App'
|
||||
import useDark from './use-dark'
|
||||
|
||||
export const SnackbarContext = createContext<{
|
||||
toast: (value: string, timeout: number) => void
|
||||
}>({toast: () => null})
|
||||
|
||||
export const useSnackbar = () => {
|
||||
return useContext(SnackbarContext)
|
||||
}
|
||||
|
||||
export default function MassiveSnack({
|
||||
children,
|
||||
}: {
|
||||
children?: JSX.Element[] | JSX.Element
|
||||
}) {
|
||||
const [snackbar, setSnackbar] = useState('')
|
||||
const [timeoutId, setTimeoutId] = useState(0)
|
||||
const dark = useDark()
|
||||
|
||||
const toast = (value: string, timeout: number) => {
|
||||
setSnackbar(value)
|
||||
clearTimeout(timeoutId)
|
||||
const id = setTimeout(() => setSnackbar(''), timeout)
|
||||
setTimeoutId(id)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SnackbarContext.Provider value={{toast}}>
|
||||
{children}
|
||||
</SnackbarContext.Provider>
|
||||
<Snackbar
|
||||
onDismiss={() => setSnackbar('')}
|
||||
visible={!!snackbar}
|
||||
action={{
|
||||
label: 'Close',
|
||||
onPress: () => setSnackbar(''),
|
||||
color: dark
|
||||
? CombinedDarkTheme.colors.background
|
||||
: CombinedDefaultTheme.colors.background,
|
||||
}}>
|
||||
{snackbar}
|
||||
</Snackbar>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import {createDrawerNavigator} from '@react-navigation/drawer'
|
||||
import {useMemo} from 'react'
|
||||
import {IconButton} from 'react-native-paper'
|
||||
import {useEffect, useMemo, useState} from 'react'
|
||||
import {DeviceEventEmitter} from 'react-native'
|
||||
import {IconButton, Snackbar, useTheme} from 'react-native-paper'
|
||||
import BestPage from './BestPage'
|
||||
import {DrawerParamList} from './drawer-param-list'
|
||||
import HomePage from './HomePage'
|
||||
|
|
11
SetForm.tsx
11
SetForm.tsx
|
@ -4,10 +4,10 @@ import DocumentPicker from 'react-native-document-picker'
|
|||
import {Button, Card, TouchableRipple} from 'react-native-paper'
|
||||
import ConfirmDialog from './ConfirmDialog'
|
||||
import {MARGIN} from './constants'
|
||||
import {setRepo} from './db'
|
||||
import {getNow, setRepo} from './db'
|
||||
import GymSet from './gym-set'
|
||||
import MassiveInput from './MassiveInput'
|
||||
import {useSnackbar} from './MassiveSnack'
|
||||
import {toast} from './toast'
|
||||
import {useSettings} from './use-settings'
|
||||
|
||||
export default function SetForm({
|
||||
|
@ -28,7 +28,6 @@ export default function SetForm({
|
|||
end: set.reps.toString().length,
|
||||
})
|
||||
const [removeImage, setRemoveImage] = useState(false)
|
||||
const {toast} = useSnackbar()
|
||||
const {settings} = useSettings()
|
||||
const weightRef = useRef<TextInput>(null)
|
||||
const repsRef = useRef<TextInput>(null)
|
||||
|
@ -42,8 +41,10 @@ export default function SetForm({
|
|||
image = await setRepo.findOne({where: {name}}).then(s => s?.image)
|
||||
|
||||
console.log(`${SetForm.name}.handleSubmit:`, {image})
|
||||
const [{now}] = await getNow()
|
||||
save({
|
||||
name,
|
||||
created: now,
|
||||
reps: Number(reps),
|
||||
weight: Number(weight),
|
||||
id: set.id,
|
||||
|
@ -59,13 +60,13 @@ export default function SetForm({
|
|||
const handleName = (value: string) => {
|
||||
setName(value.replace(/,|'/g, ''))
|
||||
if (value.match(/,|'/))
|
||||
toast('Commas and single quotes would break CSV exports', 6000)
|
||||
toast('Commas and single quotes would break CSV exports')
|
||||
}
|
||||
|
||||
const handleUnit = (value: string) => {
|
||||
setUnit(value.replace(/,|'/g, ''))
|
||||
if (value.match(/,|'/))
|
||||
toast('Commas and single quotes would break CSV exports', 6000)
|
||||
toast('Commas and single quotes would break CSV exports')
|
||||
}
|
||||
|
||||
const changeImage = useCallback(async () => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Picker} from '@react-native-picker/picker'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {NativeModules, ScrollView} from 'react-native'
|
||||
import {DeviceEventEmitter, NativeModules, ScrollView} from 'react-native'
|
||||
import DocumentPicker from 'react-native-document-picker'
|
||||
import {Button} from 'react-native-paper'
|
||||
import {darkColors, lightColors} from './colors'
|
||||
|
@ -10,10 +10,10 @@ import {MARGIN} from './constants'
|
|||
import {settingsRepo} from './db'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import Input from './input'
|
||||
import {useSnackbar} from './MassiveSnack'
|
||||
import Page from './Page'
|
||||
import Settings from './settings'
|
||||
import Switch from './Switch'
|
||||
import {toast} from './toast'
|
||||
import {useSettings} from './use-settings'
|
||||
|
||||
export default function SettingsPage() {
|
||||
|
@ -21,7 +21,6 @@ export default function SettingsPage() {
|
|||
const [ignoring, setIgnoring] = useState(false)
|
||||
const [term, setTerm] = useState('')
|
||||
const {settings, setSettings} = useSettings()
|
||||
const {toast} = useSnackbar()
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`${SettingsPage.name}.useEffect:`, {settings})
|
||||
|
@ -43,21 +42,25 @@ export default function SettingsPage() {
|
|||
|
||||
const changeAlarmEnabled = useCallback(
|
||||
(enabled: boolean) => {
|
||||
if (enabled) toast('Timers will now run after each set.', 4000)
|
||||
else toast('Stopped timers running after each set.', 4000)
|
||||
if (enabled)
|
||||
DeviceEventEmitter.emit('toast', {
|
||||
value: 'Timers will now run after each set',
|
||||
timeout: 4000,
|
||||
})
|
||||
else toast('Stopped timers running after each set.')
|
||||
if (enabled && !ignoring) setBattery(true)
|
||||
update(enabled, 'alarm')
|
||||
},
|
||||
[setBattery, ignoring, toast, update],
|
||||
[setBattery, ignoring, update],
|
||||
)
|
||||
|
||||
const changeVibrate = useCallback(
|
||||
(enabled: boolean) => {
|
||||
if (enabled) toast('When a timer completes, vibrate your phone.', 4000)
|
||||
else toast('Stop vibrating at the end of timers.', 4000)
|
||||
if (enabled) toast('When a timer completes, vibrate your phone.')
|
||||
else toast('Stop vibrating at the end of timers.')
|
||||
update(enabled, 'vibrate')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const changeSound = useCallback(async () => {
|
||||
|
@ -68,70 +71,70 @@ export default function SettingsPage() {
|
|||
if (!fileCopyUri) return
|
||||
settingsRepo.update({}, {sound: fileCopyUri})
|
||||
setSettings({...settings, sound: fileCopyUri})
|
||||
toast('This song will now play after rest timers complete.', 4000)
|
||||
}, [toast, setSettings, settings])
|
||||
toast('This song will now play after rest timers complete.')
|
||||
}, [setSettings, settings])
|
||||
|
||||
const changeNotify = useCallback(
|
||||
(enabled: boolean) => {
|
||||
update(enabled, 'notify')
|
||||
if (enabled) toast('Show when a set is a new record.', 4000)
|
||||
else toast('Stopped showing notifications for new records.', 4000)
|
||||
if (enabled) toast('Show when a set is a new record.')
|
||||
else toast('Stopped showing notifications for new records.')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const changeImages = useCallback(
|
||||
(enabled: boolean) => {
|
||||
update(enabled, 'images')
|
||||
if (enabled) toast('Show images for sets.', 4000)
|
||||
else toast('Stopped showing images for sets.', 4000)
|
||||
if (enabled) toast('Show images for sets.')
|
||||
else toast('Stopped showing images for sets.')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const changeUnit = useCallback(
|
||||
(enabled: boolean) => {
|
||||
update(enabled, 'showUnit')
|
||||
if (enabled) toast('Show option to select unit for sets.', 4000)
|
||||
else toast('Hid unit option for sets.', 4000)
|
||||
if (enabled) toast('Show option to select unit for sets.')
|
||||
else toast('Hid unit option for sets.')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const changeSteps = useCallback(
|
||||
(enabled: boolean) => {
|
||||
update(enabled, 'steps')
|
||||
if (enabled) toast('Show steps for a workout.', 4000)
|
||||
else toast('Stopped showing steps for workouts.', 4000)
|
||||
if (enabled) toast('Show steps for a workout.')
|
||||
else toast('Stopped showing steps for workouts.')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const changeShowDate = useCallback(
|
||||
(enabled: boolean) => {
|
||||
update(enabled, 'showDate')
|
||||
if (enabled) toast('Show date for sets by default.', 4000)
|
||||
else toast('Stopped showing date for sets by default.', 4000)
|
||||
if (enabled) toast('Show date for sets by default.')
|
||||
else toast('Stopped showing date for sets by default.')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const changeShowSets = useCallback(
|
||||
(enabled: boolean) => {
|
||||
update(enabled, 'showSets')
|
||||
if (enabled) toast('Show target sets for workouts.', 4000)
|
||||
else toast('Stopped showing target sets for workouts.', 4000)
|
||||
if (enabled) toast('Show target sets for workouts.')
|
||||
else toast('Stopped showing target sets for workouts.')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const changeNoSound = useCallback(
|
||||
(enabled: boolean) => {
|
||||
update(enabled, 'noSound')
|
||||
if (enabled) toast('Disable sound on rest timer alarms.', 4000)
|
||||
else toast('Enabled sound for rest timer alarms.', 4000)
|
||||
if (enabled) toast('Disable sound on rest timer alarms.')
|
||||
else toast('Enabled sound for rest timer alarms.')
|
||||
},
|
||||
[toast, update],
|
||||
[update],
|
||||
)
|
||||
|
||||
const switches: Input<boolean>[] = [
|
||||
|
@ -183,10 +186,13 @@ export default function SettingsPage() {
|
|||
)
|
||||
}, [term, settings.color, changeTheme, settings.theme])
|
||||
|
||||
const changeColor = useCallback((value: string) => {
|
||||
setSettings({...settings, color: value})
|
||||
settingsRepo.update({}, {color: value})
|
||||
}, [])
|
||||
const changeColor = useCallback(
|
||||
(value: string) => {
|
||||
setSettings({...settings, color: value})
|
||||
settingsRepo.update({}, {color: value})
|
||||
},
|
||||
[setSettings, settings],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -10,11 +10,11 @@ import {AppDataSource} from './data-source'
|
|||
import {getNow, setRepo} from './db'
|
||||
import GymSet from './gym-set'
|
||||
import MassiveInput from './MassiveInput'
|
||||
import {useSnackbar} from './MassiveSnack'
|
||||
import {PlanPageParams} from './plan-page-params'
|
||||
import SetForm from './SetForm'
|
||||
import StackHeader from './StackHeader'
|
||||
import StartPlanItem from './StartPlanItem'
|
||||
import {toast} from './toast'
|
||||
import {useSettings} from './use-settings'
|
||||
|
||||
export default function StartPlan() {
|
||||
|
@ -23,7 +23,6 @@ export default function StartPlan() {
|
|||
const [reps, setReps] = useState('')
|
||||
const [weight, setWeight] = useState('')
|
||||
const [unit, setUnit] = useState<string>('kg')
|
||||
const {toast} = useSnackbar()
|
||||
const [minutes, setMinutes] = useState(3)
|
||||
const [seconds, setSeconds] = useState(30)
|
||||
const [best, setBest] = useState<GymSet>()
|
||||
|
@ -109,9 +108,9 @@ export default function StartPlan() {
|
|||
settings.notify &&
|
||||
(+weight > best.weight || (+reps > best.reps && +weight === best.weight))
|
||||
)
|
||||
toast("Great work King! That's a new record.", 5000)
|
||||
else if (settings.alarm) toast('Resting...', 3000)
|
||||
else toast('Added set', 3000)
|
||||
toast("Great work King! That's a new record.")
|
||||
else if (settings.alarm) toast('Resting...')
|
||||
else toast('Added set')
|
||||
if (!settings.alarm) return
|
||||
const milliseconds = Number(minutes) * 60 * 1000 + Number(seconds) * 1000
|
||||
const {vibrate, sound, noSound} = settings
|
||||
|
@ -119,14 +118,11 @@ export default function StartPlan() {
|
|||
NativeModules.AlarmModule.timer(...args)
|
||||
}
|
||||
|
||||
const handleUnit = useCallback(
|
||||
(value: string) => {
|
||||
setUnit(value.replace(/,|'/g, ''))
|
||||
if (value.match(/,|'/))
|
||||
toast('Commas and single quotes would break CSV exports', 6000)
|
||||
},
|
||||
[toast],
|
||||
)
|
||||
const handleUnit = useCallback((value: string) => {
|
||||
setUnit(value.replace(/,|'/g, ''))
|
||||
if (value.match(/,|'/))
|
||||
toast('Commas and single quotes would break CSV exports')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
Loading…
Reference in New Issue
Block a user