diff --git a/App.tsx b/App.tsx
index 72586fdc..889c03b4 100644
--- a/App.tsx
+++ b/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 => }}>
-
- {initialized && (
-
-
-
- )}
-
+ {initialized && (
+
+
+
+ )}
+
+ setSnackbar('')}
+ visible={!!snackbar}
+ action={{
+ label: 'Close',
+ onPress: () => setSnackbar(''),
+ color: theme.colors.primary,
+ }}>
+ {snackbar}
+
)
}
diff --git a/DrawerMenu.tsx b/DrawerMenu.tsx
index 8a7ca822..0189025a 100644
--- a/DrawerMenu.tsx
+++ b/DrawerMenu.tsx
@@ -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>()
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 (
diff --git a/EditSet.tsx b/EditSet.tsx
index 13e70006..a3f8f6a9 100644
--- a/EditSet.tsx
+++ b/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>()
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(
diff --git a/EditWorkout.tsx b/EditWorkout.tsx
index a435d616..006bfe90 100644
--- a/EditWorkout.tsx
+++ b/EditWorkout.tsx
@@ -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(null)
const stepsRef = useRef(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 = () => {
diff --git a/MassiveSnack.tsx b/MassiveSnack.tsx
deleted file mode 100644
index 36131894..00000000
--- a/MassiveSnack.tsx
+++ /dev/null
@@ -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 (
- <>
-
- {children}
-
- setSnackbar('')}
- visible={!!snackbar}
- action={{
- label: 'Close',
- onPress: () => setSnackbar(''),
- color: dark
- ? CombinedDarkTheme.colors.background
- : CombinedDefaultTheme.colors.background,
- }}>
- {snackbar}
-
- >
- )
-}
diff --git a/Routes.tsx b/Routes.tsx
index 23b2060b..b93685d7 100644
--- a/Routes.tsx
+++ b/Routes.tsx
@@ -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'
diff --git a/SetForm.tsx b/SetForm.tsx
index f07a4a63..0658d788 100644
--- a/SetForm.tsx
+++ b/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(null)
const repsRef = useRef(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 () => {
diff --git a/SettingsPage.tsx b/SettingsPage.tsx
index 748d8463..b4e10eb4 100644
--- a/SettingsPage.tsx
+++ b/SettingsPage.tsx
@@ -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[] = [
@@ -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 (
<>
diff --git a/StartPlan.tsx b/StartPlan.tsx
index 5d8ba355..2d87dac7 100644
--- a/StartPlan.tsx
+++ b/StartPlan.tsx
@@ -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('kg')
- const {toast} = useSnackbar()
const [minutes, setMinutes] = useState(3)
const [seconds, setSeconds] = useState(30)
const [best, setBest] = useState()
@@ -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 (
<>
diff --git a/toast.ts b/toast.ts
new file mode 100644
index 00000000..82272838
--- /dev/null
+++ b/toast.ts
@@ -0,0 +1,7 @@
+import {DeviceEventEmitter} from 'react-native'
+
+export const TOAST = 'toast'
+
+export function toast(value: string) {
+ DeviceEventEmitter.emit(TOAST, {value})
+}