From 37892eff3edf09d09b6acae8f9ee1a6ece9f3303 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 25 Jul 2023 09:16:18 +0000 Subject: [PATCH] separating states into multiple states --- App.js | 13 +- locales/de.json | 2 +- src/Components/ColorPicker/index.js | 22 +-- src/Components/SideBar/index.js | 9 +- src/Screens/Device/index.js | 8 +- .../AddSceneAction/LayerSelection/index.js | 44 ++++- .../modals/ChooseScene/CreateScene/index.js | 30 +++- .../Device/modals/ChooseScene/index.js | 36 +++-- .../Device/modals/EditActions/Lights/index.js | 64 ++++++-- src/Screens/Device/scene.js | 118 ++++++++++---- src/Screens/Device/settings.js | 13 +- src/utils.js | 151 ++++++++++++------ 12 files changed, 373 insertions(+), 137 deletions(-) diff --git a/App.js b/App.js index 9b321c8..0c17e5f 100644 --- a/App.js +++ b/App.js @@ -52,12 +52,17 @@ export function MyApp() { "appColorScheme", "userExpertMode", "userDeveloperMode", + "userColorSwatchesFavorites", ]); const appLanguage = GetDataFromList(data, "appLanguage"); const appColorScheme = GetDataFromList(data, "appColorScheme"); const userExpertMode = GetDataFromList(data, "userExpertMode"); const userDeveloperMode = GetDataFromList(data, "userDeveloperMode"); + const userColorSwatchesFavorites = GetDataFromList( + data, + "userColorSwatchesFavorites" + ); appContext.setAppLanguage( appLanguage === null ? Constants.defaultLanguage : appLanguage @@ -73,6 +78,10 @@ export function MyApp() { appContext.setUserIsDeveloperModeEnabled( userDeveloperMode == null ? false : userDeveloperMode ); + + appContext.setUserColorSwatchesFavorites( + userColorSwatchesFavorites === null ? [] : userColorSwatchesFavorites + ); }; loadData(); @@ -255,7 +264,7 @@ function MyDrawer() { key={device.id} name={device.displayName} component={DeviceScreenStack} - initialParams={{ device: device }} + initialParams={{ deviceDisplayName: device.displayName }} /> ))} @@ -272,7 +281,7 @@ const DeviceScreenStack = (props) => { return ( { @@ -283,15 +282,18 @@ export function MyColorPickerV2({ isUserExpertModeEnabled, appThemeText }) { marginTop: 10, }} > - {colorSwatchesFavorites.map((color, i) => ( + {appContext.userColorSwatchesFavorites.map((color, i) => ( pickerRef.current.setColor(color)} onLongPress={() => { - setColorSwatchesFavorites((colors) => - colors.filter((c) => c !== color) - ); + let filteredColors = + appContext.userColorSwatchesFavorites.filter( + (c) => c !== color + ); + + appContext.setUserColorSwatchesFavorites(filteredColors); VibrateShort(); }} @@ -313,10 +315,12 @@ export function MyColorPickerV2({ isUserExpertModeEnabled, appThemeText }) { { if ( - !colorSwatchesFavorites.includes(selectedColorPickerColor.value) + !appContext.userColorSwatchesFavorites.includes( + selectedColorPickerColor.value + ) ) { - setColorSwatchesFavorites([ - ...colorSwatchesFavorites, + appContext.setUserColorSwatchesFavorites([ + ...appContext.userColorSwatchesFavorites, selectedColorPickerColor.value, ]); } diff --git a/src/Components/SideBar/index.js b/src/Components/SideBar/index.js index ffc8e38..bac900d 100644 --- a/src/Components/SideBar/index.js +++ b/src/Components/SideBar/index.js @@ -11,6 +11,7 @@ export default function Sidebar(props) { const { t } = useTranslation(); const MyDrawerItem = ({ + _deviceId, label, onPress, iconName, @@ -27,7 +28,12 @@ export default function Sidebar(props) { ? appContext.appTheme.drawer.item.activeTintColor : appContext.appTheme.drawer.item.inactiveTintColor; - if (isFocused && isDevice) AppSelectedUserDevice.current = routeName; + if (isFocused && isDevice) { + AppSelectedUserDevice.current = { + id: _deviceId, + routeName: routeName, + }; + } return ( ( { switch (selectedView) { case 0: - return ; + return ; case 2: - return ; + return ; default: Not found; } diff --git a/src/Screens/Device/modals/AddSceneAction/LayerSelection/index.js b/src/Screens/Device/modals/AddSceneAction/LayerSelection/index.js index ed4830d..4857f89 100644 --- a/src/Screens/Device/modals/AddSceneAction/LayerSelection/index.js +++ b/src/Screens/Device/modals/AddSceneAction/LayerSelection/index.js @@ -1,8 +1,16 @@ -import { useState } from "react"; +import { useContext, useState } from "react"; import { Text, TouchableHighlight, View } from "react-native"; import MyButton from "../../../../../Components/Button"; import { useTranslation } from "react-i18next"; -import { AppSelectedUserDevice, ModalContainer } from "../../../../../utils"; +import { + AppContext, + AppSelectedUserDevice, + Constants, + GetDevice, + ModalContainer, + NewAction, + NewInitialDeviceSceneAction, +} from "../../../../../utils"; function Layer({ number, selected, onPress }) { return ( @@ -38,6 +46,7 @@ function Layer({ number, selected, onPress }) { } export default function LayerSelectionModalContent({ navigation, route }) { + const appContext = useContext(AppContext); const { t } = useTranslation(); const [selectedLayer, setSelectedLayer] = useState([]); @@ -77,7 +86,36 @@ export default function LayerSelectionModalContent({ navigation, route }) { style={{ marginTop: 20, width: 180 }} disabled={selectedLayer.length === 0} onPress={() => { - navigation.navigate(AppSelectedUserDevice.current); + const newAction = NewAction(Constants.actionType.layers); + + appContext.setDeviceSceneActions((arr) => { + let newArr = [...arr]; + + const deviceSelectedScene = GetDevice( + appContext.devices + ).selectedScene; + + const foundDeviceSceneActionIndex = newArr.findIndex( + (a) => a.sceneId === deviceSelectedScene + ); + + if (foundDeviceSceneActionIndex === -1) { + const initialDeviceActionScene = + NewInitialDeviceSceneAction(deviceSelectedScene); + + initialDeviceActionScene.actions.push(newAction); + + newArr.push(initialDeviceActionScene); + } else { + newArr[foundDeviceSceneActionIndex].actions.push(newAction); + } + + return newArr; + }); + + route.params["actionId"] = newAction.id; + + navigation.navigate(AppSelectedUserDevice.current.routeName); navigation.navigate("modalLayersEditAction", route.params); }} /> diff --git a/src/Screens/Device/modals/ChooseScene/CreateScene/index.js b/src/Screens/Device/modals/ChooseScene/CreateScene/index.js index ca1f996..1903186 100644 --- a/src/Screens/Device/modals/ChooseScene/CreateScene/index.js +++ b/src/Screens/Device/modals/ChooseScene/CreateScene/index.js @@ -1,14 +1,16 @@ import { useContext } from "react"; import { AppContext, + AppSelectedUserDevice, ModalContainer, NewEmptyDeviceScene, + NewInitialDeviceScene, } from "../../../../../utils"; import { FlatList } from "react-native"; import { MyPickerModalListItem } from "../../../../../Components/Modal"; import { useTranslation } from "react-i18next"; -export default function CreateSceneModalContent({ navigation, route }) { +export default function CreateSceneModalContent({ navigation }) { const appContext = useContext(AppContext); const { t } = useTranslation(); @@ -38,12 +40,28 @@ export default function CreateSceneModalContent({ navigation, route }) { { - appContext.setDevices((arr) => { - const newArr = [...arr]; + appContext.setDeviceScenes((arr) => { + let newArr = [...arr]; - newArr[ - arr.findIndex((d) => d.id === route.params.id) - ].scenes.push(NewEmptyDeviceScene()); + const deviceId = AppSelectedUserDevice.current.id; + + const foundDeviceSceneIndex = newArr.findIndex( + (s) => s.deviceId === deviceId + ); + + const newEmptyDeviceScene = NewEmptyDeviceScene("Leere Szene"); + + if (foundDeviceSceneIndex === -1) { + const initalDeviceScene = NewInitialDeviceScene(deviceId); + + initalDeviceScene.scenes.push(newEmptyDeviceScene); + + newArr.push(initalDeviceScene); + } else { + newArr[foundDeviceSceneIndex].scenes.push( + newEmptyDeviceScene + ); + } return newArr; }); diff --git a/src/Screens/Device/modals/ChooseScene/index.js b/src/Screens/Device/modals/ChooseScene/index.js index 9bf9693..ea5c498 100644 --- a/src/Screens/Device/modals/ChooseScene/index.js +++ b/src/Screens/Device/modals/ChooseScene/index.js @@ -1,6 +1,10 @@ import { useContext } from "react"; import { FlatList } from "react-native"; -import { AppContext, ModalContainer } from "../../../../utils"; +import { + AppContext, + AppSelectedUserDevice, + ModalContainer, +} from "../../../../utils"; import { MyPickerModalListItem } from "../../../../Components/Modal"; import { useTranslation } from "react-i18next"; import { MyTextButton } from "../../../../Components/Button"; @@ -10,35 +14,47 @@ export default function ChooseSceneModalContent({ navigation, route }) { const appContext = useContext(AppContext); const { t } = useTranslation(); - const device = route.params; + const { device } = route.params; + + let scenes = []; + + const deviceScenes = appContext.deviceScenes.find( + (s) => s.deviceId === AppSelectedUserDevice.current.id + ); + + if (deviceScenes !== undefined) { + scenes = deviceScenes.scenes; + } return ( - {device.scenes.length === 0 ? ( + {scenes.length === 0 ? ( ) : ( item.id} renderItem={({ item }) => ( { + navigation.goBack(); + appContext.setDevices((arr) => { const newArr = [...arr]; newArr[ - arr.findIndex((d) => d.id === device.id) + arr.findIndex( + (d) => d.id === AppSelectedUserDevice.current.id + ) ].selectedScene = item.id; return newArr; }); - - navigation.goBack(); }} /> )} @@ -47,10 +63,8 @@ export default function ChooseSceneModalContent({ navigation, route }) { - navigation.navigate("modalCreateScene", { id: device.id }) - } + actionColor={!scenes.length} + onPress={() => navigation.navigate("modalCreateScene")} title={t("screens.device.scenes.modalChooseScene.textButtonAddScene")} /> diff --git a/src/Screens/Device/modals/EditActions/Lights/index.js b/src/Screens/Device/modals/EditActions/Lights/index.js index 1c731c5..12ce674 100644 --- a/src/Screens/Device/modals/EditActions/Lights/index.js +++ b/src/Screens/Device/modals/EditActions/Lights/index.js @@ -7,18 +7,20 @@ import { import Card from "../../../../../Components/Card"; import MyDropdown from "../../../../../Components/Dropdown"; import { useContext, useState } from "react"; -import { AppContext, ModalContainer } from "../../../../../utils"; -import MyModal, { - MyDefaultModalHeader, - MyPickerModalListItem, -} from "../../../../../Components/Modal"; +import { + AppContext, + GetDevice, + GetDeviceSceneAction, + ModalContainer, +} from "../../../../../utils"; +import { MyPickerModalListItem } from "../../../../../Components/Modal"; import { MyColorPickerV2 } from "../../../../../Components/ColorPicker"; export function LightsEditActionModalContent({ navigation, route }) { const appContext = useContext(AppContext); - const [modalColorModeSelectionIsOpen, setModalColorSelectionModeIsOpen] = - useState(false); - const [selectedLightMode, setSelectedLightMode] = useState(null); + //const [selectedLightModeId, setSelectedLightMode] = useState(null); + + const { actionId } = route.params; const supportedLightModes = appContext.deviceFirmwareModes.lightModes.filter( (lightMode) => @@ -27,6 +29,12 @@ export function LightsEditActionModalContent({ navigation, route }) { ) ); + const selectedLightModeId = GetDeviceSceneAction( + appContext.deviceSceneActions, + GetDevice(appContext.devices).selectedScene, + actionId + ).modeId; + return ( {appContext.isUserDeveloperModeEnabled ? ( @@ -47,10 +55,10 @@ export function LightsEditActionModalContent({ navigation, route }) { /> )} - + @@ -58,16 +66,16 @@ export function LightsEditActionModalContent({ navigation, route }) { s.id === selectedLightModeId) + .name[appContext.appLanguage] } onPress={() => navigation.navigate("modalLayersEditActionColorModeSelection", { supportedLightModes: supportedLightModes, - selectedLightMode: selectedLightMode, + selectedLightModeId: selectedLightModeId, + actionId: route.params.actionId, }) } /> @@ -99,9 +107,31 @@ export function LayersEditActionColorModeSelectionModalContent({ renderItem={({ item, index }) => ( { - // TODO: set selected light mode directly in appContext actions + const deviceScene = GetDevice(appContext.devices).selectedScene; + + appContext.setDeviceSceneActions((arr) => { + const newArr = [...arr]; + + const deviceActionIndex = newArr.findIndex( + (a) => a.sceneId === deviceScene + ); + + if (deviceActionIndex !== -1) { + const actionIndex = newArr[ + deviceActionIndex + ].actions.findIndex((a) => a.id === route.params.actionId); + + if (actionIndex !== -1) { + newArr[deviceActionIndex].actions[actionIndex].modeId = + item.id; + } + } + + return newArr; + }); + navigation.goBack(); }} /> diff --git a/src/Screens/Device/scene.js b/src/Screens/Device/scene.js index 8382059..2f4c9eb 100644 --- a/src/Screens/Device/scene.js +++ b/src/Screens/Device/scene.js @@ -1,19 +1,59 @@ import { FlatList, Text, TouchableOpacity, View } from "react-native"; import Card from "../../Components/Card"; -import { AppContext } from "../../utils"; -import { useContext } from "react"; +import { + AppContext, + AppSelectedUserDevice, + Constants, + GetDevice, +} from "../../utils"; +import { useCallback, useContext, useEffect, useState } from "react"; import MyDropdown from "../../Components/Dropdown"; import MyIcon from "../../Components/Icon"; import { useTranslation } from "react-i18next"; import MyResult from "../../Components/Result"; import { MyTextButton } from "../../Components/Button"; +import { useFocusEffect } from "@react-navigation/native"; -export default function SceneView({ navigation, device }) { +export default function SceneView({ navigation }) { const appContext = useContext(AppContext); const { t } = useTranslation(); - const deviceScene = device.scenes.find( - (scene) => scene.id === device.selectedScene + const [device, setDevice] = useState(); + + useFocusEffect( + useCallback(() => { + setDevice(GetDevice(appContext.devices)); + console.log("callback"); + }, [device]) + ); + + console.log("device", device !== undefined); + + if (device === undefined) return <>; + + const getActionTypeIcon = (actionType) => { + let iconName = ""; + + switch (actionType) { + case Constants.actionType.layers: + iconName = "lightbulb-on-outline"; + break; + case Constants.actionType.ambilight: + iconName = "television-ambient-light"; + break; + case Constants.actionType.motor: + iconName = "axis-z-rotate-counterclockwise"; + break; + default: + iconName = "help"; + break; + } + + return ; + }; + + const deviceSceneActions = appContext.deviceSceneActions.find( + (a) => a.sceneId === device.selectedScene ); return ( @@ -23,9 +63,7 @@ export default function SceneView({ navigation, device }) { label={t("screens.device.scenes.dropdownSceneSelection.label")} onPress={() => navigation.navigate("modalChooseScene", { - id: device.id, - selectedScene: device.selectedScene, - scenes: device.scenes, + device: { selectedScene: device.selectedScene }, }) } selectedItemLabel={ @@ -33,7 +71,9 @@ export default function SceneView({ navigation, device }) { ? t( "screens.device.scenes.dropdownSceneSelection.noSceneSelected" ) - : deviceScene.name + : appContext.deviceScenes + .find((s) => s.deviceId === AppSelectedUserDevice.current.id) + .scenes.find((s) => s.id === device.selectedScene).name } /> @@ -45,7 +85,8 @@ export default function SceneView({ navigation, device }) { /> ) : ( - {deviceScene.actions.length === 0 ? ( + {deviceSceneActions === undefined || + deviceSceneActions.actions.length === 0 ? ( item.id} - renderItem={({ item }) => ( - - - {item.name} - - navigation.navigate("modalLayersEditAction", { - deviceFirmwareVersion: device.firmware.version, - }) - } + renderItem={({ item }) => { + console.log("item", item); + + return ( + + - - - - - )} + {getActionTypeIcon(item.type)} + + Layer 1 wird auf rot setzen + Animation In: Fade in + Animation out: Fade out + + + navigation.navigate("modalLayersEditAction", { + actionId: item.id, + deviceFirmwareVersion: device.firmware.version, + }) + } + > + + + + + ); + }} /> )} navigation.navigate("modalAddSceneAction", { deviceFirmwareVersion: device.firmware.version, diff --git a/src/Screens/Device/settings.js b/src/Screens/Device/settings.js index 77e57c8..78647fa 100644 --- a/src/Screens/Device/settings.js +++ b/src/Screens/Device/settings.js @@ -1,18 +1,27 @@ import { Text, View } from "react-native"; import Card from "../../Components/Card"; import { useContext, useEffect, useState } from "react"; -import { AppContext, AppStyles, ModalContainer } from "../../utils"; +import { + AppContext, + AppSelectedUserDevice, + AppStyles, + ModalContainer, +} from "../../utils"; import { Divider } from "../../Components/Divider"; import { useTranslation } from "react-i18next"; import MySwitch from "../../Components/Switch"; import MyTextInput from "../../Components/TextInput"; import { MyIconButton } from "../../Components/Button"; -export default function SettingsView({ navigation, device }) { +export default function SettingsView({ navigation }) { const appContext = useContext(AppContext); const { t } = useTranslation(); const [switchState, setSwitchState] = useState(false); + const device = appContext.devices.find( + (d) => d.id === AppSelectedUserDevice.current.id + ); + return ( <> diff --git a/src/utils.js b/src/utils.js index 27729b2..6b3eb1e 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,5 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; -import { createContext, createRef, useState } from "react"; +import { createContext, createRef, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Appearance, @@ -22,6 +22,11 @@ export const Constants = { label: "English", }, ], + actionType: { + layers: 0, + ambilight: 1, + motor: 2, + }, }; export const AppStyles = StyleSheet.create({ @@ -178,20 +183,11 @@ export function GetUuid() { return uuid.v4(); } -const appContextPreview = { - appColorScheme: "", - appLanguage: "", - isUserExpertModeEnabled: "", - isUserDeveloperModeEnabled: "", - appTheme: DarkAppTheme, - devices: [], - deviceFirmwareModes: [], -}; - // data set in the app by default or provided by our servers on firmware update const devDevicesFirmwareModes = { lightModes: [ { + id: "6138e651-54c5-4fd4-8f40-3364f4e9dfe2", type: "solidColor", supportedFirmwareVersions: ["1.0.1"], name: { @@ -202,8 +198,9 @@ const devDevicesFirmwareModes = { adjustments: [], }, { + id: "c7097a60-faa9-4928-a531-0a8ecbc115d9", type: "multipleColors", - supportedFirmwareVersions: ["1.0.0"], + supportedFirmwareVersions: ["1.0.1"], name: { de: "Zufällig", en: "Random", @@ -225,6 +222,7 @@ const devDevicesFirmwareModes = { adjustments: [], }, { + id: "b42c665a-c2ab-4029-9162-2280caae3274", type: "multipleColors", supportedFirmwareVersions: ["1.0.1"], name: { @@ -245,6 +243,7 @@ const devDevicesFirmwareModes = { ], motorModes: [ { + type: "motor", name: { de: "Hin und her", en: "Back and forth", @@ -312,36 +311,18 @@ const devDevices = [ lastUpdated: "11.07.2023 um 20:33 Uhr", }, selectedScene: null, - scenes: [ + /*scenes: [ { id: "2f21a12a-0bec-4336-99bb-df3f9fc9f537", name: "Szene 1", - actions: [ - { - id: 0, - name: "Test", - }, - { - id: 1, - name: "Test1", - }, - ], + actions: [], }, { id: "3f21a12a-0bec-4336-99bb-df3f9fc9f537", name: "Szene 2", - actions: [ - { - id: 0, - name: "Haha", - }, - { - id: 1, - name: "Haha 1", - }, - ], + actions: [], }, - ], + ],*/ }, { id: "5b331a12a-0bec-4336-99bb-df3f9fc9f537", // deviceId @@ -352,30 +333,88 @@ const devDevices = [ lastUpdated: "11.07.2023 um 20:33 Uhr", }, selectedScene: null, + //scenes: [], + }, +]; + +const devDeviceScenes = [ + { + deviceId: "", scenes: [ { - id: "3f21a12a-0bec-4336-99bb-df3f9fc9f537", - name: "Yolo", - actions: [ - { - id: 0, - name: "Haha", - }, - ], + id: "", + name: "", }, ], }, ]; -export function NewEmptyDeviceScene() { +const devDeviceSceneActions = [ + { + sceneId: "", + actions: [ + { + id: "", + modeId: "", + }, + ], + }, +]; + +export function NewInitialDeviceScene(deviceId) { return { - id: GetUuid(), - name: "Leere Szene", + deviceId: deviceId, + scenes: [], + }; +} + +export function NewInitialDeviceSceneAction(sceneId) { + return { + sceneId: sceneId, actions: [], }; } -export const AppSelectedUserDevice = createRef(null); +export function NewEmptyDeviceScene(name) { + return { + id: GetUuid(), + name: name, + }; +} + +export function NewAction(actionType) { + return { + id: GetUuid(), + type: actionType, // layers, ambilight, motor + modeId: "", + }; +} + +export function GetDevice(devices) { + return devices.find((d) => d.id === AppSelectedUserDevice.current.id); +} + +export function GetDeviceSceneAction(sceneActions, sceneId, actionId) { + return sceneActions + .find((s) => s.sceneId === sceneId) + .actions.find((a) => a.id === actionId); +} + +const appContextPreview = { + appColorScheme: "", + appLanguage: "", + isUserExpertModeEnabled: "", + isUserDeveloperModeEnabled: "", + appTheme: DarkAppTheme, + devices: [], + deviceScenes: [], + deviceSceneActions: [], + deviceFirmwareModes: [], + userColorSwatchesFavorites: [], +}; + +export const AppSelectedUserDevice = createRef(); +AppSelectedUserDevice.current = { id: "", routeName: "" }; export const AppContext = createContext(appContextPreview); @@ -385,9 +424,14 @@ export function AppProvider({ children }) { const [appTheme, setAppTheme] = useState(DarkAppTheme); const [isUserExpertModeEnabled, setIsUserExpertModeEnabled] = useState(false); const [devices, setDevices] = useState(devDevices); + const [deviceScenes, setDeviceScenes] = useState([]); + const [deviceSceneActions, setDeviceSceneActions] = useState([]); const [deviceFirmwareModes, setDeviceFirmwareModes] = useState( devDevicesFirmwareModes ); + const [userColorSwatchesFavorites, setUserColorSwatchesFavorites] = useState( + [] + ); const { i18n } = useTranslation(); // TODO: only while development @@ -422,6 +466,11 @@ export function AppProvider({ children }) { setIsUserDeveloperModeEnabled(value); }; + const saveUserColorSwatchesFavorites = async (value) => { + StoreData("userColorSwatchesFavorites", value); + setUserColorSwatchesFavorites(value); + }; + return ( {children}