New views

This commit is contained in:
Martin Bauer 2020-07-25 14:06:39 +02:00
parent aee89d799c
commit 4544f19938
12 changed files with 647 additions and 6 deletions

11
App.js
View File

@ -10,11 +10,9 @@ import { DeviceReduxCoupling } from './state/DeviceReduxCoupling';
import { Provider } from 'react-redux';
import ThemedStackNavigation from './components/ThemedStackNavigation';
import NewAppMain from "./components/NewAppMain";
const store = createStore(swimtrackerReducer);
//const deviceReduxCoupling = new DeviceReduxCoupling(store);
export default class App extends React.Component {
constructor(props) {
@ -38,11 +36,16 @@ export default class App extends React.Component {
if (!this.state.isReady) {
return <AppLoading />;
}
/*
return (
<Provider store={store}>
<ThemedStackNavigation />
</Provider>
);*/
return (
<Provider store={store}>
<NewAppMain />
</Provider>
);
}
}

BIN
assets/infinity_pool.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

BIN
assets/infinity_pool2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

41
components/NewAppMain.js Normal file
View File

@ -0,0 +1,41 @@
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
// Own views
import MainMenuView from "../views/MainMenuView";
import SettingsView from "../views/SettingsView";
import TrainingView from "../views/TrainingView";
const Stack = createStackNavigator();
function NewAppMain(props) {
const screenOptions = {
headerShown: false,
}
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={MainMenuView}
options={screenOptions}
/>
<Stack.Screen
name="Settings"
component={SettingsView}
options={screenOptions}
/>
<Stack.Screen
name="Training"
component={TrainingView}
options={screenOptions}
/>
</Stack.Navigator>
</NavigationContainer>
)
};
export default NewAppMain;

View File

@ -33,7 +33,6 @@ export default class DataAnalysis {
this.activeMeasurements += newAverages.reduce((n, val) => {
return n + ((val >= analysisParameters.activeTimeThreshold) ? 1 : 0);
}, 0);
console.log("data", newDataArr, "newAverages", newAverages, "reduction", this.activeMeasurements);
// peaks
const newPeaks = this.peakDetectorSimple.addVector(newDataArr);
@ -64,7 +63,6 @@ export default class DataAnalysis {
};
}
_resetCache(analysisParameters, sessionId) {
this.movingAverage = analysisParameters ? new MovingAverage(analysisParameters.movingAverageWindowSize) : null;
this.activeMeasurements = 0;

View File

@ -27,6 +27,7 @@ export const stopSession = () => ({
const INITIAL_SETTINGS = {
theme: "hot",
username: "",
//swimTrackerHost: "192.168.178.107",
swimTrackerHost: "192.168.178.110",
analysis: {

0
views/LastSessionView.js Normal file
View File

233
views/MainMenuView.js Normal file
View File

@ -0,0 +1,233 @@
import React from "react";
import {
StyleSheet,
View,
StatusBar,
ImageBackground,
Text,
TouchableOpacity,
} from "react-native";
import themeColors from './themeColors';
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons";
import EntypoIcon from "react-native-vector-icons/Entypo";
import { ConnState, startSession } from '../state/DeviceReduxCoupling';
import { connect } from 'react-redux';
// ---------------------------------------------------------------------------------------------
function LargeHeaderView(props) {
return (
<View style={largeHeaderStyles.container}>
<Text style={largeHeaderStyles.titleText}>swimtracker</Text>
<View style={largeHeaderStyles.separator}></View>
<Text style={largeHeaderStyles.subText}>bauer.tech</Text>
</View>
);
}
const largeHeaderStyles = StyleSheet.create({
container: {
height: 160,
maxHeight: 160,
flex: 1,
alignItems: "center",
justifyContent: "flex-end",
paddingBottom: 10,
//alignContent: "space-between"
},
titleText: {
color: "rgba(255,255,255,1)",
fontSize: 48,
},
subText: {
color: "rgba(255,255,255, 0.8)",
marginTop: 15,
textAlign: "center",
},
separator: {
height: 7,
backgroundColor: "rgba(255,255,255,1)",
opacity: 0.5,
width: 230
}
});
// ---------------------------------------------------------------------------------------------
function ButtonGrid(props) {
return (
<View style={buttonGridStyles.rowContainer}>
<View style={buttonGridStyles.columnContainer}>
<TouchableOpacity
onPress={props.onLastSessionsPress}
style={[{ backgroundColor: themeColors["GREEN SEA"] }, buttonGridStyles.button]}
activeOpacity={0.6}
>
<MaterialIcon name="swim" style={buttonGridStyles.icon}></MaterialIcon>
<Text style={buttonGridStyles.buttonText}>{"LETZTE\nSESSIONS"}</Text>
</TouchableOpacity>
</View>
<View style={buttonGridStyles.columnContainer}>
<TouchableOpacity
onPress={props.onSocialPress}
style={[{ backgroundColor: themeColors["MIDNIGHT BLUE"] }, buttonGridStyles.button]}
activeOpacity={0.6}
>
<MaterialIcon name="account-group" style={buttonGridStyles.icon}></MaterialIcon>
<Text style={buttonGridStyles.buttonText}>SOCIAL</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={props.onSettingsPress}
style={[{ backgroundColor: themeColors["MIDNIGHT BLUE"] }, buttonGridStyles.button]}
activeOpacity={0.6}
>
<MaterialIcon name="settings-outline" style={buttonGridStyles.icon}></MaterialIcon>
<Text style={buttonGridStyles.buttonText}>EINSTELLUNGEN</Text>
</TouchableOpacity>
</View>
</View>
)
}
const buttonGridStyles = StyleSheet.create({
rowContainer: {
flex: 1,
flexDirection: "column",
justifyContent: "space-around",
alignItems: "center",
paddingTop: 20,
paddingBottom: 50,
},
columnContainer: {
flex: 1,
width: "100%",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center",
},
button: {
flex: 1,
margin: 20,
padding: 20,
width: 120,
height: 130,
borderRadius: 10,
opacity: 0.95,
justifyContent: "space-around",
alignItems: "center",
},
buttonText: {
color: "rgba(255,255,255,1)",
textAlign: "center",
fontSize: 16,
},
icon: {
color: "rgba(255,255,255,1)",
fontSize: 60,
}
});
// ---------------------------------------------------------------------------------------------
function FullWidthButton(props) {
let textStyle = [fullWidthButtonStyles.buttonText];
let iconStyle = [fullWidthButtonStyles.icon];
if (props.disabled) {
textStyle.push(fullWidthButtonStyles.buttonTextDisabled);
iconStyle.push(fullWidthButtonStyles.iconDisabled);
}
return (
<TouchableOpacity
onPress={props.onPress}
style={fullWidthButtonStyles.container}
disabled={props.disabled}
activeOpacity={0.6}>
<View style={{ flex: 1, flexDirection: "row", justifyContent: "center" }}>
<EntypoIcon name="air" style={iconStyle}></EntypoIcon>
<Text style={textStyle}>{props.text}</Text>
</View>
</TouchableOpacity>
)
}
const fullWidthButtonStyles = StyleSheet.create({
container: {
flex: 1,
maxHeight: 70,
backgroundColor: themeColors["WET ASPHALT"],
flexDirection: "row",
alignItems: "center",
//paddingBottom: 10,
justifyContent: "center",
},
buttonText: {
padding: 10,
color: "rgba(255,255,255,1)",
fontSize: 25,
fontWeight: "600",
textAlign: "center",
},
buttonTextDisabled: {
opacity: 0.3,
},
icon: {
fontSize: 40,
padding: 10,
color: themeColors["GREEN SEA"],
},
iconDisabled: {
color: themeColors["POMEGRANATE"],
},
});
// ---------------------------------------------------------------------------------------------
function MainMenuView(props) {
const s = props.connState;
let startButtonText = "JETZT SCHWIMMEN";
let startButtonDisabled = false;
if (s === ConnState.DISCONNECTED) {
startButtonText = "NICHT VERBUNDEN";
startButtonDisabled = true;
} else if (s === ConnState.CONNECTED_RUNNING || s === ConnState.CONNECTED_STARTING) {
startButtonText = "TRAINING LÄUFT";
} else if (s === ConnState.CONNECTED_STOPPING) {
startButtonDisabled = true;
}
const onStartButtonPress = () => {
if (!props.connState !== ConnState.CONNECTED_RUNNING) {
props.dispatch(startSession());
}
props.navigation.navigate('Training')
};
return (
<View style={{ flex: 1 }}>
<StatusBar barStyle="light-content" backgroundColor="rgba(0,0,0,0.4)" translucent={true} />
<ImageBackground
source={require("../assets/pool_sky_background_blurred.jpg")}
resizeMode="cover"
style={{ flex: 1 }}
>
<View style={{ flex: 1 }}>
<LargeHeaderView />
<ButtonGrid onSettingsPress={() => props.navigation.navigate('Settings')} />
<FullWidthButton
onPress={onStartButtonPress}
text={startButtonText}
disabled={startButtonDisabled} />
</View>
</ImageBackground>
</View>
);
}
const mapStateToProps = (state) => {
return { connState: state.deviceState.connState };
};
export default connect(mapStateToProps)(MainMenuView);

207
views/SettingsView.js Normal file
View File

@ -0,0 +1,207 @@
import React, { useState } from "react";
import {
StyleSheet,
View,
StatusBar,
TextInput,
ImageBackground,
Text,
TouchableOpacity,
SafeAreaView,
Slider,
Switch,
} from "react-native";
import themeColors from './themeColors';
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons";
import EntypoIcon from "react-native-vector-icons/Entypo";
function ImageHeader(props) {
return (
<View style={imageHeaderStyles.container}>
<ImageBackground
source={require("../assets/infinity_pool2.jpg")}
resizeMode="cover"
style={{ flex: 1 }}
>
<View style={imageHeaderStyles.row}>
<TouchableOpacity onPress={() => props.navigation.goBack()}>
<EntypoIcon name="chevron-left" style={imageHeaderStyles.icon}></EntypoIcon>
</TouchableOpacity>
<Text style={imageHeaderStyles.text}>{props.text}</Text>
</View>
</ImageBackground>
</View >
)
}
const imageHeaderStyles = StyleSheet.create({
container: {
flex: 1,
minHeight: 175,
maxHeight: 175,
height: 175,
width: "100%",
},
row: {
paddingTop: 30,
flexDirection: "row",
},
icon: {
color: "white",
fontSize: 40,
paddingRight: 10,
paddingLeft: 10,
},
text: {
color: "white",
fontSize: 30,
},
});
// ---------------------------------------------------------------------------------------------
function SettingsTextInput(props) {
return (
<React.Fragment>
<Text style={settingsGroupStyles.label}>{props.label}</Text>
<TextInput
style={settingsGroupStyles.textInput}
placeholder={props.placeholder}
placeholderTextColor="rgba(167,167,167,1)"
selectionColor='rgb(120,120,120)'
></TextInput>
</React.Fragment>
);
}
function SettingsSwitch(props) {
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => setIsEnabled(previousState => !previousState);
return (
<React.Fragment>
<Text style={settingsGroupStyles.label}>{props.label}</Text>
<Switch
value={isEnabled}
//thumbColor={themeColors["WET ASPHALT"]}
onValueChange={toggleSwitch}
trackColor={{ false: "#767577", true: themeColors["NEPHRITIS"] }}
//thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
ios_backgroundColor="grey"
style={settingsGroupStyles.switch}
/>
</React.Fragment>
)
}
function SettingsSlider(props) {
return (
<React.Fragment>
<Text style={settingsGroupStyles.label}>{props.label}</Text>
<Slider
value={props.value}
disabled={props.disabled}
thumbTintColor={themeColors["WET ASPHALT"]}
minimumTrackTintColor={themeColors["CLOUDS"]}
maximumTrackTintColor={themeColors["CLOUDS"]}
style={settingsGroupStyles.slider}
/>
</React.Fragment>
)
}
function SettingsCombo(props) {
}
function SettingsGroup(props) {
return (
<View style={settingsGroupStyles.container}>
<Text style={settingsGroupStyles.title}>{props.title}</Text>
<View style={settingsGroupStyles.subsettings}>
{React.Children.map(props.children, (child, idx) =>
<View style={idx == 0 ? [settingsGroupStyles.row, settingsGroupStyles.firstRow] : settingsGroupStyles.row}>
{child}
</View>
)}
</View>
</View>
);
};
const settingsGroupStyles = StyleSheet.create({
container: {
padding: 20,
paddingRight: 30,
},
title: {
color: "white",
fontSize: 20,
fontWeight: "600",
},
subsettings: {
padding: 10,
paddingLeft: 30,
},
row: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
borderTopColor: "rgba(255, 255,255, 0.6)",
paddingTop: 5,
paddingBottom: 5,
borderTopWidth: 0.5,
minHeight: 40,
},
firstRow: {
paddingTop: 0,
borderTopWidth: 0,
},
label: {
color: "white",
fontSize: 17,
},
textInput: {
color: "rgba(255,255,255,1)",
marginTop: 8,
},
slider: {
minWidth: 100,
width: 150,
//minHeight: 50,
},
switch: {
//minHeight: 50,
//minWidth: 80,
}
});
// ---------------------------------------------------------------------------------------------
function SettingsView(props) {
return (
<View style={{ flex: 1 }}>
<StatusBar hidden={true} />
<View style={{ flex: 1 }}>
<ImageHeader text="EINSTELLUNGEN" navigation={props.navigation} />
<View style={{ flex: 1, backgroundColor: themeColors["BELIZE HOLE"] }}>
<SettingsGroup title="swimtracker Device">
<SettingsTextInput
label="URL/IP"
placeholder="192.168.178.??"
/>
<SettingsSwitch
label="SomeSwitch"
/>
</SettingsGroup>
</View>
</View>
</View>
)
}
export default SettingsView;

132
views/TrainingView.js Normal file
View File

@ -0,0 +1,132 @@
import React, { Component } from "react";
import {
StyleSheet,
View,
StatusBar,
Text,
TouchableOpacity,
SafeAreaView
} from "react-native";
import themeColors from './themeColors';
import EntypoIcon from "react-native-vector-icons/Entypo";
import { connect } from 'react-redux';
import { useKeepAwake } from 'expo-keep-awake';
import { stopSession } from '../state/DeviceReduxCoupling';
import CycleView from '../components/CycleView';
import IconCard from '../components/IconCard';
import Graph from '../components/Graph';
function SmallHeaderView(props) {
return (
<View style={smallHeaderStyles.container}>
<View style={smallHeaderStyles.row}>
<TouchableOpacity onPress={() => props.navigation.goBack()}>
<EntypoIcon name="chevron-left" style={smallHeaderStyles.backIcon}></EntypoIcon>
</TouchableOpacity>
<Text style={smallHeaderStyles.text}>{props.text}</Text>
<TouchableOpacity onPress={props.onStopPressed}>
<EntypoIcon name="controller-stop" style={smallHeaderStyles.stopIcon}></EntypoIcon>
</TouchableOpacity>
</View>
</View >
)
}
const smallHeaderStyles = StyleSheet.create({
container: {
flex: 1,
minHeight: 80,
maxHeight: 80,
height: 80,
width: "100%",
backgroundColor: themeColors["WET ASPHALT"],
},
row: {
paddingTop: 30,
flexDirection: "row",
justifyContent: "space-between",
},
backIcon: {
color: "white",
fontSize: 40,
},
stopIcon: {
color: themeColors["ALIZARIN"],
fontSize: 40,
paddingRight: 10,
paddingLeft: 10,
},
text: {
color: "white",
fontSize: 30,
},
});
const toTimeStr = seconds => {
let minuteStr = String(Math.floor(seconds / 60));
if (minuteStr.length < 2)
minuteStr = "0" + minuteStr;
let secondStr = String(Math.floor(seconds % 60));
if (secondStr.length < 2)
secondStr = "0" + secondStr;
return minuteStr + ":" + secondStr;
}
// ---------------------------------------------------------------------------------------------
function TrainingView(props) {
useKeepAwake();
const analysis = props.session.analysis;
const laps = (analysis.peaks.size / props.peaksPerLap).toFixed(1);
const totalMomentum = Math.trunc(analysis.totalMomentum * props.kgFactor / 10 / 60);
const onStopPressed = () => {
props.dispatch(stopSession());
props.navigation.navigate('Home');
};
return (
<View style={{ flex: 1 }}>
<StatusBar hidden={true} />
<View style={{ flex: 1 }}>
<SmallHeaderView text="TRAINING" navigation={props.navigation} onStopPressed={onStopPressed} />
<View style={trainingViewStyles.container}>
<CycleView>
<IconCard label="BAHNEN" value={laps} iconName="retweet" iconType="AntDesign" />
<IconCard label="ZÜGE" value={analysis.peaks.size} iconName="dashboard" iconType="AntDesign" />
</CycleView>
<CycleView>
<IconCard label="DAUER" value={toTimeStr(analysis.totalTime)} iconName="clock" iconType="FontAwesome5" />
<IconCard label="AKTIVE DAUER" value={toTimeStr(analysis.activeTime)} iconName="stopwatch" iconType="FontAwesome5" />
</CycleView>
<IconCard label="KRAFT" value={totalMomentum} iconName="ruler" iconType="Entypo" />
<Graph></Graph>
</View>
</View>
</View>
)
}
const trainingViewStyles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: themeColors["BELIZE HOLE"],
padding: 20,
justifyContent: "space-around",
}
});
const mapStateToProps = (state) => {
return {
session: state.deviceState,
peaksPerLap: state.settings.analysis.peaksPerLap,
theme: state.settings.theme,
kgFactor: state.settings.analysis.kgFactor,
};
};
export default connect(mapStateToProps)(TrainingView);

26
views/themeColors.js Normal file
View File

@ -0,0 +1,26 @@
// https://flatuicolors.com/palette/defo
const themeColors = {
'TURQUOISE': "rgb(26, 188, 156)",
"EMERALD": "rgb(46, 204, 113)",
"PETER RIVER" : "rgb(52, 152, 219)",
"AMETHYST" : "rgb(155, 89, 182)",
"WET ASPHALT" : "rgb(52, 73, 94)",
"GREEN SEA" : "rgb(22, 160, 133)",
"NEPHRITIS" : "rgb(39, 174, 96)",
"BELIZE HOLE" : "rgb(41, 128, 185)",
"WISTERIA" : "rgb(142, 68, 173)",
"MIDNIGHT BLUE" : "rgb(44, 62, 80)",
"SUN FLOWER" : "rgb(241, 196, 15)",
"CARROT" : "rgb(230, 126, 34)",
"ALIZARIN" : "rgb(231, 76, 60)",
"CLOUDS" : "rgb(236, 240, 241)",
"CONCRETE" : "rgb(149, 165, 166)",
"ORANGE" : "rgb(243, 156, 18)",
"PUMPKIN" : "rgb(211, 84, 0)",
"POMEGRANATE" : "rgb(192, 57, 43)",
"SILVER" : "rgb(189, 195, 199)",
"ASBESTOS" : "rgb(127, 140, 141)",
};
export default themeColors;