Switch some modals to dialogs and fix light mode
This commit is contained in:
parent
d938ec775a
commit
07fa6f7ab2
47
App.tsx
47
App.tsx
|
@ -1,4 +1,5 @@
|
|||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
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,
|
||||
|
@ -7,6 +8,11 @@ import {
|
|||
} from '@react-navigation/native';
|
||||
import React, {useEffect} 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 Exercises from './Exercises';
|
||||
import Home from './Home';
|
||||
|
@ -25,23 +31,38 @@ setupSchema();
|
|||
|
||||
const App = () => {
|
||||
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 minutes = await getMinutes();
|
||||
if (minutes === null) await setMinutes('3');
|
||||
const seconds = await getSeconds();
|
||||
if (seconds === null) await setSeconds('30');
|
||||
const alarmEnabled = await getAlarmEnabled();
|
||||
if (alarmEnabled === null) await setAlarmEnabled('false');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
AsyncStorage.getItem('minutes').then(async minutes => {
|
||||
if (!minutes) await AsyncStorage.setItem('minutes', '3');
|
||||
});
|
||||
defaults();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<NavigationContainer theme={dark ? DarkTheme : DefaultTheme}>
|
||||
<StatusBar barStyle={dark ? 'light-content' : 'dark-content'} />
|
||||
<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>
|
||||
</NavigationContainer>
|
||||
<Provider
|
||||
theme={dark ? DarkThemePaper : DefaultThemePaper}
|
||||
settings={{icon: props => <Ionicon {...props} />}}>
|
||||
<NavigationContainer theme={dark ? DarkTheme : DefaultTheme}>
|
||||
<StatusBar barStyle={dark ? 'light-content' : 'dark-content'} />
|
||||
<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>
|
||||
</NavigationContainer>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
33
BatteryDialog.tsx
Normal file
33
BatteryDialog.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import React from 'react';
|
||||
import {NativeModules, Text} from 'react-native';
|
||||
import {Button, Dialog, Portal} from 'react-native-paper';
|
||||
|
||||
export default function BatteryDialog({
|
||||
show,
|
||||
setShow,
|
||||
}: {
|
||||
show: boolean;
|
||||
setShow: (show: boolean) => void;
|
||||
}) {
|
||||
const ok = () => {
|
||||
NativeModules.AlarmModule.openBatteryOptimizations();
|
||||
setShow(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Dialog visible={show} onDismiss={() => setShow(false)}>
|
||||
<Dialog.Title>Battery optimizations</Dialog.Title>
|
||||
<Dialog.Content>
|
||||
<Text>
|
||||
Disable battery optimizations for Massive to use rest timers. Settings -> Apps -> Massive -> Battery -> Unrestricted.
|
||||
</Text>
|
||||
</Dialog.Content>
|
||||
<Dialog.Actions>
|
||||
<Button onPress={ok}>Open settings</Button>
|
||||
<Button onPress={() => setShow(false)}>Cancel</Button>
|
||||
</Dialog.Actions>
|
||||
</Dialog>
|
||||
</Portal>
|
||||
);
|
||||
}
|
42
EditPlan.tsx
42
EditPlan.tsx
|
@ -1,6 +1,6 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import {Button, Modal, Portal, TextInput} from 'react-native-paper';
|
||||
import {Button, Dialog, Modal, Portal, TextInput} from 'react-native-paper';
|
||||
import DayMenu from './DayMenu';
|
||||
import WorkoutMenu from './WorkoutMenu';
|
||||
import {getDb} from './db';
|
||||
|
@ -85,12 +85,9 @@ export default function EditPlan({
|
|||
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={show}
|
||||
contentContainerStyle={styles.modal}
|
||||
onDismiss={() => setShow(false)}>
|
||||
<Text style={styles.title}>{id ? `Edit "${days}"` : 'Add a plan'}</Text>
|
||||
<View style={{alignItems: 'flex-start'}}>
|
||||
<Dialog visible={show} onDismiss={() => setShow(false)}>
|
||||
<Dialog.Title>{id ? `Edit "${days}"` : 'Add a plan'}</Dialog.Title>
|
||||
<Dialog.Content>
|
||||
{days.split(',').map((day, index) => (
|
||||
<DayMenu
|
||||
index={index}
|
||||
|
@ -112,39 +109,16 @@ export default function EditPlan({
|
|||
key={index}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
<View style={styles.bottom}>
|
||||
</Dialog.Content>
|
||||
<Dialog.Actions>
|
||||
<Button mode="contained" icon="save" onPress={save}>
|
||||
Save
|
||||
</Button>
|
||||
<Button icon="close" onPress={() => setShow(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
{id && (
|
||||
<Button icon="copy" onPress={clearId}>
|
||||
Duplicate
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
</Modal>
|
||||
</Dialog.Actions>
|
||||
</Dialog>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
backgroundColor: 'black',
|
||||
padding: 20,
|
||||
},
|
||||
text: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
bottom: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 10,
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
marginBottom: 10,
|
||||
},
|
||||
});
|
||||
|
|
105
EditSet.tsx
105
EditSet.tsx
|
@ -1,6 +1,6 @@
|
|||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import {Button, Modal, Portal, TextInput} from 'react-native-paper';
|
||||
import {Button, Dialog, Modal, Portal, TextInput} from 'react-native-paper';
|
||||
import {getDb} from './db';
|
||||
import Set from './set';
|
||||
import {format} from 'date-fns';
|
||||
|
@ -62,75 +62,62 @@ export default function EditSet({
|
|||
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={show}
|
||||
contentContainerStyle={styles.modal}
|
||||
onDismiss={() => setShow(false)}>
|
||||
<Text style={styles.title}>{id ? `Edit "${name}"` : 'Add a set'}</Text>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
autoFocus
|
||||
label="Name *"
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
onSubmitEditing={() => repsRef.current?.focus()}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
label="Reps *"
|
||||
keyboardType="numeric"
|
||||
value={reps}
|
||||
onChangeText={setReps}
|
||||
ref={repsRef}
|
||||
onSubmitEditing={() => weightRef.current?.focus()}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
label="Weight *"
|
||||
keyboardType="numeric"
|
||||
value={weight}
|
||||
onChangeText={setWeight}
|
||||
onSubmitEditing={save}
|
||||
ref={weightRef}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
label="Unit (kg)"
|
||||
value={unit}
|
||||
onChangeText={setUnit}
|
||||
ref={unitRef}
|
||||
onSubmitEditing={save}
|
||||
/>
|
||||
<Text style={styles.text}>{format(created, 'PPPP p')}</Text>
|
||||
<View style={styles.bottom}>
|
||||
<Button mode="contained" icon="save" onPress={save}>
|
||||
Save
|
||||
</Button>
|
||||
<Dialog visible={show} onDismiss={() => setShow(false)}>
|
||||
<Dialog.Title>{id ? `Edit "${name}"` : 'Add a set'}</Dialog.Title>
|
||||
<Dialog.Content>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
autoFocus
|
||||
label="Name *"
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
onSubmitEditing={() => repsRef.current?.focus()}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
label="Reps *"
|
||||
keyboardType="numeric"
|
||||
value={reps}
|
||||
onChangeText={setReps}
|
||||
ref={repsRef}
|
||||
onSubmitEditing={() => weightRef.current?.focus()}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
label="Weight *"
|
||||
keyboardType="numeric"
|
||||
value={weight}
|
||||
onChangeText={setWeight}
|
||||
onSubmitEditing={save}
|
||||
ref={weightRef}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
label="Unit (kg)"
|
||||
value={unit}
|
||||
onChangeText={setUnit}
|
||||
ref={unitRef}
|
||||
onSubmitEditing={save}
|
||||
/>
|
||||
<Text style={styles.text}>{format(created, 'PPPP p')}</Text>
|
||||
</Dialog.Content>
|
||||
<Dialog.Actions>
|
||||
<Button icon="close" onPress={() => setShow(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
{id && (
|
||||
<Button icon="copy" onPress={clearId}>
|
||||
Duplicate
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
</Modal>
|
||||
<Button mode="contained" icon="save" onPress={save}>
|
||||
Save
|
||||
</Button>
|
||||
</Dialog.Actions>
|
||||
</Dialog>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
backgroundColor: 'black',
|
||||
padding: 20,
|
||||
},
|
||||
text: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
bottom: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
marginBottom: 10,
|
||||
|
|
16
Home.tsx
16
Home.tsx
|
@ -86,15 +86,13 @@ export default function Home() {
|
|||
refreshing={refreshing}
|
||||
onRefresh={refresh}
|
||||
/>
|
||||
<View style={styles.bottom}>
|
||||
<EditSet
|
||||
clearId={() => setId(undefined)}
|
||||
id={id}
|
||||
show={showEdit}
|
||||
setShow={setShowEdit}
|
||||
onSave={save}
|
||||
/>
|
||||
</View>
|
||||
<EditSet
|
||||
clearId={() => setId(undefined)}
|
||||
id={id}
|
||||
show={showEdit}
|
||||
setShow={setShowEdit}
|
||||
onSave={save}
|
||||
/>
|
||||
|
||||
<AnimatedFAB
|
||||
extended={false}
|
||||
|
|
16
Settings.tsx
16
Settings.tsx
|
@ -5,6 +5,7 @@ import {NativeModules, StyleSheet, Text, View} from 'react-native';
|
|||
import {Button, Snackbar, Switch, TextInput} from 'react-native-paper';
|
||||
import {RootStackParamList} from './App';
|
||||
import {getDb} from './db';
|
||||
import BatteryDialog from './BatteryDialog';
|
||||
|
||||
export default function Settings({
|
||||
navigation,
|
||||
|
@ -13,6 +14,7 @@ export default function Settings({
|
|||
const [seconds, setSeconds] = useState<string>('');
|
||||
const [alarmEnabled, setAlarmEnabled] = useState<boolean>(true);
|
||||
const [snackbar, setSnackbar] = useState('');
|
||||
const [showBattery, setShowBattery] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
|
@ -43,6 +45,16 @@ export default function Settings({
|
|||
NativeModules.ImportModule.sets();
|
||||
};
|
||||
|
||||
const changeAlarmEnabled = (enabled: boolean) => {
|
||||
if (!enabled) return setAlarmEnabled(enabled);
|
||||
NativeModules.AlarmModule.ignoringBatteryOptimizations(
|
||||
(ignoring: boolean) => {
|
||||
if (ignoring) return setAlarmEnabled(true);
|
||||
setShowBattery(true);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TextInput
|
||||
|
@ -65,7 +77,7 @@ export default function Settings({
|
|||
<Switch
|
||||
style={[styles.text, {alignSelf: 'flex-start'}]}
|
||||
value={alarmEnabled}
|
||||
onValueChange={setAlarmEnabled}
|
||||
onValueChange={changeAlarmEnabled}
|
||||
/>
|
||||
<Button
|
||||
style={{alignSelf: 'flex-start'}}
|
||||
|
@ -86,6 +98,8 @@ export default function Settings({
|
|||
Delete all data
|
||||
</Button>
|
||||
|
||||
<BatteryDialog show={showBattery} setShow={setShowBattery} />
|
||||
|
||||
<Snackbar
|
||||
visible={!!snackbar}
|
||||
onDismiss={() => setSnackbar('')}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package com.massive
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.facebook.react.bridge.Callback
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||
import com.facebook.react.bridge.ReactMethod
|
||||
|
||||
|
||||
class AlarmModule internal constructor(context: ReactApplicationContext?) :
|
||||
ReactContextBaseJavaModule(context) {
|
||||
|
||||
|
@ -23,4 +29,26 @@ class AlarmModule internal constructor(context: ReactApplicationContext?) :
|
|||
intent.putExtra("milliseconds", milliseconds)
|
||||
reactApplicationContext.startService(intent)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
@ReactMethod
|
||||
fun ignoringBatteryOptimizations(callback: Callback) {
|
||||
val packageName = reactApplicationContext.packageName
|
||||
val pm =
|
||||
reactApplicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
callback.invoke(pm.isIgnoringBatteryOptimizations(packageName))
|
||||
} else {
|
||||
callback.invoke(true)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
@ReactMethod
|
||||
fun openBatteryOptimizations() {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + reactApplicationContext.packageName)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
reactApplicationContext.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
|
17
index.js
17
index.js
|
@ -1,20 +1,7 @@
|
|||
import {AppRegistry} from 'react-native';
|
||||
import 'react-native-gesture-handler';
|
||||
import 'react-native-sqlite-storage';
|
||||
import React from 'react';
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './App';
|
||||
import {name as appName} from './app.json';
|
||||
import {Provider as PaperProvider, DarkTheme} from 'react-native-paper';
|
||||
import Ionicon from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
export default function Main() {
|
||||
return (
|
||||
<PaperProvider
|
||||
theme={DarkTheme}
|
||||
settings={{icon: props => <Ionicon {...props} />}}>
|
||||
<App />
|
||||
</PaperProvider>
|
||||
);
|
||||
}
|
||||
|
||||
AppRegistry.registerComponent(appName, () => Main);
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
|
|
Loading…
Reference in New Issue
Block a user