new ui device design

main
alex 2023-07-19 23:58:24 +00:00
parent 5ae3f9ffde
commit d96311c3d6
27 changed files with 1317 additions and 178 deletions

8
App.js
View File

@ -15,13 +15,14 @@ import { Suspense, lazy, useContext, useEffect } from "react";
import { SafeAreaView } from "react-native-safe-area-context";
import "./i18n";
import DeviceScreen from "./src/Screens/Device";
import DeviceOldScreen from "./src/Screens/DeviceOld";
const Drawer = createDrawerNavigator();
const SettingsScreen = lazy(() => import("./src/Screens/Settings"));
const FaqScreen = lazy(() => import("./src/Screens/FAQ"));
const FeedbackScreen = lazy(() => import("./src/Screens/Feedback"));
const Drawer = createDrawerNavigator();
export function MyApp() {
const appContext = useContext(AppContext);
@ -63,7 +64,7 @@ export function MyApp() {
<NavigationContainer>
<Drawer.Navigator
screenOptions={{
// headerShown: false,
headerShown: true,
headerStyle: {
backgroundColor: appContext.appTheme.backgroundColor,
},
@ -75,6 +76,7 @@ export function MyApp() {
drawerContent={(props) => <SideBar {...props} />}
>
<Drawer.Screen name="Turtle" component={DeviceScreen} />
<Drawer.Screen name="Old Turtle" component={DeviceOldScreen} />
<Drawer.Screen name="FAQ" component={FaqScreen} />
<Drawer.Screen name="Feedback" component={FeedbackScreen} />
<Drawer.Screen name="Settings" component={SettingsScreen} />

BIN
assets/ambilight.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

BIN
assets/layers.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
assets/motor.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

111
package-lock.json generated
View File

@ -26,6 +26,7 @@
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0",
"react-native-tab-view": "^3.5.2",
"react-native-uuid": "^2.0.1",
"reanimated-color-picker": "^2.3.1"
},
"devDependencies": {
@ -5010,34 +5011,6 @@
"react-native-screens": ">= 3.0.0"
}
},
"node_modules/@react-navigation/drawer/node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
"dependencies": {
"color-convert": "^2.0.1",
"color-string": "^1.9.0"
},
"engines": {
"node": ">=12.5.0"
}
},
"node_modules/@react-navigation/drawer/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/@react-navigation/drawer/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/@react-navigation/elements": {
"version": "1.3.18",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.18.tgz",
@ -6291,6 +6264,18 @@
"node": ">=0.10.0"
}
},
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
"dependencies": {
"color-convert": "^2.0.1",
"color-string": "^1.9.0"
},
"engines": {
"node": ">=12.5.0"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -6313,6 +6298,22 @@
"simple-swizzle": "^0.2.2"
}
},
"node_modules/color/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/colorette": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
@ -7101,6 +7102,15 @@
"expo": "*"
}
},
"node_modules/expo-asset/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/expo-constants": {
"version": "14.2.1",
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-14.2.1.tgz",
@ -7113,6 +7123,15 @@
"expo": "*"
}
},
"node_modules/expo-constants/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/expo-file-system": {
"version": "15.2.2",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-15.2.2.tgz",
@ -7124,6 +7143,15 @@
"expo": "*"
}
},
"node_modules/expo-file-system/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/expo-font": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-11.1.1.tgz",
@ -7269,6 +7297,15 @@
"resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.4.4.tgz",
"integrity": "sha512-5DV0hIEWgatSC3UgQuAZBoQeaS9CqeWRZ3vzBR9R/+IUD87Adbi4FGhU10nymRqFXOizGsureButGZIXPs7zEA=="
},
"node_modules/expo/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/extend-shallow": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
@ -11931,6 +11968,15 @@
"react-native-pager-view": "*"
}
},
"node_modules/react-native-uuid": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/react-native-uuid/-/react-native-uuid-2.0.1.tgz",
"integrity": "sha512-cptnoIbL53GTCrWlb/+jrDC6tvb7ypIyzbXNJcpR3Vab0mkeaaVd5qnB3f0whXYzS+SMoSQLcUUB0gEWqkPC0g==",
"engines": {
"node": ">=10.0.0",
"npm": ">=6.0.0"
}
},
"node_modules/react-native/node_modules/promise": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz",
@ -13795,15 +13841,6 @@
"node": ">= 0.4.0"
}
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/valid-url": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",

View File

@ -27,6 +27,7 @@
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0",
"react-native-tab-view": "^3.5.2",
"react-native-uuid": "^2.0.1",
"reanimated-color-picker": "^2.3.1"
},
"devDependencies": {

View File

@ -0,0 +1,29 @@
import { useContext } from "react";
import { Text, TouchableHighlight, View } from "react-native";
import { AppContext, AppStyles } from "../../utils";
export default function MyButton({ title, style, disabled, onPress }) {
const appContext = useContext(AppContext);
return (
<TouchableHighlight
disabled={disabled}
onPress={onPress}
style={[{ borderRadius: 6 }, style]}
>
<View
style={[
{
backgroundColor: appContext.appTheme.colors.primary,
borderRadius: 6,
padding: 10,
},
AppStyles.Shadow,
disabled && { opacity: 0.6 },
]}
>
<Text style={{ textAlign: "center", color: "#fff" }}>{title}</Text>
</View>
</TouchableHighlight>
);
}

View File

@ -2,9 +2,14 @@ import { useContext } from "react";
import { View } from "react-native";
import { AppContext, AppStyles } from "../../utils";
export default function Card({ children }) {
export default function Card({ children, cardBackgroundColor }) {
const appContext = useContext(AppContext);
const backgroundColor =
cardBackgroundColor === undefined
? appContext.appTheme.card.backgroundColor
: cardBackgroundColor;
return (
<View
style={{
@ -18,7 +23,7 @@ export default function Card({ children }) {
style={[
{
borderRadius: 20,
backgroundColor: appContext.appTheme.card.backgroundColor,
backgroundColor: backgroundColor,
padding: 20,
},
AppStyles.Shadow,

View File

@ -0,0 +1,36 @@
import { Text, View } from "react-native";
import { TouchableOpacity } from "react-native";
import { AppContext, IsPlatformIos } from "../../utils";
import { useContext } from "react";
import MyIcon from "../Icon";
export default function MyDropdown({ label, onPress, selectedItemLabel }) {
const appContext = useContext(AppContext);
return (
<TouchableOpacity onPress={() => onPress()}>
<View
style={[
{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
//IsPlatformIos() && { marginBottom: 10 },
]}
>
<View>
<Text style={{ color: appContext.appTheme.text }}>{label}</Text>
<Text style={{ color: appContext.appTheme.colors.primary }}>
{selectedItemLabel}
</Text>
</View>
<MyIcon
name="chevron-down"
size={24}
color={appContext.appTheme.icon}
/>
</View>
</TouchableOpacity>
);
}

View File

@ -1,5 +1,11 @@
import Icon from "@expo/vector-icons/MaterialCommunityIcons";
import { useContext } from "react";
import { AppContext } from "../../utils";
export default function MyIcon({ style, name, color, size }) {
return <Icon style={style} name={name} color={color} size={size} />;
const appContext = useContext(AppContext);
const iconColor = color === undefined ? appContext.appTheme.icon : color;
return <Icon style={style} name={name} color={iconColor} size={size} />;
}

View File

@ -3,7 +3,7 @@ import { AppContext, AppStyles, IsPlatformIos } from "../../utils";
import {
KeyboardAvoidingView,
Modal,
Platform,
ScrollView,
Text,
TextInput,
TouchableOpacity,
@ -13,15 +13,134 @@ import { Divider } from "../Divider";
import MyIcon from "../Icon";
import { useTranslation } from "react-i18next";
const modalIosPaddingTop = 10;
/*
transparent: set this to prevent modal reopening on iOS
https://github.com/facebook/react-native/issues/34018
*/
export default function PickerModal({
const modalContentStyle = { margin: 10, paddingTop: 10 };
export default function MyModal({
children,
isOpen,
setIsOpen,
items,
searchFilter,
closeModal,
header,
content,
disableAnimationForIos,
scrollView,
}) {
const appContext = useContext(AppContext);
const getContent = () => {
if (scrollView) {
return (
<ScrollView>
<View style={modalContentStyle}>{content}</View>
</ScrollView>
);
}
return <View style={modalContentStyle}>{content}</View>;
};
return (
<View>
<Modal
visible={isOpen}
animationType={disableAnimationForIos ? "none" : "slide"}
onRequestClose={() => closeModal()}
transparent
>
{children}
<View
style={[
{
flex: 1,
backgroundColor: appContext.appTheme.backgroundColor,
paddingTop: IsPlatformIos() ? 30 : 15,
},
,
]}
>
{header}
{getContent()}
</View>
</Modal>
</View>
);
}
export function MyPickerDefaultHeader({ title, closeModal }) {
const appContext = useContext(AppContext);
return (
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<TouchableOpacity onPress={() => closeModal()}>
<MyIcon name="chevron-left" size={24} style={{ marginLeft: 20 }} />
</TouchableOpacity>
<Text
style={[
AppStyles.typography16,
{ fontWeight: "bold", color: appContext.appTheme.text },
]}
>
{title}
</Text>
<View style={{ marginRight: 40 }} />
</View>
);
}
export function MyPickerModalListItem({ onPress, itemName, itemSelected }) {
const appContext = useContext(AppContext);
return (
<>
<TouchableOpacity onPress={onPress}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
marginLeft: 10,
marginRight: 10,
}}
>
<Text
style={[
AppStyles.typography16,
{ color: appContext.appTheme.text },
]}
>
{itemName}
</Text>
{itemSelected && (
<MyIcon
name="check"
size={24}
color={appContext.appTheme.colors.primary}
/>
)}
</View>
</TouchableOpacity>
<Divider />
</>
);
}
export function MyPickerModal({ isOpen, setIsOpen, items, searchFilter }) {
const appContext = useContext(AppContext);
const { t } = useTranslation();
const [providedItems, setProvidedItems] = useState([]);
const [searchFilterInput, setSearchFilterInput] = useState("");
@ -47,7 +166,6 @@ export default function PickerModal({
flex: 1,
backgroundColor: appContext.appTheme.backgroundColor,
},
IsPlatformIos() && { paddingTop: modalIosPaddingTop },
]}
>
<View style={{ flexDirection: "row", alignItems: "center" }}>
@ -193,7 +311,7 @@ export default function PickerModal({
);
}
export function TextInputModal({
export function MyTextInputModal({
isOpen,
setIsOpen,
onTextInputSave,
@ -217,7 +335,6 @@ export function TextInputModal({
flex: 1,
backgroundColor: appContext.appTheme.backgroundColor,
},
IsPlatformIos() && { paddingTop: modalIosPaddingTop },
]}
>
<View

View File

@ -122,7 +122,7 @@ export default function Sidebar(props) {
{t("sideBar.devicesTitle")}
</Text>
<DrawerContentScrollView contentContainerStyle={{ paddingTop: 0 }}>
{["Turtle"].map((item, i) => (
{["Turtle", "Old Turtle"].map((item, i) => (
<MyDrawerItem
key={i}
isDevice

View File

@ -1,4 +1,4 @@
import { lazy, useContext, useState } from "react";
import { useContext, useState } from "react";
import {
Image,
ScrollView,
@ -7,17 +7,12 @@ import {
Text,
TouchableHighlight,
TouchableOpacity,
Dimensions,
Platform,
ImageBackground,
} from "react-native";
import LightView from "./light";
import { AppContext } from "../../utils";
import MyIcon from "../../Components/Icon";
const MotorView = lazy(() => import("./motor"));
const SettingsView = lazy(() => import("./settings"));
const ColorScenesView = lazy(() => import("./colorScenes"));
import SettingsView from "./settings";
import SceneView from "./scene";
const spaceToSide = 10; // left and right
const top = 35;
@ -27,43 +22,16 @@ const topFirst = top;
const topSecond = top + spaceBetweenButtons;
const topThird = top + 2 * spaceBetweenButtons;
const windowDimensions = Dimensions.get("window");
let data = [];
const ledLines = 4;
const ledsPerLine = 16;
const deviceLeds = ledLines * ledsPerLine;
// setting default color for testing
for (let i = 0; i < deviceLeds; i++) {
data.push("lime");
}
export default function DeviceScreen() {
const appContext = useContext(AppContext);
const [selectedView, setSelectedView] = useState(0);
const [deviceLedsColors, setDeviceLedsColor] = useState(data);
const [selectedDeviceLed, setSelectedDeviceLed] = useState(2);
const SelectedView = () => {
switch (selectedView) {
case 0:
return <LightView />;
case 2:
return <MotorView />;
case 3:
return <SettingsView />;
case 4:
return (
<ColorScenesView
deviceLedsColors={deviceLedsColors}
setDeviceLedsColor={setDeviceLedsColor}
selectedDeviceLed={selectedDeviceLed}
setSelectedDeviceLed={setSelectedDeviceLed}
/>
);
case 2:
return <SceneView />;
default:
<Text>Not found</Text>;
}
@ -102,59 +70,6 @@ export default function DeviceScreen() {
);
};
const DeviceLedsColor = () => {
let elements = [];
let test = 400;
for (let l = 0; l < ledLines; l++) {
let left = 0;
let ledsElements = [];
for (let p = 0; p < ledsPerLine; p++) {
left = left + 1.5;
const ledId = l > 0 ? l * 16 + p : p;
const deviceLedColor =
l > 0 ? deviceLedsColors[l * 16 + p] : deviceLedsColors[p];
ledsElements.push(
<View
key={`${l}-${p}`}
style={{
width: 0.1 * (test / 16),
height: 0.1 * (test / 16),
backgroundColor:
ledId === selectedDeviceLed ? "red" : deviceLedColor,
borderRadius: 0.5,
left: left,
marginRight:
windowDimensions.width > 360
? 0.27 * (test / 16)
: 0.26 * (test / 16),
}}
/>
);
}
elements.push(
<View
key={`v-${l}`}
style={{
flexDirection: "row",
justifyContent: "center",
top: 118.5 + l,
right: "8%",
}}
>
{ledsElements}
</View>
);
}
return <>{elements}</>;
};
return (
<View
style={{
@ -193,49 +108,27 @@ export default function DeviceScreen() {
)}
<MyButton
iconName={"lightbulb-on-outline"}
left
iconName={"cog-outline"}
left={false}
selectedViewNumber={0}
space={spaceToSide}
top={topFirst}
/>
<MyButton
iconName={"television-ambient-light"}
left
iconName={"rotate-3d-variant"}
left={false}
selectedViewNumber={1}
space={spaceToSide}
top={topSecond}
/>
<MyButton
iconName={"axis-z-rotate-counterclockwise"}
left
iconName={"group"}
left={false}
selectedViewNumber={2}
space={spaceToSide}
top={topThird}
/>
<MyButton
iconName={"cog-outline"}
left={false}
selectedViewNumber={3}
space={spaceToSide}
top={topFirst}
/>
<MyButton
iconName={"palette-outline"}
left={false}
selectedViewNumber={4}
space={spaceToSide}
top={topSecond}
/>
<MyButton
iconName={"rotate-3d-variant"}
left={false}
selectedViewNumber={5}
space={spaceToSide}
top={topThird}
/>
<ScrollView style={{ height: "100%" }}>
<SelectedView />
</ScrollView>

View File

@ -0,0 +1,5 @@
import { Text } from "react-native";
export default function LayersActionEditModalContent() {
return <Text>Layers</Text>;
}

View File

@ -0,0 +1,83 @@
import { useState } from "react";
import { Text, TouchableHighlight, View } from "react-native";
import MyButton from "../../../../../Components/Button";
function Layer({ number, selected, onPress }) {
return (
<TouchableHighlight onPress={onPress} style={{ borderRadius: 10 }}>
<View
style={[
{
height: 150,
width: 150,
backgroundColor: "gray",
borderRadius: 10,
},
selected && { borderColor: "#9b59b6", borderWidth: 6 },
]}
>
<View style={{ alignItems: "flex-end" }}>
<View
style={{
marginTop: 6,
marginRight: 6,
backgroundColor: "#e67e22",
paddingLeft: 6,
paddingRight: 6,
borderRadius: 10,
}}
>
<Text>{number}</Text>
</View>
</View>
</View>
</TouchableHighlight>
);
}
export default function LayerSelectionModalContent({
openLayersActionEditModal,
}) {
const [selectedLayer, setSelectedLayer] = useState([]);
const handleSelectLayerClick = (layerNumber) => {
if (selectedLayer.includes(layerNumber)) {
setSelectedLayer((sLayer) =>
sLayer.filter((item) => item != layerNumber)
);
} else {
setSelectedLayer((sLayer) => [...sLayer, layerNumber]);
}
};
return (
<>
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
gap: 14,
justifyContent: "center",
}}
>
{Array.from({ length: 4 }).map((_, i) => (
<Layer
key={i}
number={i + 1}
selected={selectedLayer.includes(i + 1)}
onPress={() => handleSelectLayerClick(i + 1)}
/>
))}
</View>
<View style={{ alignItems: "center" }}>
<MyButton
title={"Hinzufügen"}
style={{ marginTop: 20, width: 180 }}
disabled={selectedLayer.length === 0}
onPress={() => openLayersActionEditModal()}
/>
</View>
</>
);
}

View File

@ -0,0 +1,89 @@
import { useContext } from "react";
import { Image, Text, TouchableOpacity, View } from "react-native";
import { AppContext, AppStyles } from "../../../../utils";
import MyIcon from "../../../../Components/Icon";
import { Divider } from "../../../../Components/Divider";
function Action({ text, iconName, imageSource, onPress }) {
const appContext = useContext(AppContext);
return (
<>
<TouchableOpacity
style={{ marginLeft: 10, marginRight: 10 }}
onPress={onPress}
>
<View
style={[
{
flexDirection: "row",
alignItems: "center",
},
imageSource !== undefined && { marginBottom: 4 },
]}
>
<MyIcon name={iconName} size={24} />
<Text
style={[
AppStyles.typography16,
{ color: appContext.appTheme.text, marginLeft: 6 },
]}
>
{text}
</Text>
</View>
{imageSource !== undefined && (
<Image source={imageSource} style={{ height: 150, width: 150 }} />
)}
</TouchableOpacity>
<Divider />
</>
);
}
export default function AddSceneActionModalContent({
openLayerSelectionModal,
}) {
return (
<>
<Action
text="Ebenen"
iconName="lightbulb-on-outline"
imageSource={require("../../../../../assets/layers.gif")}
onPress={openLayerSelectionModal}
/>
<Action
text="Motor"
iconName="axis-z-rotate-counterclockwise"
imageSource={require("../../../../../assets/motor.gif")}
onPress={() => console.log("pressed action")}
/>
<Action
text="Ambilight"
iconName="television-ambient-light"
imageSource={require("../../../../../assets/ambilight.gif")}
onPress={() => console.log("pressed action")}
/>
<Action
text="Warten"
iconName="timer-sand"
onPress={() => console.log("pressed action")}
/>
<Action
text="Stop"
iconName="pause-octagon-outline"
onPress={() => console.log("pressed action")}
/>
<Action
text="Zeitsteuerung"
iconName="timer-cog"
onPress={() => console.log("pressed action")}
/>
</>
);
}

View File

@ -0,0 +1,54 @@
import { useContext } from "react";
import {
AppContext,
DevDeviceId,
NewEmptyDeviceScene,
} from "../../../../../utils";
import { FlatList, View } from "react-native";
import { MyPickerModalListItem } from "../../../../../Components/Modal";
export default function CreateSceneModalContent({ closeChooseSceneModals }) {
const appContext = useContext(AppContext);
const options = [
{
id: 0,
name: "Leere Szene erstellen",
},
{
id: 1,
name: "Standard Szene erstellen",
},
{
id: 2,
name: "Vorhandene Szene kopieren",
},
];
return (
<FlatList
data={options}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<MyPickerModalListItem
itemName={item.name}
onPress={() => {
console.log("pressed", item);
appContext.setDevices((arr) => {
const newArr = [...arr];
newArr[arr.findIndex((d) => d.id === DevDeviceId)].scenes.push(
NewEmptyDeviceScene()
);
return newArr;
});
closeChooseSceneModals();
}}
/>
)}
/>
);
}

View File

@ -0,0 +1,64 @@
import { useContext, useState } from "react";
import { FlatList, Text, TouchableOpacity } from "react-native";
import { AppContext, AppStyles, DevDeviceId } from "../../../../utils";
import { Divider } from "../../../../Components/Divider";
import { View } from "react-native";
import MyIcon from "../../../../Components/Icon";
import { MyPickerModalListItem } from "../../../../Components/Modal";
export default function ChooseSceneModalContent({ openCreateSceneModal }) {
const appContext = useContext(AppContext);
const device = appContext.devices.find((device) => device.id === DevDeviceId);
return (
<>
<FlatList
data={device.scenes}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<MyPickerModalListItem
itemName={item.name}
itemSelected={device.selectedScene === item.id}
onPress={() => {
appContext.setDevices((arr) => {
const newArr = [...arr];
newArr[
arr.findIndex((d) => d.id === DevDeviceId)
].selectedScene = item.id;
return newArr;
});
}}
/>
)}
/>
<TouchableOpacity
style={{
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginTop: 10,
}}
onPress={openCreateSceneModal}
>
<MyIcon
name="plus-circle-outline"
color={appContext.appTheme.textSecondary}
size={24}
style={{ marginRight: 10 }}
/>
<Text
style={[
AppStyles.typography16,
{ color: appContext.appTheme.textSecondary },
]}
>
Neue Szene erstellen
</Text>
</TouchableOpacity>
</>
);
}

227
src/Screens/Device/scene.js Normal file
View File

@ -0,0 +1,227 @@
import { FlatList, Text, TouchableOpacity, View } from "react-native";
import Card from "../../Components/Card";
import { AppContext, AppStyles, DevDeviceId } from "../../utils";
import { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import MyDropdown from "../../Components/Dropdown";
import MyIcon from "../../Components/Icon";
import MyModal, { MyPickerDefaultHeader } from "../../Components/Modal";
import CreateSceneModalContent from "./modals/ChooseScene/CreateScene";
import ChooseSceneModalContent from "./modals/ChooseScene";
import AddSceneActionModalContent from "./modals/AddSceneAction";
import LayerSelectionModalContent from "./modals/AddSceneAction/LayerSelection";
import LayersActionEditModalContent from "./modals/ActionEdits/Layers";
function NothingSelected({ text }) {
const appContext = useContext(AppContext);
return (
<View
style={{
alignItems: "center",
marginTop: 40,
}}
>
<MyIcon name="selection-search" size={64} />
<Text
style={[
AppStyles.typography16,
{ color: appContext.appTheme.text, marginTop: 10 },
]}
>
{text}
</Text>
</View>
);
}
export default function SceneView() {
const appContext = useContext(AppContext);
const [modalOpenStates, setModalOpenStates] = useState({
modalChooseSceneIsOpen: false,
modalCreateSceneIsOpen: false,
modalAddSceneActionIsOpen: false,
modalLayerSectionIsOpen: false,
modalLayersActionEditIsOpen: false,
});
const setModalOpen = (modalName, open) => {
console.log("setModalOpen", modalName, open);
setModalOpenStates((prevState) => ({
...prevState,
[modalName]: open,
}));
};
const device = appContext.devices.find((device) => device.id === DevDeviceId);
const deviceScene = device.scenes.find(
(scene) => scene.id === device.selectedScene
);
const closeChooseSceneModals = () => {
setModalOpenStates((prevState) => ({
...prevState,
modalChooseSceneIsOpen: false,
modalCreateSceneIsOpen: false,
}));
};
const actionColor =
deviceScene === undefined || deviceScene.actions.length === 0
? appContext.appTheme.colors.primary
: appContext.appTheme.textSecondary;
return (
<>
<Card>
<MyDropdown
label={"Szene"}
onPress={() => {
console.log("pressed");
setModalOpen("modalChooseSceneIsOpen", true);
}}
selectedItemLabel={
device.selectedScene === 0
? "Keine Szene ausgewählt"
: device.scenes.find((scene) => scene.id === device.selectedScene)
.name
}
/>
</Card>
{device.selectedScene === 0 ? (
<NothingSelected text="Keine Szene ausgewählt" />
) : (
<View>
{deviceScene.actions.length === 0 ? (
<NothingSelected text="Keine Aktionen in der Szene vorhanden" />
) : (
<FlatList
scrollEnabled={false}
data={deviceScene.actions}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Card cardBackgroundColor={appContext.appTheme.colors.primary}>
<Text>{item.name}</Text>
</Card>
)}
/>
)}
<TouchableOpacity
onPress={() => setModalOpen("modalAddSceneActionIsOpen", true)}
>
<View
style={{
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginTop: 20,
}}
>
<MyIcon
name="plus-circle-outline"
size={24}
style={{
marginRight: 10,
color: actionColor,
}}
/>
<Text style={{ color: actionColor }}>Aktion hinzufügen</Text>
</View>
</TouchableOpacity>
</View>
)}
<MyModal
isOpen={modalOpenStates.modalChooseSceneIsOpen}
closeModal={() => setModalOpen("modalChooseSceneIsOpen", false)}
header={
<MyPickerDefaultHeader
title={"Wähle eine Szene aus"}
closeModal={() => setModalOpen("modalChooseSceneIsOpen", false)}
/>
}
content={
<ChooseSceneModalContent
openCreateSceneModal={() =>
setModalOpen("modalCreateSceneIsOpen", true)
}
/>
}
>
<MyModal
isOpen={modalOpenStates.modalCreateSceneIsOpen}
closeModal={() => setModalOpen("modalCreateSceneIsOpen", false)}
header={
<MyPickerDefaultHeader
title={"Szene erstellen"}
closeModal={() => setModalOpen("modalCreateSceneIsOpen", false)}
/>
}
content={
<CreateSceneModalContent
closeChooseSceneModals={closeChooseSceneModals}
/>
}
/>
</MyModal>
<MyModal
scrollView
isOpen={modalOpenStates.modalAddSceneActionIsOpen}
closeModal={() => setModalOpen("modalAddSceneActionIsOpen", false)}
header={
<MyPickerDefaultHeader
title={"Aktion hinzufügen"}
closeModal={() => setModalOpen("modalAddSceneActionIsOpen", false)}
/>
}
content={
<AddSceneActionModalContent
openLayerSelectionModal={() =>
setModalOpen("modalLayerSectionIsOpen", true)
}
/>
}
>
<MyModal
scrollView
isOpen={modalOpenStates.modalLayerSectionIsOpen}
closeModal={() => setModalOpen("modalLayerSectionIsOpen", false)}
header={
<MyPickerDefaultHeader
title={"Layer auswahl"}
closeModal={() => setModalOpen("modalLayerSectionIsOpen", false)}
/>
}
content={
<LayerSelectionModalContent
openLayersActionEditModal={() =>
setModalOpen("modalLayersActionEditIsOpen", true)
}
/>
}
>
<MyModal
scrollView
isOpen={modalOpenStates.modalLayersActionEditIsOpen}
closeModal={() =>
setModalOpen("modalLayersActionEditIsOpen", false)
}
header={
<MyPickerDefaultHeader
title={"Layer Action bearbeiten"}
closeModal={() =>
setModalOpen("modalLayersActionEditIsOpen", false)
}
/>
}
content={<LayersActionEditModalContent />}
/>
</MyModal>
</MyModal>
</>
);
}

View File

@ -6,7 +6,7 @@ import { Divider } from "../../Components/Divider";
import { useTranslation } from "react-i18next";
import MySwitch from "../../Components/Switch";
import MyIcon from "../../Components/Icon";
import { TextInputModal } from "../../Components/PickerModal";
import { MyTextInputModal } from "../../Components/Modal";
export default function SettingsView() {
const appContext = useContext(AppContext);
@ -104,7 +104,7 @@ export default function SettingsView() {
</TouchableOpacity>
</View>
<TextInputModal
<MyTextInputModal
isOpen={modalTextInputVisible}
setIsOpen={setModalTextInputVisible}
onTextInputSave={() => console.log("save")}

View File

@ -0,0 +1,261 @@
import { lazy, useContext, useState } from "react";
import {
Image,
ScrollView,
StyleSheet,
View,
Text,
TouchableHighlight,
TouchableOpacity,
Dimensions,
Platform,
ImageBackground,
} from "react-native";
import LightView from "./light";
import { AppContext } from "../../utils";
import MyIcon from "../../Components/Icon";
const MotorView = lazy(() => import("./motor"));
const SettingsView = lazy(() => import("./settings"));
const ColorScenesView = lazy(() => import("./colorScenes"));
const spaceToSide = 10; // left and right
const top = 35;
const spaceBetweenButtons = 60;
const topFirst = top;
const topSecond = top + spaceBetweenButtons;
const topThird = top + 2 * spaceBetweenButtons;
const windowDimensions = Dimensions.get("window");
let data = [];
const ledLines = 4;
const ledsPerLine = 16;
const deviceLeds = ledLines * ledsPerLine;
// setting default color for testing
for (let i = 0; i < deviceLeds; i++) {
data.push("lime");
}
export default function DeviceOldScreen() {
const appContext = useContext(AppContext);
const [selectedView, setSelectedView] = useState(0);
const [deviceLedsColors, setDeviceLedsColor] = useState(data);
const [selectedDeviceLed, setSelectedDeviceLed] = useState(2);
const SelectedView = () => {
switch (selectedView) {
case 0:
return <LightView />;
case 2:
return <MotorView />;
case 3:
return <SettingsView />;
case 4:
return (
<ColorScenesView
deviceLedsColors={deviceLedsColors}
setDeviceLedsColor={setDeviceLedsColor}
selectedDeviceLed={selectedDeviceLed}
setSelectedDeviceLed={setSelectedDeviceLed}
/>
);
default:
<Text>Not found</Text>;
}
};
const MyButton = ({ selectedViewNumber, top, left, space, iconName }) => {
const TouchComponent =
appContext.appColorScheme === "dark"
? TouchableHighlight
: TouchableOpacity;
return (
<TouchComponent
onPress={() => setSelectedView(selectedViewNumber)}
style={[
{
position: "absolute",
backgroundColor: appContext.appTheme.card.backgroundColor,
borderRadius: 10,
padding: 8,
},
{ top: top },
left === true ? { left: space } : { right: space },
]}
>
<MyIcon
name={iconName}
size={30}
color={
selectedView === selectedViewNumber
? appContext.appTheme.colors.primary
: appContext.appTheme.icon
}
/>
</TouchComponent>
);
};
const DeviceLedsColor = () => {
let elements = [];
let test = 400;
for (let l = 0; l < ledLines; l++) {
let left = 0;
let ledsElements = [];
for (let p = 0; p < ledsPerLine; p++) {
left = left + 1.5;
const ledId = l > 0 ? l * 16 + p : p;
const deviceLedColor =
l > 0 ? deviceLedsColors[l * 16 + p] : deviceLedsColors[p];
ledsElements.push(
<View
key={`${l}-${p}`}
style={{
width: 0.1 * (test / 16),
height: 0.1 * (test / 16),
backgroundColor:
ledId === selectedDeviceLed ? "red" : deviceLedColor,
borderRadius: 0.5,
left: left,
marginRight:
windowDimensions.width > 360
? 0.27 * (test / 16)
: 0.26 * (test / 16),
}}
/>
);
}
elements.push(
<View
key={`v-${l}`}
style={{
flexDirection: "row",
justifyContent: "center",
top: 118.5 + l,
right: "8%",
}}
>
{ledsElements}
</View>
);
}
return <>{elements}</>;
};
return (
<View
style={{
height: "100%",
backgroundColor: appContext.appTheme.backgroundColor,
}}
>
{selectedView === 4 && (
<View>
<ImageBackground
source={require("../../../assets/image19.png")}
resizeMode="contain"
style={{ height: 250 }}
>
<DeviceLedsColor />
</ImageBackground>
</View>
)}
{appContext.isUserDeveloperModeEnabled ? (
<>
{selectedView !== 4 && (
<Image
source={require("../../../assets/device.png")}
style={styles.image}
resizeMode="contain"
/>
)}
</>
) : (
<>
{selectedView !== 4 && (
<View style={[styles.image, { backgroundColor: "#ddd" }]} />
)}
</>
)}
<MyButton
iconName={"lightbulb-on-outline"}
left
selectedViewNumber={0}
space={spaceToSide}
top={topFirst}
/>
<MyButton
iconName={"television-ambient-light"}
left
selectedViewNumber={1}
space={spaceToSide}
top={topSecond}
/>
<MyButton
iconName={"axis-z-rotate-counterclockwise"}
left
selectedViewNumber={2}
space={spaceToSide}
top={topThird}
/>
<MyButton
iconName={"cog-outline"}
left={false}
selectedViewNumber={3}
space={spaceToSide}
top={topFirst}
/>
<MyButton
iconName={"palette-outline"}
left={false}
selectedViewNumber={4}
space={spaceToSide}
top={topSecond}
/>
<MyButton
iconName={"rotate-3d-variant"}
left={false}
selectedViewNumber={5}
space={spaceToSide}
top={topThird}
/>
<ScrollView style={{ height: "100%" }}>
<SelectedView />
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: "#2e2e30",
},
scrollView: {
width: "100%",
padding: 20,
},
image: {
width: "100%",
height: 250,
},
});

View File

@ -25,9 +25,9 @@ import { AppContext, AppStyles, IsPlatformIos } from "../../utils";
import Card from "../../Components/Card";
import MySwitch from "../../Components/Switch";
import MyIcon from "../../Components/Icon";
import PickerModal from "../../Components/PickerModal";
import { useTranslation } from "react-i18next";
import MySlider from "../../Components/Slider";
import MyPickerModal from "../../Components/Modal";
const colorModePickerOptions = [
{ label: "Pulse", value: "pulse" },
@ -198,7 +198,7 @@ export default function LightView() {
</View>
</TouchableOpacity>
<PickerModal
<MyPickerModal
searchFilter
isOpen={modalDeviceColorModeVisible}
setIsOpen={setModalDeviceColorModeVisible}

View File

@ -0,0 +1,174 @@
import { Text, TouchableOpacity, View } from "react-native";
import Card from "../../Components/Card";
import { useContext, useState } from "react";
import { AppContext, AppStyles } from "../../utils";
import { Divider } from "../../Components/Divider";
import { useTranslation } from "react-i18next";
import MySwitch from "../../Components/Switch";
import MyIcon from "../../Components/Icon";
import { MyTextInputModal } from "../../Components/Modal";
export default function SettingsView() {
const appContext = useContext(AppContext);
const { t } = useTranslation();
const [modalTextInputVisible, setModalTextInputVisible] = useState(false);
const [switchState, setSwitchState] = useState(false);
return (
<>
<Card>
<Text
style={[
AppStyles.typography20,
{
color: appContext.appTheme.text,
marginBottom: 10,
},
]}
>
{t("screens.device.settings.settingsTitle")}
</Text>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<View style={{ width: "80%" }}>
<Text
style={[
AppStyles.typography16,
{ color: appContext.appTheme.text },
]}
>
{t("screens.device.settings.wifiStandByTitle")}
</Text>
<Text
style={[
AppStyles.typography14,
{ color: appContext.appTheme.textSecondary },
]}
>
{t("screens.device.settings.wifiStandByDescription")}
</Text>
</View>
<MySwitch
value={switchState}
onValueChange={(e) => setSwitchState(e)}
/>
</View>
</Card>
<Card>
<Text
style={[
AppStyles.typography20,
{
color: appContext.appTheme.text,
marginBottom: 10,
},
]}
>
{t("screens.device.settings.deviceInformationText")}
</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>
<MyTextInputModal
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
style={
([AppStyles.typography16], { color: appContext.appTheme.text })
}
>
{t("screens.device.settings.deviceModelText")}
</Text>
<Text
style={
([AppStyles.typography14],
{ color: appContext.appTheme.textSecondary })
}
>
Shimmex Aurora
</Text>
<Divider />
<Text
style={
([AppStyles.typography16], { color: appContext.appTheme.text })
}
>
{t("screens.device.settings.deviceFirmwareVersionText")}
</Text>
<Text
style={
([AppStyles.typography14],
{ color: appContext.appTheme.textSecondary })
}
>
1.0.1
</Text>
<Divider />
<Text
style={
([AppStyles.typography16], { color: appContext.appTheme.text })
}
>
{t("screens.device.settings.deviceLastUpdatedText")}
</Text>
<Text
style={
([AppStyles.typography14],
{ color: appContext.appTheme.textSecondary })
}
>
11.07.2023 um 20:33 Uhr
</Text>
</Card>
</>
);
}

View File

@ -3,9 +3,9 @@ import { Text, TouchableOpacity, View } from "react-native";
import Card from "../../Components/Card";
import { AppContext, AppStyles, Constants } from "../../utils";
import { Divider } from "../../Components/Divider";
import PickerModal from "../../Components/PickerModal";
import { useTranslation } from "react-i18next";
import MySwitch from "../../Components/Switch";
import { MyPickerModal } from "../../Components/Modal";
export default function SettingsScreen() {
const appContext = useContext(AppContext);
@ -139,7 +139,7 @@ export default function SettingsScreen() {
/>
</View>
<PickerModal
<MyPickerModal
isOpen={modalAppColorSchemeVisible}
setIsOpen={setAppColorSchemeModalVisible}
items={[
@ -160,7 +160,7 @@ export default function SettingsScreen() {
]}
/>
<PickerModal
<MyPickerModal
isOpen={modalAppLanguageVisible}
setIsOpen={setModalAppLanguageVisible}
items={Constants.languages.map((language) => {

View File

@ -2,6 +2,7 @@ import AsyncStorage from "@react-native-async-storage/async-storage";
import { createContext, useState } from "react";
import { useTranslation } from "react-i18next";
import { Appearance, Platform, StyleSheet } from "react-native";
import uuid from "react-native-uuid";
export const Constants = {
defaultLanguage: "de",
@ -147,14 +148,66 @@ export function GetDataFromList(list, key) {
return list.find((v) => v[0] === key)[1];
}
export function GetUuid() {
return uuid.v4();
}
const appContextPreview = {
appColorScheme: "",
appLanguage: "",
isUserExpertModeEnabled: "",
isUserDeveloperModeEnabled: "",
appTheme: DarkAppTheme,
devices: [],
};
export const DevDeviceId = 0;
const devData = [
{
id: 0, // deviceId
selectedScene: 0,
scenes: [
{
id: 1,
name: "Szene 1",
actions: [
{
id: 0,
name: "Test",
},
{
id: 1,
name: "Test1",
},
],
},
{
id: 2,
name: "Szene 2",
actions: [
{
id: 0,
name: "Haha",
},
{
id: 1,
name: "Haha 1",
},
],
},
],
},
];
export function NewEmptyDeviceScene() {
return {
id: GetUuid(),
name: "Leere Szene",
actions: [],
};
}
export const AppContext = createContext(appContextPreview);
export function AppProvider({ children }) {
@ -165,6 +218,7 @@ export function AppProvider({ children }) {
// TODO: only while development
const [isUserDeveloperModeEnabled, setIsUserDeveloperModeEnabled] =
useState(false);
const [devices, setDevices] = useState(devData);
const { i18n } = useTranslation();
const saveAppColorScheme = async (value) => {
@ -207,6 +261,8 @@ export function AppProvider({ children }) {
setIsUserExpertModeEnabled: saveUserExpertMode,
isUserDeveloperModeEnabled: isUserDeveloperModeEnabled,
setUserIsDeveloperModeEnabled: saveUserDeveloperMode,
devices: devices,
setDevices: setDevices,
}}
>
{children}