Add timer page

Closes #54
This commit is contained in:
Brandon Presley 2022-09-26 14:38:25 +13:00
parent 1cdc15dd69
commit a20a0a1832
9 changed files with 133 additions and 3 deletions

View File

@ -13,7 +13,7 @@ import {SnackbarContext} from './MassiveSnack';
import Set from './set';
import {addSet, updateSet} from './set.service';
import SetForm from './SetForm';
import {settings} from './settings.service';
import {getSettings, settings, updateSettings} from './settings.service';
export default function EditSet() {
const {params} = useRoute<RouteProp<HomePageParams, 'EditSet'>>();
@ -47,6 +47,10 @@ export default function EditSet() {
!!settings.vibrate,
settings.sound,
);
const next = new Date();
next.setTime(next.getTime() + milliseconds);
await updateSettings({...settings, nextAlarm: next.toISOString()});
await getSettings();
}, []);
const update = useCallback(

View File

@ -13,7 +13,6 @@ export default function MassiveFab(
return (
<FAB
{...props}
icon="add"
color={fabColor}
style={{
@ -22,6 +21,7 @@ export default function MassiveFab(
bottom: 60,
backgroundColor: color,
}}
{...props}
/>
);
}

View File

@ -11,6 +11,7 @@ import PlanPage from './PlanPage';
import Route from './route';
import {getSettings, settings} from './settings.service';
import SettingsPage from './SettingsPage';
import TimerPage from './TimerPage';
import WorkoutsPage from './WorkoutsPage';
const Drawer = createDrawerNavigator<DrawerParamList>();
@ -36,6 +37,7 @@ export default function Routes() {
{name: 'Plans', component: PlanPage, icon: 'calendar'},
{name: 'Best', component: BestPage, icon: 'stats-chart'},
{name: 'Workouts', component: WorkoutsPage, icon: 'barbell'},
{name: 'Timer', component: TimerPage, icon: 'time'},
{name: 'Settings', component: SettingsPage, icon: 'settings'},
];

93
TimerPage.tsx Normal file
View File

@ -0,0 +1,93 @@
import {useFocusEffect} from '@react-navigation/native';
import React, {useCallback, useEffect, useState} from 'react';
import {NativeModules, View} from 'react-native';
import {Button, Text} from 'react-native-paper';
import {MARGIN, PADDING} from './constants';
import {
getNext,
getSettings,
settings,
updateSettings,
} from './settings.service';
export default function TimerPage() {
const [next, setNext] = useState(new Date());
const [ms, setMs] = useState(0);
const [intervalId, setIntervalId] = useState(0);
const seconds =
ms > 0
? Math.floor((ms / 1000) % 60)
.toString()
.padStart(2, '0')
: '00';
const minutes =
ms > 0
? Math.floor(ms / 1000 / 60)
.toString()
.padStart(2, '0')
: '00';
useFocusEffect(
useCallback(() => {
getNext().then(nextIso =>
setNext(nextIso ? new Date(nextIso) : new Date()),
);
}, []),
);
const tick = (date: Date) => {
const remaining = date.getTime() - new Date().getTime();
console.log(`${TimerPage.name}.useEffect`, {remaining});
if (remaining <= 0) return;
setMs(remaining);
};
useEffect(() => {
console.log(`${TimerPage.name}.useEffect:`, {next});
const date = next || new Date();
tick(date);
const id = setInterval(() => {
tick(date);
}, 1000);
setIntervalId(oldId => {
clearInterval(oldId);
return id;
});
return () => clearInterval(id);
}, [next]);
const stop = () => {
NativeModules.AlarmModule.stop();
setNext(new Date());
updateSettings({...settings, nextAlarm: undefined});
getSettings();
tick(new Date());
setMs(0);
};
const add = async () => {
console.log(`${TimerPage.name}.add:`, {intervalId, next});
const date = next || new Date();
date.setTime(date.getTime() + 1000 * 60);
await updateSettings({...settings, nextAlarm: date.toISOString()});
setNext(date);
NativeModules.AlarmModule.add(ms, !!settings.vibrate, settings.sound);
tick(date);
};
return (
<View style={{padding: PADDING, alignItems: 'center'}}>
<Text>
{minutes}:{seconds}
</Text>
<Button style={{marginTop: MARGIN}} onPress={stop}>
Stop
</Button>
<Button style={{marginTop: MARGIN}} onPress={add}>
Add 1 min
</Button>
</View>
);
}

View File

@ -25,6 +25,26 @@ class AlarmModule internal constructor(context: ReactApplicationContext?) :
return "AlarmModule"
}
@RequiresApi(api = Build.VERSION_CODES.O)
@ReactMethod
fun add(milliseconds: Int, vibrate: Boolean, sound: String?) {
Log.d("AlarmModule", "Add 1 min to alarm.")
val addIntent = Intent(reactApplicationContext, TimerService::class.java)
addIntent.action = "add"
addIntent.putExtra("vibrate", vibrate)
addIntent.putExtra("sound", sound)
addIntent.data = Uri.parse("$milliseconds")
reactApplicationContext.startService(addIntent)
}
@RequiresApi(api = Build.VERSION_CODES.O)
@ReactMethod
fun stop() {
Log.d("AlarmModule", "Stop alarm.")
val intent = Intent(reactApplicationContext, TimerService::class.java)
reactApplicationContext.stopService(intent)
}
@RequiresApi(api = Build.VERSION_CODES.O)
@ReactMethod
fun timer(milliseconds: Int, vibrate: Boolean, sound: String?) {

3
db.ts
View File

@ -100,6 +100,9 @@ const migrations = [
`
ALTER TABLE settings ADD COLUMN steps BOOLEAN DEFAULT 1
`,
`
ALTER TABLE settings ADD COLUMN nextAlarm TEXT NULL
`,
];
export let db: SQLiteDatabase;

View File

@ -4,5 +4,5 @@ export type DrawerParamList = {
Best: {};
Plans: {};
Workouts: {};
Loading: {};
Timer: {};
};

View File

@ -17,3 +17,10 @@ export const updateSettings = async (value: Settings) => {
const values = keys.map(key => value[key]);
return db.executeSql(update, values);
};
export const getNext = async (): Promise<string | undefined> => {
const [result] = await db.executeSql(
`SELECT nextAlarm FROM settings LIMIT 1`,
);
return result.rows.item(0)?.nextAlarm;
};

View File

@ -9,4 +9,5 @@ export default interface Settings {
color?: string;
workouts: number;
steps: number;
nextAlarm?: string;
}