Ensure only one connection to SQLite exists
This commit is contained in:
parent
ecb436f8a6
commit
570b43715f
22
App.tsx
22
App.tsx
|
@ -1,19 +1,20 @@
|
|||
import {useAsyncStorage} from '@react-native-async-storage/async-storage';
|
||||
import Ionicon from 'react-native-vector-icons/Ionicons';
|
||||
import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';
|
||||
import {
|
||||
DarkTheme,
|
||||
DefaultTheme,
|
||||
NavigationContainer,
|
||||
} from '@react-navigation/native';
|
||||
import React, {useEffect} from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {StatusBar, useColorScheme} from 'react-native';
|
||||
import {
|
||||
DarkTheme as DarkThemePaper,
|
||||
DefaultTheme as DefaultThemePaper,
|
||||
Provider,
|
||||
} from 'react-native-paper';
|
||||
import {setupSchema} from './db';
|
||||
import {SQLiteDatabase} from 'react-native-sqlite-storage';
|
||||
import Ionicon from 'react-native-vector-icons/Ionicons';
|
||||
import {createPlans, createSets, getDb} from './db';
|
||||
import Exercises from './Exercises';
|
||||
import Home from './Home';
|
||||
import Plans from './Plans';
|
||||
|
@ -27,16 +28,21 @@ export type RootStackParamList = {
|
|||
Plans: {};
|
||||
};
|
||||
|
||||
setupSchema();
|
||||
export const DatabaseContext = React.createContext<SQLiteDatabase>({} as any);
|
||||
|
||||
const App = () => {
|
||||
const [db, setDb] = useState<SQLiteDatabase | null>(null);
|
||||
const dark = useColorScheme() === 'dark';
|
||||
const {getItem: getMinutes, setItem: setMinutes} = useAsyncStorage('minutes');
|
||||
const {getItem: getSeconds, setItem: setSeconds} = useAsyncStorage('seconds');
|
||||
const {getItem: getAlarmEnabled, setItem: setAlarmEnabled} =
|
||||
useAsyncStorage('alarmEnabled');
|
||||
|
||||
const defaults = async () => {
|
||||
const init = async () => {
|
||||
const gotDb = await getDb();
|
||||
await gotDb.executeSql(createPlans);
|
||||
await gotDb.executeSql(createSets);
|
||||
setDb(gotDb);
|
||||
const minutes = await getMinutes();
|
||||
if (minutes === null) await setMinutes('3');
|
||||
const seconds = await getSeconds();
|
||||
|
@ -46,7 +52,7 @@ const App = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
defaults();
|
||||
init();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
@ -55,12 +61,16 @@ const App = () => {
|
|||
settings={{icon: props => <Ionicon {...props} />}}>
|
||||
<NavigationContainer theme={dark ? DarkTheme : DefaultTheme}>
|
||||
<StatusBar barStyle={dark ? 'light-content' : 'dark-content'} />
|
||||
{db && (
|
||||
<DatabaseContext.Provider value={db}>
|
||||
<Tab.Navigator>
|
||||
<Tab.Screen name="Home" component={Home} />
|
||||
<Tab.Screen name="Plans" component={Plans} />
|
||||
<Tab.Screen name="Exercises" component={Exercises} />
|
||||
<Tab.Screen name="Settings" component={Settings} />
|
||||
</Tab.Navigator>
|
||||
</DatabaseContext.Provider>
|
||||
)}
|
||||
</NavigationContainer>
|
||||
</Provider>
|
||||
);
|
||||
|
|
23
EditPlan.tsx
23
EditPlan.tsx
|
@ -1,10 +1,9 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import {Button, Dialog, Modal, Portal, TextInput} from 'react-native-paper';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {Button, Dialog, Portal} from 'react-native-paper';
|
||||
import {DatabaseContext} from './App';
|
||||
import DayMenu from './DayMenu';
|
||||
import WorkoutMenu from './WorkoutMenu';
|
||||
import {getDb} from './db';
|
||||
import {Plan} from './plan';
|
||||
import WorkoutMenu from './WorkoutMenu';
|
||||
|
||||
export default function EditPlan({
|
||||
id,
|
||||
|
@ -22,12 +21,10 @@ export default function EditPlan({
|
|||
const [days, setDays] = useState('');
|
||||
const [workouts, setWorkouts] = useState('');
|
||||
const [names, setNames] = useState<string[]>([]);
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
useEffect(() => {
|
||||
getDb().then(async db => {
|
||||
const [namesResult] = await db.executeSql(
|
||||
'SELECT DISTINCT name FROM sets',
|
||||
);
|
||||
const refresh = async () => {
|
||||
const [namesResult] = await db.executeSql('SELECT DISTINCT name FROM sets');
|
||||
if (!namesResult.rows.length) return;
|
||||
setNames(namesResult.rows.raw().map(({name}) => name));
|
||||
if (!id) return;
|
||||
|
@ -38,12 +35,14 @@ export default function EditPlan({
|
|||
const set: Plan = result.rows.item(0);
|
||||
setDays(set.days);
|
||||
setWorkouts(set.workouts);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
}, [id]);
|
||||
|
||||
const save = async () => {
|
||||
if (!days || !workouts) return;
|
||||
const db = await getDb();
|
||||
if (!id)
|
||||
await db.executeSql(`INSERT INTO plans(days, workouts) VALUES (?, ?)`, [
|
||||
days,
|
||||
|
|
41
EditSet.tsx
41
EditSet.tsx
|
@ -1,22 +1,20 @@
|
|||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import {Button, Dialog, Modal, Portal, TextInput} from 'react-native-paper';
|
||||
import {getDb} from './db';
|
||||
import Set from './set';
|
||||
import {format} from 'date-fns';
|
||||
import React, {useContext, useEffect, useRef, useState} from 'react';
|
||||
import {StyleSheet, Text} from 'react-native';
|
||||
import {Button, Dialog, Portal, TextInput} from 'react-native-paper';
|
||||
import {DatabaseContext} from './App';
|
||||
import Set from './set';
|
||||
|
||||
export default function EditSet({
|
||||
id,
|
||||
onSave,
|
||||
show,
|
||||
setShow,
|
||||
clearId,
|
||||
set,
|
||||
}: {
|
||||
id?: number;
|
||||
clearId: () => void;
|
||||
onSave: () => void;
|
||||
show: boolean;
|
||||
setShow: (visible: boolean) => void;
|
||||
set?: Set;
|
||||
}) {
|
||||
const [name, setName] = useState('');
|
||||
const [reps, setReps] = useState('');
|
||||
|
@ -26,27 +24,24 @@ export default function EditSet({
|
|||
const weightRef = useRef<any>(null);
|
||||
const repsRef = useRef<any>(null);
|
||||
const unitRef = useRef<any>(null);
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return setCreated(new Date(new Date().toUTCString()));
|
||||
getDb().then(async db => {
|
||||
const [result] = await db.executeSql(`SELECT * FROM sets WHERE id = ?`, [
|
||||
id,
|
||||
]);
|
||||
if (!result.rows.item(0)) throw new Error("Can't find specified Set.");
|
||||
const set: Set = result.rows.item(0);
|
||||
const refresh = async () => {
|
||||
if (!set) return setCreated(new Date(new Date().toUTCString()));
|
||||
setName(set.name);
|
||||
setReps(set.reps.toString());
|
||||
setWeight(set.weight.toString());
|
||||
setUnit(set.unit);
|
||||
setCreated(new Date(set.created));
|
||||
});
|
||||
}, [id]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
}, [set]);
|
||||
|
||||
const save = async () => {
|
||||
if (!name || !reps || !weight) return;
|
||||
const db = await getDb();
|
||||
if (!id)
|
||||
if (!set)
|
||||
await db.executeSql(
|
||||
`INSERT INTO sets(name, reps, weight, created, unit) VALUES (?,?,?,?,?)`,
|
||||
[name, reps, weight, new Date().toISOString(), unit || 'kg'],
|
||||
|
@ -54,7 +49,7 @@ export default function EditSet({
|
|||
else
|
||||
await db.executeSql(
|
||||
`UPDATE sets SET name = ?, reps = ?, weight = ?, unit = ? WHERE id = ?`,
|
||||
[name, reps, weight, unit, id],
|
||||
[name, reps, weight, unit, set.id],
|
||||
);
|
||||
setShow(false);
|
||||
onSave();
|
||||
|
@ -63,7 +58,7 @@ export default function EditSet({
|
|||
return (
|
||||
<Portal>
|
||||
<Dialog visible={show} onDismiss={() => setShow(false)}>
|
||||
<Dialog.Title>{id ? `Edit "${name}"` : 'Add a set'}</Dialog.Title>
|
||||
<Dialog.Title>{set?.id ? `Edit "${name}"` : 'Add a set'}</Dialog.Title>
|
||||
<Dialog.Content>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {FlatList, StyleSheet, View} from 'react-native';
|
||||
import {List, Searchbar, TextInput} from 'react-native-paper';
|
||||
import {RootStackParamList} from './App';
|
||||
import {getDb} from './db';
|
||||
import {List, Searchbar} from 'react-native-paper';
|
||||
import {DatabaseContext} from './App';
|
||||
import Exercise from './exercise';
|
||||
|
||||
export default function Exercises() {
|
||||
const [exercises, setExercises] = useState<Exercise[]>([]);
|
||||
const [search, setSearch] = useState('');
|
||||
const [refreshing, setRefresing] = useState(false);
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
const refresh = async () => {
|
||||
const db = await getDb();
|
||||
const [result] = await db.executeSql(
|
||||
`SELECT name, reps, unit, MAX(weight) AS weight
|
||||
FROM sets
|
||||
|
|
41
Home.tsx
41
Home.tsx
|
@ -1,9 +1,9 @@
|
|||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {FlatList, NativeModules, SafeAreaView, StyleSheet} from 'react-native';
|
||||
import {AnimatedFAB, Searchbar} from 'react-native-paper';
|
||||
import {getSets} from './db';
|
||||
import {DatabaseContext} from './App';
|
||||
import EditSet from './EditSet';
|
||||
|
||||
import Set from './set';
|
||||
|
@ -13,11 +13,28 @@ const limit = 20;
|
|||
|
||||
export default function Home() {
|
||||
const [sets, setSets] = useState<Set[]>();
|
||||
const [id, setId] = useState<number>();
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [showEdit, setShowEdit] = useState(false);
|
||||
const [edit, setEdit] = useState<Set>();
|
||||
const [show, setShow] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
const [refreshing, setRefresing] = useState(false);
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
const selectSets = `
|
||||
SELECT * from sets
|
||||
WHERE name LIKE ?
|
||||
ORDER BY created DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`;
|
||||
const getSets = ({
|
||||
search,
|
||||
limit,
|
||||
offset,
|
||||
}: {
|
||||
search: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}) => db.executeSql(selectSets, [`%${search}%`, limit, offset]);
|
||||
|
||||
const refresh = async () => {
|
||||
const [result] = await getSets({search, limit, offset: 0});
|
||||
|
@ -43,8 +60,8 @@ export default function Home() {
|
|||
<SetItem
|
||||
item={item}
|
||||
key={item.id}
|
||||
setShowEdit={setShowEdit}
|
||||
setId={setId}
|
||||
setShowEdit={setShow}
|
||||
setSet={setEdit}
|
||||
onRemove={refresh}
|
||||
/>
|
||||
);
|
||||
|
@ -82,13 +99,7 @@ export default function Home() {
|
|||
refreshing={refreshing}
|
||||
onRefresh={refreshLoader}
|
||||
/>
|
||||
<EditSet
|
||||
clearId={() => setId(undefined)}
|
||||
id={id}
|
||||
show={showEdit}
|
||||
setShow={setShowEdit}
|
||||
onSave={save}
|
||||
/>
|
||||
<EditSet set={edit} show={show} setShow={setShow} onSave={save} />
|
||||
|
||||
<AnimatedFAB
|
||||
extended={false}
|
||||
|
@ -96,8 +107,8 @@ export default function Home() {
|
|||
icon="add"
|
||||
style={{position: 'absolute', right: 20, bottom: 20}}
|
||||
onPress={() => {
|
||||
setId(undefined);
|
||||
setShowEdit(true);
|
||||
setEdit(undefined);
|
||||
setShow(true);
|
||||
}}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {useState} from 'react';
|
||||
import React, {useContext, useState} from 'react';
|
||||
import {IconButton, List, Menu} from 'react-native-paper';
|
||||
import {getDb} from './db';
|
||||
import {DatabaseContext} from './App';
|
||||
import {Plan} from './plan';
|
||||
|
||||
export default function PlanItem({
|
||||
|
@ -15,9 +15,9 @@ export default function PlanItem({
|
|||
onRemove: () => void;
|
||||
}) {
|
||||
const [show, setShow] = useState(false);
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
const remove = async () => {
|
||||
const db = await getDb();
|
||||
await db.executeSql(`DELETE FROM plans WHERE id = ?`, [item.id]);
|
||||
setShow(false);
|
||||
onRemove();
|
||||
|
|
20
Plans.tsx
20
Plans.tsx
|
@ -1,9 +1,9 @@
|
|||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import {format} from 'date-fns';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {FlatList, StyleSheet, Text, View} from 'react-native';
|
||||
import {AnimatedFAB, ProgressBar, Searchbar} from 'react-native-paper';
|
||||
import {getPlans, getProgress} from './db';
|
||||
import {DatabaseContext} from './App';
|
||||
import EditPlan from './EditPlan';
|
||||
import {Plan} from './plan';
|
||||
import PlanItem from './PlanItem';
|
||||
|
@ -18,6 +18,22 @@ export default function Plans() {
|
|||
const [progresses, setProgresses] = useState<Progress[]>([]);
|
||||
const today = `%${format(new Date(new Date().toUTCString()), 'EEEE')}%`;
|
||||
const now = `${format(new Date(new Date().toUTCString()), 'yyyy-MM-dd')}%`;
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
const selectPlans = `
|
||||
SELECT * from plans
|
||||
WHERE days LIKE ? OR workouts LIKE ?
|
||||
`;
|
||||
const getPlans = ({search}: {search: string}) =>
|
||||
db.executeSql(selectPlans, [`%${search}%`, `%${search}%`]);
|
||||
|
||||
const selectProgress = `
|
||||
SELECT COUNT(*) as count from sets
|
||||
WHERE created LIKE ?
|
||||
AND name = ?
|
||||
`;
|
||||
const getProgress = ({created, name}: {created: string; name: string}) =>
|
||||
db.executeSql(selectProgress, [`%${created}%`, name]);
|
||||
|
||||
const refresh = async () => {
|
||||
const [plansResult] = await getPlans({search});
|
||||
|
|
12
SetItem.tsx
12
SetItem.tsx
|
@ -1,23 +1,23 @@
|
|||
import React, {useState} from 'react';
|
||||
import React, {useContext, useState} from 'react';
|
||||
import {IconButton, List, Menu} from 'react-native-paper';
|
||||
import {getDb} from './db';
|
||||
import {DatabaseContext} from './App';
|
||||
import Set from './set';
|
||||
|
||||
export default function SetItem({
|
||||
item,
|
||||
setId,
|
||||
setSet,
|
||||
setShowEdit,
|
||||
onRemove,
|
||||
}: {
|
||||
item: Set;
|
||||
setId: (id: number) => void;
|
||||
setSet: (set: Set) => void;
|
||||
setShowEdit: (show: boolean) => void;
|
||||
onRemove: () => void;
|
||||
}) {
|
||||
const [show, setShow] = useState(false);
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
const remove = async () => {
|
||||
const db = await getDb();
|
||||
await db.executeSql(`DELETE FROM sets WHERE id = ?`, [item.id]);
|
||||
setShow(false);
|
||||
onRemove();
|
||||
|
@ -27,7 +27,7 @@ export default function SetItem({
|
|||
<>
|
||||
<List.Item
|
||||
onPress={() => {
|
||||
setId(item.id);
|
||||
setSet(item);
|
||||
setShowEdit(true);
|
||||
}}
|
||||
title={item.name}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import {useFocusEffect, useNavigation} from '@react-navigation/native';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {NativeModules, StyleSheet, Text, View} from 'react-native';
|
||||
import {Button, Snackbar, Switch, TextInput} from 'react-native-paper';
|
||||
import {DatabaseContext} from './App';
|
||||
import BatteryDialog from './BatteryDialog';
|
||||
import {getDb} from './db';
|
||||
|
||||
export default function Settings() {
|
||||
const [minutes, setMinutes] = useState<string>('');
|
||||
|
@ -13,6 +13,7 @@ export default function Settings() {
|
|||
const [snackbar, setSnackbar] = useState('');
|
||||
const [showBattery, setShowBattery] = useState(false);
|
||||
const [ignoring, setIgnoring] = useState(false);
|
||||
const db = useContext(DatabaseContext);
|
||||
|
||||
const refresh = async () => {
|
||||
setMinutes((await AsyncStorage.getItem('minutes')) || '3');
|
||||
|
@ -34,7 +35,6 @@ export default function Settings() {
|
|||
const clear = async () => {
|
||||
setSnackbar('Deleting all data...');
|
||||
setTimeout(() => setSnackbar(''), 5000);
|
||||
const db = await getDb();
|
||||
await db.executeSql(`DELETE FROM sets`);
|
||||
};
|
||||
|
||||
|
|
37
db.ts
37
db.ts
|
@ -3,7 +3,7 @@ import {enablePromise, openDatabase} from 'react-native-sqlite-storage';
|
|||
enablePromise(true);
|
||||
export const getDb = () => openDatabase({name: 'massive.db'});
|
||||
|
||||
const createSets = `
|
||||
export const createSets = `
|
||||
CREATE TABLE IF NOT EXISTS sets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
|
@ -14,7 +14,7 @@ const createSets = `
|
|||
);
|
||||
`;
|
||||
|
||||
const createPlans = `
|
||||
export const createPlans = `
|
||||
CREATE TABLE IF NOT EXISTS plans (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
days TEXT NOT NULL,
|
||||
|
@ -22,21 +22,6 @@ const createPlans = `
|
|||
);
|
||||
`;
|
||||
|
||||
export const setupSchema = () =>
|
||||
getDb().then(db => {
|
||||
db.executeSql(createSets);
|
||||
db.executeSql(createPlans);
|
||||
});
|
||||
|
||||
const selectPlans = `
|
||||
SELECT * from plans
|
||||
WHERE days LIKE ? OR workouts LIKE ?
|
||||
`;
|
||||
export const getPlans = ({search}: {search: string}) =>
|
||||
getDb().then(db =>
|
||||
db.executeSql(selectPlans, [`%${search}%`, `%${search}%`]),
|
||||
);
|
||||
|
||||
const selectProgress = `
|
||||
SELECT count(*) as count from sets
|
||||
WHERE created LIKE ?
|
||||
|
@ -44,21 +29,3 @@ const selectProgress = `
|
|||
`;
|
||||
export const getProgress = ({created, name}: {created: string; name: string}) =>
|
||||
getDb().then(db => db.executeSql(selectProgress, [`%${created}%`, name]));
|
||||
|
||||
const selectSets = `
|
||||
SELECT * from sets
|
||||
WHERE name LIKE ?
|
||||
ORDER BY created DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`;
|
||||
|
||||
export const getSets = ({
|
||||
search,
|
||||
limit,
|
||||
offset,
|
||||
}: {
|
||||
search: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}) =>
|
||||
getDb().then(db => db.executeSql(selectSets, [`%${search}%`, limit, offset]));
|
||||
|
|
Loading…
Reference in New Issue
Block a user