main
alex 2023-07-16 09:46:49 +00:00
parent 784133fb60
commit 3a8c8369ba
14 changed files with 14545 additions and 79 deletions

14182
2package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -21,10 +21,12 @@
"settingsTitle": "Einstellungen", "settingsTitle": "Einstellungen",
"wifiStandByTitle": "WLAN im Standby", "wifiStandByTitle": "WLAN im Standby",
"wifiStandByDescription": "Die WLAN-Verbindung bleibt bestehen, auch wenn das Gerät ausgeschaltet ist. Bitte beachten Sie, dass dies zu einem erhöhten Stromverbrauch führen kann.", "wifiStandByDescription": "Die WLAN-Verbindung bleibt bestehen, auch wenn das Gerät ausgeschaltet ist. Bitte beachten Sie, dass dies zu einem erhöhten Stromverbrauch führen kann.",
"deviceInformationTitle": "Geräteinformationen", "deviceInformationText": "Geräteinformationen",
"deviceModelTitle": "Gerätemodell", "deviceNameText": "Gerätename",
"deviceFirmwareVersionTitle": "Firmware Version", "deviceModelText": "Gerätemodell",
"deviceLastUpdatedTitle": "Letzte Aktualisierung" "deviceFirmwareVersionText": "Firmware Version",
"deviceLastUpdatedText": "Letzte Aktualisierung",
"modalTextInputDeviceNameDescription": "Der Name des Geräts wird lokal auf dem Gerät gespeichert und dient Ihnen dazu, die verbundenen Geräte in der App voneinander zu unterscheiden."
} }
}, },
"settings": { "settings": {

View File

@ -21,10 +21,12 @@
"settingsTitle": "Settings", "settingsTitle": "Settings",
"wifiStandByTitle": "WLAN in standby", "wifiStandByTitle": "WLAN in standby",
"wifiStandByDescription": "The WLAN connection remains established even if the device is switched off. Please note that this can lead to increased power consumption.", "wifiStandByDescription": "The WLAN connection remains established even if the device is switched off. Please note that this can lead to increased power consumption.",
"deviceInformationTitle": "Device information", "deviceInformationText": "Device information",
"deviceModelTitle": "Device model", "deviceNameText": "Device name",
"deviceFirmwareVersionTitle": "Firmware Version", "deviceModelText": "Device model",
"deviceLastUpdatedTitle": "Last updated" "deviceFirmwareVersionText": "Firmware Version",
"deviceLastUpdatedText": "Last updated",
"modalTextInputDeviceNameDescription": "The name of the device is stored locally on the device and serves you to distinguish the connected devices from each other in the app."
} }
}, },
"settings": { "settings": {

22
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@react-navigation/native": "^6.1.7", "@react-navigation/native": "^6.1.7",
"@react-navigation/native-stack": "^6.9.13", "@react-navigation/native-stack": "^6.9.13",
"expo": "~48.0.18", "expo": "~48.0.18",
"expo-haptics": "~12.2.1",
"expo-status-bar": "~1.4.4", "expo-status-bar": "~1.4.4",
"i18next": "^23.2.11", "i18next": "^23.2.11",
"i18next-browser-languagedetector": "^7.1.0", "i18next-browser-languagedetector": "^7.1.0",
@ -7135,6 +7136,14 @@
"expo": "*" "expo": "*"
} }
}, },
"node_modules/expo-haptics": {
"version": "12.2.1",
"resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-12.2.1.tgz",
"integrity": "sha512-XRZtmIQi901Q4+/cZnVrULRFOqShsgCuSP0SCbVEhnq8sK0OA4jgun12O93Pu5aGvTyoqsAcIArE8tX+8AEqRA==",
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-keep-awake": { "node_modules/expo-keep-awake": {
"version": "12.0.1", "version": "12.0.1",
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-12.0.1.tgz", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-12.0.1.tgz",
@ -7632,19 +7641,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
}, },
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",

View File

@ -27,7 +27,8 @@
"react-native-safe-area-context": "4.5.0", "react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0", "react-native-screens": "~3.20.0",
"react-native-tab-view": "^3.5.2", "react-native-tab-view": "^3.5.2",
"reanimated-color-picker": "^2.3.1" "reanimated-color-picker": "^2.3.1",
"expo-haptics": "~12.2.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0" "@babel/core": "^7.20.0"

View File

@ -1,6 +1,6 @@
import { useContext } from "react"; import { useContext } from "react";
import { View } from "react-native"; import { View } from "react-native";
import { AppContext } from "../../utils"; import { AppContext, AppStyles } from "../../utils";
export default function Card({ children }) { export default function Card({ children }) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
@ -9,18 +9,20 @@ export default function Card({ children }) {
<View <View
style={{ style={{
paddingTop: 10, paddingTop: 10,
paddingLeft: 20, paddingLeft: 10,
paddingRight: 20, paddingRight: 10,
paddingBottom: 10, paddingBottom: 10,
}} }}
> >
<View <View
style={{ style={[
borderRadius: 20, {
backgroundColor: appContext.appTheme.card.backgroundColor, borderRadius: 20,
elevation: 5, backgroundColor: appContext.appTheme.card.backgroundColor,
padding: 20, padding: 20,
}} },
AppStyles.Shadow,
]}
> >
{children} {children}
</View> </View>

View File

@ -178,3 +178,85 @@ export default function PickerModal({
</Modal> </Modal>
); );
} }
export function TextInputModal({
isOpen,
setIsOpen,
onTextInputSave,
modalTitle,
inputTitle,
inputDescription,
}) {
const appContext = useContext(AppContext);
const closeModal = () => setIsOpen(false);
return (
<Modal
visible={isOpen}
animationType="slide"
onRequestClose={() => closeModal()}
>
<View
style={{
flex: 1,
backgroundColor: appContext.appTheme.backgroundColor,
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginTop: 20,
}}
>
<TouchableOpacity onPress={() => closeModal()}>
<MyIcon
name="chevron-left"
size={24}
style={{ marginLeft: 20 }}
color={appContext.appTheme.icon}
/>
</TouchableOpacity>
<Text style={AppStyles.typography20}>{modalTitle}</Text>
<TouchableOpacity
onPress={() => {
closeModal();
onTextInputSave();
}}
>
<MyIcon
name="check"
size={24}
style={{ marginRight: 20 }}
color={appContext.appTheme.colors.primary}
/>
</TouchableOpacity>
</View>
<View
style={{
flex: 1,
padding: 20,
}}
>
<Text style={{ color: "gray" }}>{inputTitle}</Text>
<TextInput
style={{
height: 40,
borderBottomWidth: 1,
borderColor: appContext.appTheme.textInputBottomColor,
}}
value="Turtle"
/>
<Text
style={[AppStyles.typography14, { color: "gray", marginTop: 6 }]}
>
{inputDescription}
</Text>
</View>
</View>
</Modal>
);
}

View File

@ -1,6 +1,7 @@
import { useContext } from "react"; import { useContext } from "react";
import { Switch } from "react-native"; import { Switch } from "react-native";
import { AppContext } from "../../utils"; import { AppContext } from "../../utils";
import * as Haptics from "expo-haptics";
export default function MySwitch({ style, value, onValueChange }) { export default function MySwitch({ style, value, onValueChange }) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);

View File

@ -0,0 +1,86 @@
import { Dimensions, Text, View } from "react-native";
import Card from "../../Components/Card";
import MyIcon from "../../Components/Icon";
import { useContext } from "react";
import { AppContext } from "../../utils";
import { TouchableOpacity } from "react-native-gesture-handler";
const windowDimensions = Dimensions.get("window");
const screenDimensions = Dimensions.get("screen");
export default function ColorScenesView() {
const appContext = useContext(AppContext);
console.log("wD", windowDimensions, screenDimensions, screenDimensions.width);
const Leds = ({ style }) => {
let elements = [];
for (let i = 0; i < 16; i++) {
elements.push(
<View
key={i}
style={{
width: windowDimensions.width / 16 - 5.7,
height: windowDimensions.width / 16 - 5.7,
backgroundColor: "lime",
borderRadius: 4,
}}
/>
);
}
return (
<View
style={[
{ flexDirection: "row", gap: 2, justifyContent: "center" },
style,
]}
>
{elements}
</View>
);
};
return (
<Card>
<Text style={{ textAlign: "center" }}>Rückseite</Text>
<Leds />
<Leds style={{ marginTop: 2 }} />
<Leds style={{ marginTop: 2 }} />
<Leds style={{ marginTop: 2 }} />
<Text style={{ textAlign: "center" }}>Vorderseite</Text>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity onPress={() => console.log("up")}>
<MyIcon
name="arrow-up-bold-box"
size={36}
color={appContext.appTheme.icon}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => console.log("down")}>
<MyIcon
name="arrow-down-bold-box"
size={36}
color={appContext.appTheme.icon}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => console.log("left")}>
<MyIcon
name="arrow-left-bold-box"
size={36}
color={appContext.appTheme.icon}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => console.log("right")}>
<MyIcon
name="arrow-right-bold-box"
size={36}
color={appContext.appTheme.icon}
/>
</TouchableOpacity>
</View>
</Card>
);
}

View File

@ -16,6 +16,7 @@ import MyIcon from "../../Components/Icon";
const MotorView = lazy(() => import("./motor")); const MotorView = lazy(() => import("./motor"));
const SettingsView = lazy(() => import("./settings")); const SettingsView = lazy(() => import("./settings"));
const ColorScenesView = lazy(() => import("./colorScenes"));
const spaceToSide = 10; // left and right const spaceToSide = 10; // left and right
const top = 35; const top = 35;
@ -37,6 +38,8 @@ export default function DeviceScreen() {
return <MotorView />; return <MotorView />;
case 3: case 3:
return <SettingsView />; return <SettingsView />;
case 4:
return <ColorScenesView />;
default: default:
<Text>Not found</Text>; <Text>Not found</Text>;
} }

View File

@ -15,8 +15,13 @@ import ColorPicker, {
InputWidget, InputWidget,
HueCircular, HueCircular,
Panel1, Panel1,
Panel3,
BrightnessSlider,
OpacitySlider,
HueSlider,
SaturationSlider,
} from "reanimated-color-picker"; } from "reanimated-color-picker";
import { AppContext, AppStyles } from "../../utils"; import { AppContext, AppStyles, IsPlatformIos } from "../../utils";
import Card from "../../Components/Card"; import Card from "../../Components/Card";
import MySwitch from "../../Components/Switch"; import MySwitch from "../../Components/Switch";
import MyIcon from "../../Components/Icon"; import MyIcon from "../../Components/Icon";
@ -37,7 +42,7 @@ function ColorLayer({ layerNumber, sharedColor, selected, onPress }) {
}; };
}); });
const contrastColor = useAnimatedStyle(() => { const contrastColorText = useAnimatedStyle(() => {
const getContrastRatio = (hexColor) => { const getContrastRatio = (hexColor) => {
const hex = hexColor.replace("#", ""); const hex = hexColor.replace("#", "");
const r = parseInt(hex.substr(0, 2), 16); const r = parseInt(hex.substr(0, 2), 16);
@ -49,11 +54,28 @@ function ColorLayer({ layerNumber, sharedColor, selected, onPress }) {
let obj = {}; let obj = {};
obj["color"] = getContrastRatio(sharedColor.value); obj.color = getContrastRatio(sharedColor.value);
return obj;
});
const contrastColorBorder = useAnimatedStyle(() => {
const getContrastRatio = (hexColor) => {
const hex = hexColor.replace("#", "");
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
const luminance = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
return luminance > 0.5 ? "#000000" : "#ffffff";
};
let obj = {};
//obj.color = getContrastRatio(sharedColor.value);
if (selected === true) { if (selected === true) {
obj.borderColor = obj.color; obj.borderColor = getContrastRatio(sharedColor.value);
obj.borderBottomWidth = 4; obj.borderBottomWidth = 2;
} else { } else {
obj.borderBottomWidth = 0; obj.borderBottomWidth = 0;
} }
@ -87,11 +109,19 @@ function ColorLayer({ layerNumber, sharedColor, selected, onPress }) {
backgroundColorStyle, backgroundColorStyle,
]} ]}
> >
<Animated.Text <Animated.View style={contrastColorBorder}>
style={[{ fontSize: 16, fontWeight: "bold" }, contrastColor]} <Animated.Text
> style={[
{layerNumber} {
</Animated.Text> fontSize: 16,
fontWeight: "bold",
},
contrastColorText,
]}
>
{layerNumber}
</Animated.Text>
</Animated.View>
</Animated.View> </Animated.View>
</TouchableHighlight> </TouchableHighlight>
); );
@ -141,11 +171,14 @@ export default function LightView() {
<Card> <Card>
<TouchableOpacity onPress={() => setModalDeviceColorModeVisible(true)}> <TouchableOpacity onPress={() => setModalDeviceColorModeVisible(true)}>
<View <View
style={{ style={[
flexDirection: "row", {
alignItems: "center", flexDirection: "row",
justifyContent: "space-between", alignItems: "center",
}} justifyContent: "space-between",
},
IsPlatformIos() && { marginBottom: 10 },
]}
> >
<View> <View>
<Text style={{ color: appContext.appTheme.text }}> <Text style={{ color: appContext.appTheme.text }}>
@ -180,7 +213,12 @@ export default function LightView() {
})} })}
/> />
<View style={{ flexDirection: "row", alignItems: "center" }}> <View
style={[
{ flexDirection: "row", alignItems: "center" },
IsPlatformIos() && { gap: 10 },
]}
>
<MySwitch <MySwitch
value={switchState} value={switchState}
onValueChange={(e) => setSwitchState(e)} onValueChange={(e) => setSwitchState(e)}
@ -210,8 +248,7 @@ export default function LightView() {
<Animated.View> <Animated.View>
<ColorPicker <ColorPicker
value={selectedColorPickerColor.value} value={selectedColorPickerColor.value}
sliderThickness={25} thumbSize={26}
thumbSize={27}
onChange={onColorSelect} onChange={onColorSelect}
> >
<View <View
@ -280,16 +317,14 @@ export default function LightView() {
/> />
</TouchableOpacity> </TouchableOpacity>
<View style={{ marginLeft: 30, marginRight: 30 }}> <View
<HueCircular style={{ flexDirection: "row", justifyContent: "space-evenly" }}
containerStyle={{ >
justifyContent: "center", <Panel3 style={{ width: 200 }} />
backgroundColor: appContext.appTheme.backgroundColor,
}} <BrightnessSlider style={styles.sliderStyle} vertical reverse />
thumbShape="pill"
> <OpacitySlider style={styles.sliderStyle} vertical reverse />
<Panel1 style={styles.panelStyle} />
</HueCircular>
</View> </View>
<Swatches <Swatches
@ -317,17 +352,25 @@ export default function LightView() {
</Card> </Card>
<Card> <Card>
<View style={{ flexDirection: "row", alignItems: "center" }}> <View
<MyIcon style={{
name="brightness-6" flexDirection: "row",
size={24} alignItems: "center",
color={appContext.appTheme.icon} justifyContent: "space-between",
/> }}
<Text style={{ color: appContext.appTheme.text, marginLeft: 10 }}> >
Auto brightness <View style={{ flexDirection: "row", alignItems: "center" }}>
</Text> <MyIcon
name="brightness-6"
size={24}
color={appContext.appTheme.icon}
/>
<Text style={{ color: appContext.appTheme.text, marginLeft: 10 }}>
Auto brightness
</Text>
</View>
<MySwitch <MySwitch
style={{ flex: 1 }}
value={switchState} value={switchState}
onValueChange={(e) => setSwitchState(e)} onValueChange={(e) => setSwitchState(e)}
/> />
@ -416,6 +459,7 @@ const styles = StyleSheet.create({
sliderStyle: { sliderStyle: {
borderRadius: 20, borderRadius: 20,
marginTop: 20, marginTop: 20,
width: 10,
shadowColor: "#000", shadowColor: "#000",
shadowOffset: { shadowOffset: {
width: 0, width: 0,

View File

@ -3,7 +3,7 @@ import Card from "../../Components/Card";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import MySwitch from "../../Components/Switch"; import MySwitch from "../../Components/Switch";
import MySlider from "../../Components/Slider"; import MySlider from "../../Components/Slider";
import { AppContext } from "../../utils"; import { AppContext, IsPlatformIos } from "../../utils";
import MyIcon from "../../Components/Icon"; import MyIcon from "../../Components/Icon";
export default function MotorView() { export default function MotorView() {
@ -12,7 +12,12 @@ export default function MotorView() {
return ( return (
<Card> <Card>
<View style={{ flexDirection: "row", alignItems: "center" }}> <View
style={[
{ flexDirection: "row", alignItems: "center" },
IsPlatformIos() && { gap: 10 },
]}
>
<MySwitch <MySwitch
value={switchState} value={switchState}
onValueChange={(e) => setSwitchState(e)} onValueChange={(e) => setSwitchState(e)}

View File

@ -1,17 +1,18 @@
import { Text, View } from "react-native"; import { Text, TouchableOpacity, View } from "react-native";
import Card from "../../Components/Card"; import Card from "../../Components/Card";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { AppContext, AppStyles } from "../../utils"; import { AppContext, AppStyles } from "../../utils";
import { Divider } from "../../Components/Divider"; import { Divider } from "../../Components/Divider";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import MySwitch from "../../Components/Switch"; import MySwitch from "../../Components/Switch";
import MyIcon from "../../Components/Icon";
import { TextInputModal } from "../../Components/PickerModal";
export default function SettingsView() { export default function SettingsView() {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const [modalTextInputVisible, setModalTextInputVisible] = useState(false);
const [switchState, setSwitchState] = useState(false); const [switchState, setSwitchState] = useState(false);
const [sliderValue, setSliderValue] = useState(0);
return ( return (
<> <>
@ -70,14 +71,58 @@ export default function SettingsView() {
}, },
]} ]}
> >
{t("screens.device.settings.deviceInformationTitle")} {t("screens.device.settings.deviceInformationText")}
</Text> </Text>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<View>
<Text
style={
([AppStyles.typography16], { color: appContext.appTheme.text })
}
>
{t("screens.device.settings.deviceNameText")}
</Text>
<Text
style={
([AppStyles.typography14],
{ color: appContext.appTheme.textSecondary })
}
>
Turtle
</Text>
</View>
<TouchableOpacity onPress={() => setModalTextInputVisible(true)}>
<MyIcon name="pencil" size={18} color={appContext.appTheme.icon} />
</TouchableOpacity>
</View>
<TextInputModal
isOpen={modalTextInputVisible}
setIsOpen={setModalTextInputVisible}
onTextInputSave={() => console.log("save")}
modalTitle={t("screens.device.settings.deviceNameText")}
inputTitle={t("screens.device.settings.deviceNameText")}
inputDescription={t(
"screens.device.settings.modalTextInputDeviceNameDescription"
)}
/>
<Divider />
<Text <Text
style={ style={
([AppStyles.typography16], { color: appContext.appTheme.text }) ([AppStyles.typography16], { color: appContext.appTheme.text })
} }
> >
{t("screens.device.settings.deviceModelTitle")} {t("screens.device.settings.deviceModelText")}
</Text> </Text>
<Text <Text
style={ style={
@ -87,13 +132,15 @@ export default function SettingsView() {
> >
Shimmex Aurora Shimmex Aurora
</Text> </Text>
<Divider /> <Divider />
<Text <Text
style={ style={
([AppStyles.typography16], { color: appContext.appTheme.text }) ([AppStyles.typography16], { color: appContext.appTheme.text })
} }
> >
{t("screens.device.settings.deviceFirmwareVersionTitle")} {t("screens.device.settings.deviceFirmwareVersionText")}
</Text> </Text>
<Text <Text
style={ style={
@ -103,13 +150,15 @@ export default function SettingsView() {
> >
1.0.1 1.0.1
</Text> </Text>
<Divider /> <Divider />
<Text <Text
style={ style={
([AppStyles.typography16], { color: appContext.appTheme.text }) ([AppStyles.typography16], { color: appContext.appTheme.text })
} }
> >
{t("screens.device.settings.deviceLastUpdatedTitle")} {t("screens.device.settings.deviceLastUpdatedText")}
</Text> </Text>
<Text <Text
style={ style={

View File

@ -1,7 +1,7 @@
import AsyncStorage from "@react-native-async-storage/async-storage"; import AsyncStorage from "@react-native-async-storage/async-storage";
import { createContext, useState } from "react"; import { createContext, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Appearance, StyleSheet } from "react-native"; import { Appearance, Platform, StyleSheet } from "react-native";
export const Constants = { export const Constants = {
defaultLanguage: "de", defaultLanguage: "de",
@ -31,6 +31,13 @@ export const AppStyles = StyleSheet.create({
fontSize: 14, fontSize: 14,
lineHeight: 20, lineHeight: 20,
}, },
Shadow: {
elevation: 2, // only android
shadowColor: "#000", // only ios
shadowOffset: { width: 0, height: 1 }, // only ios
shadowOpacity: 0.2, // only ios
shadowRadius: 1, // only ios
},
}); });
const DarkAppTheme = { const DarkAppTheme = {
@ -206,3 +213,7 @@ export function AppProvider({ children }) {
</AppContext.Provider> </AppContext.Provider>
); );
} }
export function IsPlatformIos() {
return Platform.OS === "ios";
}