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 Page from './Page';
import Set from './set';
import {settings} from './settings.service';
import {useSettings} from './use-settings';
export default function BestList() {
const [bests, setBests] = useState<Set[]>([]);
const [search, setSearch] = useState('');
const navigation = useNavigation<NavigationProp<BestPageParams>>();
const {settings} = useSettings();
const refresh = useCallback(async () => {
const weights = await getBestWeights(search);

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import MassiveInput from './MassiveInput';
import {SnackbarContext} from './MassiveSnack';
import Set from './set';
import {getSets} from './set.service';
import {settings} from './settings.service';
import {useSettings} from './use-settings';
export default function SetForm({
save,
@ -31,6 +31,7 @@ export default function SetForm({
});
const [removeImage, setRemoveImage] = useState(false);
const {toast} = useContext(SnackbarContext);
const {settings} = useSettings();
const weightRef = useRef<TextInput>(null);
const repsRef = 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 Set from './set';
import {deleteSet} from './set.service';
import {useSettings} from './use-settings';
export default function SetItem({
item,
onRemove,
dates,
setDates,
images,
setImages,
}: {
item: Set;
onRemove: () => void;
dates: boolean;
setDates: (value: boolean) => void;
images: boolean;
setImages: (value: boolean) => void;
}) {
const [showMenu, setShowMenu] = useState(false);
const [anchor, setAnchor] = useState({x: 0, y: 0});
const {settings} = useSettings();
const navigation = useNavigation<NavigationProp<HomePageParams>>();
const remove = useCallback(async () => {
@ -51,11 +49,6 @@ export default function SetItem({
setShowMenu(false);
}, [dates, setDates]);
const toggleImages = useCallback(() => {
setImages(!images);
setShowMenu(false);
}, [images, setImages]);
return (
<>
<List.Item
@ -66,7 +59,7 @@ export default function SetItem({
description={`${item.reps} x ${item.weight}${item.unit || 'kg'}`}
onLongPress={longPress}
left={() =>
images &&
!!settings.images &&
item.image && (
<Image source={{uri: item.image}} style={{height: 75, width: 75}} />
)
@ -86,7 +79,6 @@ export default function SetItem({
visible={showMenu}
onDismiss={() => setShowMenu(false)}>
<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" />
<Divider />
<Menu.Item icon="delete" onPress={remove} title="Delete" />

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,6 @@ export default interface Settings {
showUnit?: number;
color?: string;
workouts: number;
steps: number;
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);
}