diff --git a/BestList.tsx b/BestList.tsx index af57fae..a8dc320 100644 --- a/BestList.tsx +++ b/BestList.tsx @@ -3,18 +3,17 @@ import { useFocusEffect, useNavigation, } from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {FlatList, StyleSheet, View} from 'react-native'; import {List, Searchbar} from 'react-native-paper'; import Best from './best'; import {BestPageParams} from './BestPage'; -import {DatabaseContext} from './Routes'; +import {db} from './db'; export default function BestList() { const [bests, setBests] = useState([]); const [search, setSearch] = useState(''); const [refreshing, setRefresing] = useState(false); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); const refresh = useCallback(async () => { @@ -41,7 +40,7 @@ export default function BestList() { newBest = newBest.concat(reps.rows.raw()); } setBests(newBest); - }, [search, db]); + }, [search]); useFocusEffect( useCallback(() => { diff --git a/DrawerMenu.tsx b/DrawerMenu.tsx index f2ce3eb..ae52603 100644 --- a/DrawerMenu.tsx +++ b/DrawerMenu.tsx @@ -5,8 +5,8 @@ import {FileSystem} from 'react-native-file-access'; import {Divider, IconButton, Menu} from 'react-native-paper'; import {DrawerParamList, SnackbarContext} from './App'; import ConfirmDialog from './ConfirmDialog'; +import {db} from './db'; import {Plan} from './plan'; -import {DatabaseContext} from './Routes'; import Set from './set'; import {write} from './write'; @@ -16,7 +16,6 @@ const planFields = 'id,days,workouts'; export default function DrawerMenu({name}: {name: keyof DrawerParamList}) { const [showMenu, setShowMenu] = useState(false); const [showRemove, setShowRemove] = useState(false); - const db = useContext(DatabaseContext); const {toast} = useContext(SnackbarContext); const {reset} = useNavigation>(); @@ -34,7 +33,7 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) { .join('\n'); console.log(`${DrawerMenu.name}.exportSets`, {length: sets.length}); await write('sets.csv', data); - }, [db]); + }, []); const exportPlans = useCallback(async () => { const [result] = await db.executeSql('SELECT * FROM plans'); @@ -45,7 +44,7 @@ export default function DrawerMenu({name}: {name: keyof DrawerParamList}) { .join('\n'); console.log(`${DrawerMenu.name}.exportPlans`, {length: sets.length}); await write('plans.csv', data); - }, [db]); + }, []); const download = useCallback(async () => { setShowMenu(false); diff --git a/EditPlan.tsx b/EditPlan.tsx index 44d1e5a..85cc697 100644 --- a/EditPlan.tsx +++ b/EditPlan.tsx @@ -5,13 +5,13 @@ import { useNavigation, useRoute, } from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {ScrollView, StyleSheet, Text, View} from 'react-native'; import {Button, IconButton} from 'react-native-paper'; import {DrawerParamList} from './App'; +import {db} from './db'; import MassiveSwitch from './MassiveSwitch'; import {PlanPageParams} from './PlanPage'; -import {DatabaseContext} from './Routes'; import {DAYS} from './time'; export default function EditPlan() { @@ -21,7 +21,6 @@ export default function EditPlan() { params.plan.workouts.split(','), ); const [names, setNames] = useState([]); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); useFocusEffect( @@ -45,7 +44,7 @@ export default function EditPlan() { setNames(namesResult.rows.raw().map(({name}) => name)); }; refresh(); - }, [db]); + }, []); const save = useCallback(async () => { console.log(`${EditPlan.name}.save`, {days, workouts, params}); @@ -63,7 +62,7 @@ export default function EditPlan() { [newDays, newWorkouts, params.plan.id], ); navigation.goBack(); - }, [days, workouts, db, params, navigation]); + }, [days, workouts, params, navigation]); const toggleWorkout = useCallback( (on: boolean, name: string) => { diff --git a/EditSet.tsx b/EditSet.tsx index c7a39c5..0dae118 100644 --- a/EditSet.tsx +++ b/EditSet.tsx @@ -8,15 +8,14 @@ import React, {useCallback, useContext} from 'react'; import {NativeModules, View} from 'react-native'; import {IconButton} from 'react-native-paper'; import {SnackbarContext} from './App'; +import {db} from './db'; import {HomePageParams} from './HomePage'; -import {DatabaseContext} from './Routes'; import Set from './set'; import SetForm from './SetForm'; import Settings from './settings'; export default function EditSet() { const {params} = useRoute>(); - const db = useContext(DatabaseContext); const navigation = useNavigation(); const {toast} = useContext(SnackbarContext); @@ -42,7 +41,7 @@ export default function EditSet() { !!settings.vibrate, settings.sound, ); - }, [db]); + }, []); const update = useCallback( async (set: Set) => { @@ -53,7 +52,7 @@ export default function EditSet() { ); navigation.goBack(); }, - [db, navigation], + [navigation], ); const add = useCallback( @@ -75,7 +74,7 @@ export default function EditSet() { toast("Great work King, that's a new record!", 3000); navigation.goBack(); }, - [db, navigation, startTimer, params.set, toast], + [navigation, startTimer, params.set, toast], ); const save = useCallback( diff --git a/EditWorkout.tsx b/EditWorkout.tsx index 5dea8a4..6b38911 100644 --- a/EditWorkout.tsx +++ b/EditWorkout.tsx @@ -4,20 +4,19 @@ import { useNavigation, useRoute, } from '@react-navigation/native'; -import React, {useCallback, useContext, useState} from 'react'; +import React, {useCallback, useState} from 'react'; import {Image, ScrollView, View} from 'react-native'; import DocumentPicker from 'react-native-document-picker'; import {Button, IconButton} from 'react-native-paper'; import {set} from 'react-native-reanimated'; +import {db} from './db'; import MassiveInput from './MassiveInput'; -import {DatabaseContext} from './Routes'; import {WorkoutsPageParams} from './WorkoutsPage'; export default function EditWorkout() { const [name, setName] = useState(''); const [uri, setUri] = useState(''); const {params} = useRoute>(); - const db = useContext(DatabaseContext); const navigation = useNavigation(); useFocusEffect( @@ -32,7 +31,7 @@ export default function EditWorkout() { db.executeSql(`SELECT image FROM sets WHERE name = ? LIMIT 1`, [ params.value.name, ]).then(([result]) => setUri(result.rows.item(0)?.image)); - }, [navigation, params.value.name, db]), + }, [navigation, params.value.name]), ); const update = useCallback(async () => { @@ -54,7 +53,7 @@ export default function EditWorkout() { params.value.name, ]); navigation.goBack(); - }, [db, navigation, params.value.name, name, uri]); + }, [navigation, params.value.name, name, uri]); const add = useCallback(async () => { const insert = ` @@ -63,7 +62,7 @@ export default function EditWorkout() { `; await db.executeSql(insert, [name]); navigation.goBack(); - }, [db, navigation, name]); + }, [navigation, name]); const save = useCallback(async () => { if (params.value.name) return update(); diff --git a/PlanItem.tsx b/PlanItem.tsx index f20758e..78ff03e 100644 --- a/PlanItem.tsx +++ b/PlanItem.tsx @@ -1,10 +1,10 @@ import {NavigationProp, useNavigation} from '@react-navigation/native'; -import React, {useCallback, useContext, useState} from 'react'; +import React, {useCallback, useState} from 'react'; import {GestureResponderEvent} from 'react-native'; import {List, Menu} from 'react-native-paper'; +import {db} from './db'; import {Plan} from './plan'; import {PlanPageParams} from './PlanPage'; -import {DatabaseContext} from './Routes'; export default function PlanItem({ item, @@ -15,14 +15,13 @@ export default function PlanItem({ }) { const [show, setShow] = useState(false); const [anchor, setAnchor] = useState({x: 0, y: 0}); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); const remove = useCallback(async () => { await db.executeSql(`DELETE FROM plans WHERE id = ?`, [item.id]); setShow(false); onRemove(); - }, [db, setShow, item.id, onRemove]); + }, [setShow, item.id, onRemove]); const longPress = useCallback( (e: GestureResponderEvent) => { diff --git a/PlanList.tsx b/PlanList.tsx index 8465498..0178565 100644 --- a/PlanList.tsx +++ b/PlanList.tsx @@ -3,21 +3,20 @@ import { useFocusEffect, useNavigation, } from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {FlatList, StyleSheet, View} from 'react-native'; import {List, Searchbar} from 'react-native-paper'; +import {db} from './db'; import DrawerMenu from './DrawerMenu'; import MassiveFab from './MassiveFab'; import {Plan} from './plan'; import PlanItem from './PlanItem'; import {PlanPageParams} from './PlanPage'; -import {DatabaseContext} from './Routes'; export default function PlanList() { const [search, setSearch] = useState(''); const [plans, setPlans] = useState([]); const [refreshing, setRefresing] = useState(false); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); const refresh = useCallback(async () => { @@ -29,7 +28,7 @@ export default function PlanList() { db.executeSql(selectPlans, [`%${s}%`, `%${s}%`]); const [plansResult] = await getPlans({s: search}); setPlans(plansResult.rows.raw()); - }, [search, db]); + }, [search]); useFocusEffect( useCallback(() => { diff --git a/Routes.tsx b/Routes.tsx index ca15045..0b58ec6 100644 --- a/Routes.tsx +++ b/Routes.tsx @@ -1,10 +1,9 @@ import React, {useEffect, useState} from 'react'; import {useColorScheme} from 'react-native'; import {IconButton} from 'react-native-paper'; -import {SQLiteDatabase} from 'react-native-sqlite-storage'; import {Drawer, DrawerParamList} from './App'; import BestPage from './BestPage'; -import {getDb} from './db'; +import {migrations} from './db'; import HomePage from './HomePage'; import PlanPage from './PlanPage'; import SettingsPage from './SettingsPage'; @@ -16,17 +15,15 @@ interface Route { icon: string; } -export const DatabaseContext = React.createContext(null as any); - export default function Routes() { - const [db, setDb] = useState(null); + const [migrated, setMigrated] = useState(false); const dark = useColorScheme() === 'dark'; useEffect(() => { - getDb().then(setDb); + migrations().then(() => setMigrated(true)); }, []); - if (!db) return null; + if (!migrated) return null; const routes: Route[] = [ {name: 'Home', component: HomePage, icon: 'home'}, @@ -37,27 +34,25 @@ export default function Routes() { ]; return ( - - - {routes.map(route => ( - ( - - ), - }} - /> - ))} - - + + {routes.map(route => ( + ( + + ), + }} + /> + ))} + ); } diff --git a/SetForm.tsx b/SetForm.tsx index 4f48a3f..03e1bca 100644 --- a/SetForm.tsx +++ b/SetForm.tsx @@ -1,8 +1,8 @@ -import React, {useContext, useEffect, useRef, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import {ScrollView} from 'react-native'; import {Button, Text} from 'react-native-paper'; +import {db} from './db'; import MassiveInput from './MassiveInput'; -import {DatabaseContext} from './Routes'; import Set from './set'; export default function SetForm({ @@ -25,7 +25,6 @@ export default function SetForm({ }); const weightRef = useRef(null); const repsRef = useRef(null); - const db = useContext(DatabaseContext); useEffect(() => { console.log('SetForm.useEffect:', {uri, name: set.name}); @@ -33,7 +32,7 @@ export default function SetForm({ db.executeSql(`SELECT image FROM sets WHERE name = ? LIMIT 1`, [ set.name, ]).then(([result]) => setUri(result.rows.item(0)?.image)); - }, [uri, db, set.name]); + }, [uri, set.name]); const handleSubmit = () => { if (!name) return; diff --git a/SetItem.tsx b/SetItem.tsx index 18d5a78..5048e04 100644 --- a/SetItem.tsx +++ b/SetItem.tsx @@ -1,9 +1,9 @@ import {NavigationProp, useNavigation} from '@react-navigation/native'; -import React, {useCallback, useContext, useState} from 'react'; +import React, {useCallback, useState} from 'react'; import {GestureResponderEvent, Image} from 'react-native'; import {Divider, List, Menu, Text} from 'react-native-paper'; +import {db} from './db'; import {HomePageParams} from './HomePage'; -import {DatabaseContext} from './Routes'; import Set from './set'; export default function SetItem({ @@ -23,14 +23,13 @@ export default function SetItem({ }) { const [showMenu, setShowMenu] = useState(false); const [anchor, setAnchor] = useState({x: 0, y: 0}); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); const remove = useCallback(async () => { await db.executeSql(`DELETE FROM sets WHERE id = ?`, [item.id]); setShowMenu(false); onRemove(); - }, [setShowMenu, db, onRemove, item.id]); + }, [setShowMenu, onRemove, item.id]); const copy = useCallback(() => { const set: Set = {...item}; diff --git a/SetList.tsx b/SetList.tsx index f0cd36b..d76d0b2 100644 --- a/SetList.tsx +++ b/SetList.tsx @@ -3,14 +3,14 @@ import { useFocusEffect, useNavigation, } from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {FlatList, StyleSheet, View} from 'react-native'; import {List, Searchbar} from 'react-native-paper'; +import {db} from './db'; import DrawerMenu from './DrawerMenu'; import {HomePageParams} from './HomePage'; import MassiveFab from './MassiveFab'; import {Plan} from './plan'; -import {DatabaseContext} from './Routes'; import Set from './set'; import SetItem from './SetItem'; import Settings from './settings'; @@ -35,7 +35,6 @@ export default function SetList() { const [end, setEnd] = useState(false); const [dates, setDates] = useState(false); const [images, setImages] = useState(true); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); const selectSets = ` @@ -52,7 +51,7 @@ export default function SetList() { setSets(result.rows.raw()); setOffset(0); setEnd(false); - }, [search, db, selectSets]); + }, [search, selectSets]); const refreshLoader = useCallback(async () => { setRefreshing(true); diff --git a/SettingsPage.tsx b/SettingsPage.tsx index 43f7370..e586567 100644 --- a/SettingsPage.tsx +++ b/SettingsPage.tsx @@ -10,9 +10,9 @@ import DocumentPicker from 'react-native-document-picker'; import {Button, Searchbar, Text} from 'react-native-paper'; import {SnackbarContext} from './App'; import ConfirmDialog from './ConfirmDialog'; +import {db} from './db'; import MassiveInput from './MassiveInput'; import MassiveSwitch from './MassiveSwitch'; -import {DatabaseContext} from './Routes'; import Settings from './settings'; export default function SettingsPage() { @@ -28,7 +28,6 @@ export default function SettingsPage() { const [battery, setBattery] = useState(false); const [ignoring, setIgnoring] = useState(false); const [search, setSearch] = useState(''); - const db = useContext(DatabaseContext); const {toast} = useContext(SnackbarContext); const refresh = useCallback(async () => { @@ -45,7 +44,7 @@ export default function SettingsPage() { setNotify(!!settings.notify); setImages(!!settings.images); NativeModules.AlarmModule.ignoringBattery(setIgnoring); - }, [db]); + }, []); useEffect(() => { refresh(); @@ -75,7 +74,6 @@ export default function SettingsPage() { predict, sound, notify, - db, images, ]); diff --git a/ViewBest.tsx b/ViewBest.tsx index 09f0bed..38783b5 100644 --- a/ViewBest.tsx +++ b/ViewBest.tsx @@ -4,13 +4,7 @@ import { useNavigation, useRoute, } from '@react-navigation/native'; -import React, { - useCallback, - useContext, - useEffect, - useRef, - useState, -} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {FileSystem} from 'react-native-file-access'; import {IconButton} from 'react-native-paper'; import RNPickerSelect from 'react-native-picker-select'; @@ -18,7 +12,7 @@ import Share from 'react-native-share'; import ViewShot from 'react-native-view-shot'; import {BestPageParams} from './BestPage'; import Chart from './Chart'; -import {DatabaseContext} from './Routes'; +import {db} from './db'; import Set from './set'; import {formatMonth} from './time'; @@ -46,7 +40,6 @@ export default function ViewBest() { const [volumes, setVolumes] = useState([]); const [metric, setMetric] = useState(Metrics.Weight); const [period, setPeriod] = useState(Periods.Monthly); - const db = useContext(DatabaseContext); const navigation = useNavigation(); const viewShot = useRef(null); @@ -97,7 +90,7 @@ export default function ViewBest() { ]); if (result.rows.length === 0) return; setWeights(result.rows.raw()); - }, [params.best.name, db, period]); + }, [params.best.name, period]); const refreshVolume = useCallback(async () => { const select = ` @@ -117,13 +110,13 @@ export default function ViewBest() { ]); if (result.rows.length === 0) return; setVolumes(result.rows.raw()); - }, [db, params.best.name, period]); + }, [params.best.name, period]); useEffect(() => { if (metric === Metrics.Weight) refreshWeight(); else if (metric === Metrics.Volume) refreshVolume(); console.log(`${ViewBest.name}.useEffect`, {metric, period}); - }, [params.best.name, db, metric, period, refreshVolume, refreshWeight]); + }, [params.best.name, metric, period, refreshVolume, refreshWeight]); return ( diff --git a/WorkoutItem.tsx b/WorkoutItem.tsx index 437d963..0148969 100644 --- a/WorkoutItem.tsx +++ b/WorkoutItem.tsx @@ -1,9 +1,9 @@ import {NavigationProp, useNavigation} from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {GestureResponderEvent, Image} from 'react-native'; import {List, Menu, Text} from 'react-native-paper'; import ConfirmDialog from './ConfirmDialog'; -import {DatabaseContext} from './Routes'; +import {db} from './db'; import Workout from './workout'; import {WorkoutsPageParams} from './WorkoutsPage'; @@ -18,7 +18,6 @@ export default function WorkoutItem({ const [anchor, setAnchor] = useState({x: 0, y: 0}); const [showRemove, setShowRemove] = useState(''); const [uri, setUri] = useState(''); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); useEffect(() => { @@ -28,13 +27,13 @@ export default function WorkoutItem({ setUri(result.rows.item(0)?.image); console.log(WorkoutItem.name, item.name, result.rows.item(0)?.image); }); - }, [db, item.name]); + }, [item.name]); const remove = useCallback(async () => { await db.executeSql(`DELETE FROM sets WHERE name = ?`, [item.name]); setShowMenu(false); onRemoved(); - }, [setShowMenu, db, onRemoved, item.name]); + }, [setShowMenu, onRemoved, item.name]); const longPress = useCallback( (e: GestureResponderEvent) => { diff --git a/WorkoutList.tsx b/WorkoutList.tsx index 313caf6..33a13f9 100644 --- a/WorkoutList.tsx +++ b/WorkoutList.tsx @@ -3,11 +3,11 @@ import { useFocusEffect, useNavigation, } from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {FlatList, StyleSheet, View} from 'react-native'; import {List, Searchbar} from 'react-native-paper'; +import {db} from './db'; import MassiveFab from './MassiveFab'; -import {DatabaseContext} from './Routes'; import SetList from './SetList'; import Workout from './workout'; import WorkoutItem from './WorkoutItem'; @@ -21,7 +21,6 @@ export default function WorkoutList() { const [search, setSearch] = useState(''); const [refreshing, setRefreshing] = useState(false); const [end, setEnd] = useState(false); - const db = useContext(DatabaseContext); const navigation = useNavigation>(); const select = ` @@ -39,7 +38,7 @@ export default function WorkoutList() { setWorkouts(result.rows.raw()); setOffset(0); setEnd(false); - }, [search, db, select]); + }, [search, select]); const refreshLoader = useCallback(async () => { setRefreshing(true); @@ -81,7 +80,7 @@ export default function WorkoutList() { setWorkouts([...workouts, ...result.rows.raw()]); if (result.rows.length < limit) return setEnd(true); setOffset(newOffset); - }, [search, end, offset, workouts, db, select]); + }, [search, end, offset, workouts, select]); const onAdd = useCallback(async () => { navigation.navigate('EditWorkout', { diff --git a/db.ts b/db.ts index 20f9687..dc04ce6 100644 --- a/db.ts +++ b/db.ts @@ -1,4 +1,8 @@ -import {enablePromise, openDatabase} from 'react-native-sqlite-storage'; +import { + enablePromise, + openDatabase, + SQLiteDatabase, +} from 'react-native-sqlite-storage'; enablePromise(true); @@ -67,8 +71,10 @@ const insertSettings = ` INSERT INTO settings(minutes) VALUES(3); `; -export const getDb = async () => { - const db = await openDatabase({name: 'massive.db'}); +export let db: SQLiteDatabase; + +export const migrations = async () => { + db = await openDatabase({name: 'massive.db'}); await db.executeSql(createPlans); await db.executeSql(createSets); await db.executeSql(createSettings);