189 lines
5.0 KiB
TypeScript
189 lines
5.0 KiB
TypeScript
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||
|
import {NativeStackScreenProps} from '@react-navigation/native-stack';
|
||
|
import React, {useEffect, useState} from 'react';
|
||
|
import {
|
||
|
FlatList,
|
||
|
SafeAreaView,
|
||
|
StyleSheet,
|
||
|
TextInput,
|
||
|
Vibration,
|
||
|
View,
|
||
|
} from 'react-native';
|
||
|
import BackgroundTimer from 'react-native-background-timer';
|
||
|
import {Button, List} from 'react-native-paper';
|
||
|
import PushNotification from 'react-native-push-notification';
|
||
|
import Sound from 'react-native-sound';
|
||
|
import Alarm from './Alarm';
|
||
|
import {RootStackParamList} from './App';
|
||
|
import {ALARM} from './channels';
|
||
|
import {getDb} from './db';
|
||
|
import EditSet from './EditSet';
|
||
|
|
||
|
import Set from './Set';
|
||
|
|
||
|
const limit = 20;
|
||
|
|
||
|
export default function Home({
|
||
|
navigation,
|
||
|
}: NativeStackScreenProps<RootStackParamList, 'Home'>) {
|
||
|
const [sets, setSets] = useState<Set[]>();
|
||
|
const [id, setId] = useState<number>();
|
||
|
const [offset, setOffset] = useState(0);
|
||
|
const [refreshing, setRefreshing] = useState(false);
|
||
|
const [showTimer, setShowTimer] = useState(false);
|
||
|
const [showEdit, setShowEdit] = useState(false);
|
||
|
const [search, setSearch] = useState('');
|
||
|
|
||
|
const refresh = async () => {
|
||
|
setRefreshing(true);
|
||
|
const db = await getDb();
|
||
|
const [result] = await db.executeSql(
|
||
|
`SELECT * from sets WHERE name LIKE ? LIMIT ? OFFSET ?`,
|
||
|
[`%${search}%`, limit, 0],
|
||
|
);
|
||
|
setRefreshing(false);
|
||
|
if (!result) return setSets([]);
|
||
|
setSets(result.rows.raw());
|
||
|
setOffset(0);
|
||
|
};
|
||
|
|
||
|
const alarm = new Sound('argon.mp3', Sound.MAIN_BUNDLE, error => {
|
||
|
if (error) throw new Error(error);
|
||
|
});
|
||
|
|
||
|
const focus = async () => {
|
||
|
alarm.stop();
|
||
|
Vibration.cancel();
|
||
|
};
|
||
|
|
||
|
useEffect(() => {
|
||
|
refresh();
|
||
|
}, [search]);
|
||
|
|
||
|
useEffect(() => navigation.addListener('focus', focus), [navigation]);
|
||
|
|
||
|
const renderItem = ({item}: {item: Set}) => (
|
||
|
<List.Item
|
||
|
onPress={() => {
|
||
|
setId(item.id);
|
||
|
setShowEdit(true);
|
||
|
}}
|
||
|
key={item.id}
|
||
|
title={item.name}
|
||
|
description={`${item.reps} x ${item.weight}${item.unit}`}
|
||
|
/>
|
||
|
);
|
||
|
|
||
|
const save = async () => {
|
||
|
refresh();
|
||
|
const enabled = await AsyncStorage.getItem('alarmEnabled');
|
||
|
console.log({enabled});
|
||
|
if (enabled !== 'true') return;
|
||
|
const minutes = await AsyncStorage.getItem('minutes');
|
||
|
const seconds = await AsyncStorage.getItem('seconds');
|
||
|
const milliseconds = Number(minutes) * 60 * 1000 + Number(seconds) * 1000;
|
||
|
const when = new Date();
|
||
|
when.setTime(when.getTime() + milliseconds);
|
||
|
await AsyncStorage.setItem('nextAlarm', when.toISOString());
|
||
|
const timeoutId = BackgroundTimer.setTimeout(() => {
|
||
|
alarm.play(_onEnd => Vibration.cancel());
|
||
|
Vibration.vibrate([0, 400, 600], /*repeat=*/ true);
|
||
|
PushNotification.localNotification({
|
||
|
message: 'Timer up',
|
||
|
channelId: ALARM,
|
||
|
vibrate: true,
|
||
|
});
|
||
|
}, Number(milliseconds));
|
||
|
BackgroundTimer.clearTimeout(
|
||
|
Number(await AsyncStorage.getItem('timeoutId')),
|
||
|
);
|
||
|
await AsyncStorage.setItem('timeoutId', timeoutId.toString());
|
||
|
};
|
||
|
|
||
|
const close = () => {
|
||
|
alarm.stop();
|
||
|
Vibration.cancel();
|
||
|
setShowTimer(false);
|
||
|
};
|
||
|
|
||
|
const next = async () => {
|
||
|
const newOffset = offset + limit;
|
||
|
setRefreshing(true);
|
||
|
const db = await getDb();
|
||
|
const [result] = await db.executeSql(
|
||
|
`SELECT * from sets WHERE name LIKE ? LIMIT ? OFFSET ?`,
|
||
|
[`%${search}%`, limit, newOffset],
|
||
|
);
|
||
|
setRefreshing(false);
|
||
|
if (!result) return;
|
||
|
if (!sets) return;
|
||
|
setSets([...sets, ...result.rows.raw()]);
|
||
|
setOffset(newOffset);
|
||
|
};
|
||
|
|
||
|
return (
|
||
|
<SafeAreaView style={styles.container}>
|
||
|
<TextInput placeholder="Search" value={search} onChangeText={setSearch} />
|
||
|
<FlatList
|
||
|
style={{height: '100%'}}
|
||
|
data={sets}
|
||
|
renderItem={renderItem}
|
||
|
keyExtractor={set => set.id.toString()}
|
||
|
onRefresh={refresh}
|
||
|
refreshing={refreshing}
|
||
|
onScrollEndDrag={next}
|
||
|
/>
|
||
|
<View style={styles.bottom}>
|
||
|
<View style={styles.button}></View>
|
||
|
<View style={styles.button}>
|
||
|
<Button icon="time" onPress={() => setShowTimer(true)}>
|
||
|
Time left
|
||
|
</Button>
|
||
|
</View>
|
||
|
<View style={styles.button}>
|
||
|
<Button icon="stop" onPress={close}>
|
||
|
Stop
|
||
|
</Button>
|
||
|
</View>
|
||
|
<View style={styles.button}>
|
||
|
<EditSet
|
||
|
id={id}
|
||
|
setId={setId}
|
||
|
show={showEdit}
|
||
|
setShow={setShowEdit}
|
||
|
onSave={save}
|
||
|
/>
|
||
|
</View>
|
||
|
</View>
|
||
|
{showTimer && <Alarm onClose={close} />}
|
||
|
</SafeAreaView>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
const styles = StyleSheet.create({
|
||
|
name: {
|
||
|
fontSize: 18,
|
||
|
},
|
||
|
button: {
|
||
|
marginRight: 10,
|
||
|
},
|
||
|
container: {
|
||
|
flex: 1,
|
||
|
paddingLeft: 20,
|
||
|
paddingRight: 20,
|
||
|
},
|
||
|
bottom: {
|
||
|
alignSelf: 'center',
|
||
|
marginBottom: 10,
|
||
|
flexDirection: 'row',
|
||
|
},
|
||
|
set: {
|
||
|
marginBottom: 10,
|
||
|
fontSize: 18,
|
||
|
shadowColor: 'red',
|
||
|
shadowRadius: 10,
|
||
|
shadowOffset: {width: 2, height: 40},
|
||
|
shadowOpacity: 8,
|
||
|
},
|
||
|
});
|