Use react context for settings

Closes #81
This commit is contained in:
Brandon Presley 2022-10-01 16:01:07 +13:00
parent 794504dee0
commit b0b804eae1
12 changed files with 92 additions and 71 deletions

View File

@ -10,12 +10,13 @@ import {getBestReps, getBestWeights} from './best.service';
import {BestPageParams} from './BestPage'; import {BestPageParams} from './BestPage';
import Page from './Page'; import Page from './Page';
import Set from './set'; import Set from './set';
import {settings} from './settings.service'; import {useSettings} from './use-settings';
export default function BestList() { export default function BestList() {
const [bests, setBests] = useState<Set[]>([]); const [bests, setBests] = useState<Set[]>([]);
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const navigation = useNavigation<NavigationProp<BestPageParams>>(); const navigation = useNavigation<NavigationProp<BestPageParams>>();
const {settings} = useSettings();
const refresh = useCallback(async () => { const refresh = useCallback(async () => {
const weights = await getBestWeights(search); const weights = await getBestWeights(search);

View File

@ -13,13 +13,15 @@ import {SnackbarContext} from './MassiveSnack';
import Set from './set'; import Set from './set';
import {addSet, updateSet} from './set.service'; import {addSet, updateSet} from './set.service';
import SetForm from './SetForm'; import SetForm from './SetForm';
import {getSettings, settings, updateSettings} from './settings.service'; import {getSettings, updateSettings} from './settings.service';
import {useSettings} from './use-settings';
export default function EditSet() { export default function EditSet() {
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>(); const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>();
const {set, count, workouts} = params; const {set, count, workouts} = params;
const navigation = useNavigation(); const navigation = useNavigation();
const {toast} = useContext(SnackbarContext); const {toast} = useContext(SnackbarContext);
const {settings, setSettings} = useSettings();
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
@ -35,23 +37,26 @@ export default function EditSet() {
headerRight: null, headerRight: null,
title, title,
}); });
}, [navigation, set, count]), }, [navigation, set, count, settings.newSet]),
); );
const startTimer = useCallback(async (value: Set) => { const startTimer = useCallback(
if (!settings.alarm) return; async (value: Set) => {
const milliseconds = if (!settings.alarm) return;
Number(value.minutes) * 60 * 1000 + Number(value.seconds) * 1000; const milliseconds =
NativeModules.AlarmModule.timer( Number(value.minutes) * 60 * 1000 + Number(value.seconds) * 1000;
milliseconds, NativeModules.AlarmModule.timer(
!!settings.vibrate, milliseconds,
settings.sound, !!settings.vibrate,
); settings.sound,
const next = new Date(); );
next.setTime(next.getTime() + milliseconds); const next = new Date();
await updateSettings({...settings, nextAlarm: next.toISOString()}); next.setTime(next.getTime() + milliseconds);
await getSettings(); await updateSettings({...settings, nextAlarm: next.toISOString()});
}, []); setSettings(await getSettings());
},
[settings, setSettings],
);
const update = useCallback( const update = useCallback(
async (value: Set) => { async (value: Set) => {
@ -75,7 +80,7 @@ export default function EditSet() {
toast("Great work King, that's a new record!", 3000); toast("Great work King, that's a new record!", 3000);
navigation.goBack(); navigation.goBack();
}, },
[navigation, startTimer, set, toast], [navigation, startTimer, set, toast, settings],
); );
const save = useCallback( const save = useCallback(

View File

@ -14,7 +14,7 @@ import MassiveInput from './MassiveInput';
import {SnackbarContext} from './MassiveSnack'; import {SnackbarContext} from './MassiveSnack';
import {updatePlanWorkouts} from './plan.service'; import {updatePlanWorkouts} from './plan.service';
import {addSet, updateManySet, updateSetImage} from './set.service'; import {addSet, updateManySet, updateSetImage} from './set.service';
import {settings} from './settings.service'; import {useSettings} from './use-settings';
import {WorkoutsPageParams} from './WorkoutsPage'; import {WorkoutsPageParams} from './WorkoutsPage';
export default function EditWorkout() { export default function EditWorkout() {
@ -37,6 +37,7 @@ export default function EditWorkout() {
const stepsRef = useRef<TextInput>(null); const stepsRef = useRef<TextInput>(null);
const minutesRef = useRef<TextInput>(null); const minutesRef = useRef<TextInput>(null);
const secondsRef = useRef<TextInput>(null); const secondsRef = useRef<TextInput>(null);
const {settings} = useSettings();
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {

View File

@ -9,27 +9,28 @@ import {DrawerParamList} from './drawer-param-list';
import HomePage from './HomePage'; import HomePage from './HomePage';
import PlanPage from './PlanPage'; import PlanPage from './PlanPage';
import Route from './route'; import Route from './route';
import {getSettings, settings} from './settings.service'; import Settings from './settings';
import {getSettings} from './settings.service';
import SettingsPage from './SettingsPage'; import SettingsPage from './SettingsPage';
import {SettingsContext} from './use-settings';
import WorkoutsPage from './WorkoutsPage'; import WorkoutsPage from './WorkoutsPage';
const Drawer = createDrawerNavigator<DrawerParamList>(); const Drawer = createDrawerNavigator<DrawerParamList>();
export default function Routes() { export default function Routes() {
const [migrated, setMigrated] = useState(false); const [settings, setSettings] = useState<Settings>();
const dark = useColorScheme() === 'dark'; const dark = useColorScheme() === 'dark';
const {setColor} = useContext(CustomTheme); const {setColor} = useContext(CustomTheme);
useEffect(() => { useEffect(() => {
runMigrations() runMigrations().then(async () => {
.then(getSettings) const gotSettings = await getSettings();
.then(() => { setSettings(gotSettings);
setMigrated(true); if (gotSettings.color) setColor(gotSettings.color);
if (settings.color) setColor(settings.color); });
});
}, [setColor]); }, [setColor]);
if (!migrated) return null; if (!settings) return null;
const routes: Route[] = [ const routes: Route[] = [
{name: 'Home', component: HomePage, icon: 'home'}, {name: 'Home', component: HomePage, icon: 'home'},
@ -40,21 +41,23 @@ export default function Routes() {
]; ];
return ( return (
<Drawer.Navigator <SettingsContext.Provider value={{settings, setSettings}}>
screenOptions={{ <Drawer.Navigator
headerTintColor: dark ? 'white' : 'black', screenOptions={{
swipeEdgeWidth: 1000, headerTintColor: dark ? 'white' : 'black',
}}> swipeEdgeWidth: 1000,
{routes.map(route => ( }}>
<Drawer.Screen {routes.map(route => (
key={route.name} <Drawer.Screen
name={route.name} key={route.name}
component={route.component} name={route.name}
options={{ component={route.component}
drawerIcon: () => <IconButton icon={route.icon} />, options={{
}} drawerIcon: () => <IconButton icon={route.icon} />,
/> }}
))} />
</Drawer.Navigator> ))}
</Drawer.Navigator>
</SettingsContext.Provider>
); );
} }

View File

@ -8,7 +8,7 @@ import MassiveInput from './MassiveInput';
import {SnackbarContext} from './MassiveSnack'; import {SnackbarContext} from './MassiveSnack';
import Set from './set'; import Set from './set';
import {getSets} from './set.service'; import {getSets} from './set.service';
import {settings} from './settings.service'; import {useSettings} from './use-settings';
export default function SetForm({ export default function SetForm({
save, save,
@ -31,6 +31,7 @@ export default function SetForm({
}); });
const [removeImage, setRemoveImage] = useState(false); const [removeImage, setRemoveImage] = useState(false);
const {toast} = useContext(SnackbarContext); const {toast} = useContext(SnackbarContext);
const {settings} = useSettings();
const weightRef = useRef<TextInput>(null); const weightRef = useRef<TextInput>(null);
const repsRef = useRef<TextInput>(null); const repsRef = useRef<TextInput>(null);
const unitRef = useRef<TextInput>(null); const unitRef = useRef<TextInput>(null);

View File

@ -5,24 +5,22 @@ import {Divider, List, Menu, Text} from 'react-native-paper';
import {HomePageParams} from './home-page-params'; import {HomePageParams} from './home-page-params';
import Set from './set'; import Set from './set';
import {deleteSet} from './set.service'; import {deleteSet} from './set.service';
import {useSettings} from './use-settings';
export default function SetItem({ export default function SetItem({
item, item,
onRemove, onRemove,
dates, dates,
setDates, setDates,
images,
setImages,
}: { }: {
item: Set; item: Set;
onRemove: () => void; onRemove: () => void;
dates: boolean; dates: boolean;
setDates: (value: boolean) => void; setDates: (value: boolean) => void;
images: boolean;
setImages: (value: boolean) => void;
}) { }) {
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
const [anchor, setAnchor] = useState({x: 0, y: 0}); const [anchor, setAnchor] = useState({x: 0, y: 0});
const {settings} = useSettings();
const navigation = useNavigation<NavigationProp<HomePageParams>>(); const navigation = useNavigation<NavigationProp<HomePageParams>>();
const remove = useCallback(async () => { const remove = useCallback(async () => {
@ -51,11 +49,6 @@ export default function SetItem({
setShowMenu(false); setShowMenu(false);
}, [dates, setDates]); }, [dates, setDates]);
const toggleImages = useCallback(() => {
setImages(!images);
setShowMenu(false);
}, [images, setImages]);
return ( return (
<> <>
<List.Item <List.Item
@ -66,7 +59,7 @@ export default function SetItem({
description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`} description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`}
onLongPress={longPress} onLongPress={longPress}
left={() => left={() =>
images && !!settings.images &&
item.image && ( item.image && (
<Image source={{uri: item.image}} style={{height: 75, width: 75}} /> <Image source={{uri: item.image}} style={{height: 75, width: 75}} />
) )
@ -86,7 +79,6 @@ export default function SetItem({
visible={showMenu} visible={showMenu}
onDismiss={() => setShowMenu(false)}> onDismiss={() => setShowMenu(false)}>
<Menu.Item icon="content-copy" onPress={copy} title="Copy" /> <Menu.Item icon="content-copy" onPress={copy} title="Copy" />
<Menu.Item icon="image" onPress={toggleImages} title="Images" />
<Menu.Item icon="event" onPress={toggleDates} title="Dates" /> <Menu.Item icon="event" onPress={toggleDates} title="Dates" />
<Divider /> <Divider />
<Menu.Item icon="delete" onPress={remove} title="Delete" /> <Menu.Item icon="delete" onPress={remove} title="Delete" />

View File

@ -14,7 +14,7 @@ import {getTodaysPlan} from './plan.service';
import Set from './set'; import Set from './set';
import {countToday, defaultSet, getSets, getToday} from './set.service'; import {countToday, defaultSet, getSets, getToday} from './set.service';
import SetItem from './SetItem'; import SetItem from './SetItem';
import {settings} from './settings.service'; import {useSettings} from './use-settings';
const limit = 15; const limit = 15;
@ -27,7 +27,7 @@ export default function SetList() {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [end, setEnd] = useState(false); const [end, setEnd] = useState(false);
const [dates, setDates] = useState(false); const [dates, setDates] = useState(false);
const [images, setImages] = useState(true); const {settings} = useSettings();
const navigation = useNavigation<NavigationProp<HomePageParams>>(); const navigation = useNavigation<NavigationProp<HomePageParams>>();
const predict = useCallback(async () => { const predict = useCallback(async () => {
@ -62,7 +62,7 @@ export default function SetList() {
if (best.name === '') setCount(0); if (best.name === '') setCount(0);
else setCount(_count); else setCount(_count);
setSet({...best, image}); setSet({...best, image});
}, []); }, [settings]);
const refresh = useCallback(async () => { const refresh = useCallback(async () => {
predict(); predict();
@ -80,7 +80,6 @@ export default function SetList() {
navigation.getParent()?.setOptions({ navigation.getParent()?.setOptions({
headerRight: () => <DrawerMenu name="Home" />, headerRight: () => <DrawerMenu name="Home" />,
}); });
setImages(!!settings.images);
}, [refresh, navigation]), }, [refresh, navigation]),
); );
@ -93,14 +92,12 @@ export default function SetList() {
<SetItem <SetItem
dates={dates} dates={dates}
setDates={setDates} setDates={setDates}
images={images}
setImages={setImages}
item={item} item={item}
key={item.id} key={item.id}
onRemove={refresh} onRemove={refresh}
/> />
), ),
[refresh, dates, setDates, images, setImages], [refresh, dates, setDates],
); );
const next = useCallback(async () => { const next = useCallback(async () => {

View File

@ -11,13 +11,15 @@ import {MARGIN} from './constants';
import Input from './input'; import Input from './input';
import {SnackbarContext} from './MassiveSnack'; import {SnackbarContext} from './MassiveSnack';
import Page from './Page'; import Page from './Page';
import {getSettings, settings, updateSettings} from './settings.service'; import {getSettings, updateSettings} from './settings.service';
import Switch from './Switch'; import Switch from './Switch';
import {useSettings} from './use-settings';
export default function SettingsPage() { export default function SettingsPage() {
const [battery, setBattery] = useState(false); const [battery, setBattery] = useState(false);
const [ignoring, setIgnoring] = useState(false); const [ignoring, setIgnoring] = useState(false);
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const {settings, setSettings} = useSettings();
const [vibrate, setVibrate] = useState(!!settings.vibrate); const [vibrate, setVibrate] = useState(!!settings.vibrate);
const [alarm, setAlarm] = useState(!!settings.alarm); const [alarm, setAlarm] = useState(!!settings.alarm);
const [newSet, setNewSet] = useState(settings.newSet); const [newSet, setNewSet] = useState(settings.newSet);
@ -49,7 +51,7 @@ export default function SettingsPage() {
workouts: +workouts, workouts: +workouts,
steps: +steps, steps: +steps,
}); });
getSettings(); getSettings().then(setSettings);
}, [ }, [
vibrate, vibrate,
alarm, alarm,
@ -61,6 +63,7 @@ export default function SettingsPage() {
color, color,
workouts, workouts,
steps, steps,
setSettings,
]); ]);
const changeAlarmEnabled = useCallback( const changeAlarmEnabled = useCallback(

View File

@ -5,6 +5,7 @@ import {List, Menu, Text} from 'react-native-paper';
import ConfirmDialog from './ConfirmDialog'; import ConfirmDialog from './ConfirmDialog';
import Set from './set'; import Set from './set';
import {deleteSetsBy} from './set.service'; import {deleteSetsBy} from './set.service';
import {useSettings} from './use-settings';
import {WorkoutsPageParams} from './WorkoutsPage'; import {WorkoutsPageParams} from './WorkoutsPage';
export default function WorkoutItem({ export default function WorkoutItem({
@ -17,6 +18,7 @@ export default function WorkoutItem({
const [showMenu, setShowMenu] = useState(false); 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 [showRemove, setShowRemove] = useState('');
const {settings} = useSettings();
const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>(); const navigation = useNavigation<NavigationProp<WorkoutsPageParams>>();
const remove = useCallback(async () => { const remove = useCallback(async () => {
@ -44,6 +46,7 @@ export default function WorkoutItem({
description={`${item.sets} sets ${minutes}:${seconds} rest`} description={`${item.sets} sets ${minutes}:${seconds} rest`}
onLongPress={longPress} onLongPress={longPress}
left={() => left={() =>
!!settings.images &&
item.image && ( item.image && (
<Image source={{uri: item.image}} style={{height: 75, width: 75}} /> <Image source={{uri: item.image}} style={{height: 75, width: 75}} />
) )

View File

@ -1,12 +1,9 @@
import {db} from './db'; import {db} from './db';
import Settings from './settings'; import Settings from './settings';
export let settings: Settings; export const getSettings = async (): Promise<Settings> => {
export const getSettings = async () => {
const [result] = await db.executeSql(`SELECT * FROM settings LIMIT 1`); const [result] = await db.executeSql(`SELECT * FROM settings LIMIT 1`);
settings = result.rows.item(0); return result.rows.item(0);
return settings;
}; };
export const updateSettings = async (value: Settings) => { export const updateSettings = async (value: Settings) => {

View File

@ -8,6 +8,6 @@ export default interface Settings {
showUnit?: number; showUnit?: number;
color?: string; color?: string;
workouts: number; workouts: number;
steps: number;
nextAlarm?: string; nextAlarm?: string;
steps?: number;
} }

18
use-settings.ts Normal file
View File

@ -0,0 +1,18 @@
import React, {useContext} from 'react';
import Settings from './settings';
export const SettingsContext = React.createContext<{
settings: Settings;
setSettings: (value: Settings) => void;
}>({
settings: {
alarm: 0,
vibrate: 1,
workouts: 0,
},
setSettings: () => null,
});
export function useSettings() {
return useContext(SettingsContext);
}