update scene name

main
alex 2023-08-01 21:20:40 +00:00
parent 3dd7afec41
commit 8bb1da8d00
15 changed files with 273 additions and 119 deletions

18
App.js
View File

@ -32,8 +32,9 @@ import {
SettingsAppColorSchemeModalContent, SettingsAppColorSchemeModalContent,
SettingsAppLanguageModalContent, SettingsAppLanguageModalContent,
} from "./src/Screens/Settings"; } from "./src/Screens/Settings";
import { SettingsChangeDeviceDisplayName } from "./src/Screens/Device/settings";
import { EditActionAnimationSelectionModalContent } from "./src/Screens/Device/modals/EditActions"; import { EditActionAnimationSelectionModalContent } from "./src/Screens/Device/modals/EditActions";
import UpdateSceneNameModalContent from "./src/Screens/Device/modals/UpdateSceneName";
import SettingsChangeDeviceDisplayNameModalContent from "./src/Screens/Device/modals/SettingsChangeDeviceDisplayName";
const Drawer = createDrawerNavigator(); const Drawer = createDrawerNavigator();
const Stack = createStackNavigator(); const Stack = createStackNavigator();
@ -224,6 +225,19 @@ export function MyApp() {
} }
/> />
<Stack.Screen
name="modalUpdateSceneName"
component={UpdateSceneNameModalContent}
options={({ navigation }) =>
options({
navigation: navigation,
pageTitle: t(
"screens.device.scenes.modalUpdateSceneName.pageTitle"
),
})
}
/>
<Stack.Screen <Stack.Screen
name="modalSettingsAppColorScheme" name="modalSettingsAppColorScheme"
component={SettingsAppColorSchemeModalContent} component={SettingsAppColorSchemeModalContent}
@ -248,7 +262,7 @@ export function MyApp() {
<Stack.Screen <Stack.Screen
name="modalSettingsChangeDeviceDisplayName" name="modalSettingsChangeDeviceDisplayName"
component={SettingsChangeDeviceDisplayName} component={SettingsChangeDeviceDisplayNameModalContent}
options={({ navigation }) => options={({ navigation }) =>
options({ options({
navigation: navigation, navigation: navigation,

View File

@ -22,7 +22,7 @@
"deviceFirmwareVersionText": "Firmware Version", "deviceFirmwareVersionText": "Firmware Version",
"deviceLastUpdatedText": "Letzte Aktualisierung", "deviceLastUpdatedText": "Letzte Aktualisierung",
"modalSettingsChangeDeviceDisplayName": { "modalSettingsChangeDeviceDisplayName": {
"pageTitle": "Gerätename anpassen", "pageTitle": "Gerätename ändern",
"textTitle": "Gerätename", "textTitle": "Gerätename",
"textDescription": "Der Name wird lokal auf dem Gerät gespeichert und dient dazu, die verbundenen Geräte in der App voneinander zu unterscheiden." "textDescription": "Der Name wird lokal auf dem Gerät gespeichert und dient dazu, die verbundenen Geräte in der App voneinander zu unterscheiden."
} }
@ -93,6 +93,11 @@
"modalEditActionAnimationOutSelection": { "modalEditActionAnimationOutSelection": {
"pageTitle": "Animation out auswählen", "pageTitle": "Animation out auswählen",
"noResult": "Keine Animationen verfügbar" "noResult": "Keine Animationen verfügbar"
},
"modalUpdateSceneName": {
"pageTitle": "Szenennamen ändern",
"textTitle": "Szenenname",
"textDescription": "Benenne die Szene, um sie später leichter wiederzufinden."
} }
} }
}, },
@ -106,9 +111,9 @@
"pageTitle": "Einstellungen", "pageTitle": "Einstellungen",
"settingsCardTitle": "Einstellungen", "settingsCardTitle": "Einstellungen",
"languageText": "Sprache", "languageText": "Sprache",
"languageModalTitle": "Sprache anpassen", "languageModalTitle": "Sprache ändern",
"appColorSchemeText": "Anzeigemodus", "appColorSchemeText": "Anzeigemodus",
"appColorSchemeModalTitle": "Anzeigemodus anpassen", "appColorSchemeModalTitle": "Anzeigemodus ändern",
"appColorSchemePicker": { "appColorSchemePicker": {
"auto": "System Standard", "auto": "System Standard",
"dark": "Dunkel", "dark": "Dunkel",

View File

@ -93,6 +93,11 @@
"modalEditActionAnimationOutSelection": { "modalEditActionAnimationOutSelection": {
"pageTitle": "Choose animation out", "pageTitle": "Choose animation out",
"noResult": "No animations available" "noResult": "No animations available"
},
"modalUpdateSceneName": {
"pageTitle": "Change scene name",
"textTitle": "Scene name",
"textDescription": "Give the scene a name to easily find it later"
} }
} }
}, },

View File

@ -35,6 +35,7 @@ export default function MyDropdown({
{selectedItemLabel} {selectedItemLabel}
</Text> </Text>
</View> </View>
<MyIcon <MyIcon
name="chevron-down" name="chevron-down"
size={24} size={24}

View File

@ -1,24 +1,18 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { AppContext, AppStyles, IsPlatformIos } from "../../utils"; import { AppContext, AppStyles, ModalContainer } from "../../utils";
import { import { Text, TouchableOpacity, View } from "react-native";
KeyboardAvoidingView,
Modal,
ScrollView,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
import { Divider } from "../Divider"; import { Divider } from "../Divider";
import MyIcon from "../Icon"; import MyIcon from "../Icon";
import { useTranslation } from "react-i18next"; import MyTextInput from "../TextInput";
import { MyIconButton } from "../Button";
const modalContentStyle = { margin: 10, paddingTop: 10 }; //const modalContentStyle = { margin: 10, paddingTop: 10 };
/* /*
transparent: set this to prevent modal reopening on iOS transparent: set this to prevent modal reopening on iOS
https://github.com/facebook/react-native/issues/34018 https://github.com/facebook/react-native/issues/34018
*/ */
/*
export default function MyModal({ export default function MyModal({
children, children,
isOpen, isOpen,
@ -107,14 +101,19 @@ export function MyDefaultModalHeader({ title, closeModal }) {
<View style={{ marginRight: 40 }} /> <View style={{ marginRight: 40 }} />
</View> </View>
); );
} } */
export function MyPickerModalListItem({ onPress, itemName, itemSelected }) { export function MyPickerModalListItem({
onPress,
itemName,
itemSelected,
disabled,
}) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
return ( return (
<> <>
<TouchableOpacity onPress={onPress}> <TouchableOpacity onPress={onPress} disabled={disabled}>
<View <View
style={{ style={{
flexDirection: "row", flexDirection: "row",
@ -127,6 +126,7 @@ export function MyPickerModalListItem({ onPress, itemName, itemSelected }) {
style={[ style={[
AppStyles.typography16, AppStyles.typography16,
{ color: appContext.appTheme.text }, { color: appContext.appTheme.text },
disabled && AppStyles.disabled,
]} ]}
> >
{itemName} {itemName}
@ -147,6 +147,48 @@ export function MyPickerModalListItem({ onPress, itemName, itemSelected }) {
); );
} }
export function MyTextInputModalContent({
navigation,
defaultValue,
onCheckIconPress,
textInputTitle,
textInputDescription,
textMaxLength,
}) {
const [newValue, setNewValue] = useState(defaultValue);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<MyIconButton
usePrimaryColor
iconName="check"
iconSize={24}
style={{ marginRight: 20 }}
onPress={() => {
navigation.goBack();
onCheckIconPress(newValue);
}}
disabled={newValue.length === 0}
/>
),
});
}, [navigation, newValue]);
return (
<ModalContainer>
<MyTextInput
textTitle={textInputTitle}
textDescription={textInputDescription}
value={newValue}
onChangeText={(v) => setNewValue(v)}
textMaxLength={textMaxLength}
/>
</ModalContainer>
);
}
/*
export function MyPickerModal({ isOpen, setIsOpen, items, searchFilter }) { export function MyPickerModal({ isOpen, setIsOpen, items, searchFilter }) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
@ -317,8 +359,8 @@ export function MyPickerModal({ isOpen, setIsOpen, items, searchFilter }) {
</KeyboardAvoidingView> </KeyboardAvoidingView>
</Modal> </Modal>
); );
} }*/
/*
export function MyTextInputModal({ export function MyTextInputModal({
isOpen, isOpen,
setIsOpen, setIsOpen,
@ -402,3 +444,4 @@ export function MyTextInputModal({
</Modal> </Modal>
); );
} }
*/

View File

@ -23,6 +23,7 @@ export default function MySlider({
minimumTrackTintColor={appContext.appTheme.slider.minimumTrackTintColor} minimumTrackTintColor={appContext.appTheme.slider.minimumTrackTintColor}
maximumTrackTintColor={appContext.appTheme.slider.maximumTrackTintColor} maximumTrackTintColor={appContext.appTheme.slider.maximumTrackTintColor}
thumbTintColor={appContext.appTheme.slider.thumbTintColor} thumbTintColor={appContext.appTheme.slider.thumbTintColor}
step={1}
/> />
); );
} }

View File

@ -5,6 +5,7 @@ import { useContext } from "react";
export default function MyTextInput({ export default function MyTextInput({
textTitle, textTitle,
textDescription, textDescription,
textMaxLength,
value, value,
onChangeText, onChangeText,
}) { }) {
@ -22,6 +23,7 @@ export default function MyTextInput({
borderBottomWidth: 1, borderBottomWidth: 1,
borderColor: appContext.appTheme.textInputBottomColor, borderColor: appContext.appTheme.textInputBottomColor,
}} }}
maxLength={textMaxLength}
value={value} value={value}
onChangeText={onChangeText} onChangeText={onChangeText}
/> />

View File

@ -1,13 +1,5 @@
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { import { Image, ScrollView, StyleSheet, View, Text, Alert } from "react-native";
Image,
ScrollView,
StyleSheet,
View,
Text,
KeyboardAvoidingView,
Alert,
} from "react-native";
import { AppContext } from "../../utils"; import { AppContext } from "../../utils";
import SettingsView from "./settings"; import SettingsView from "./settings";
import SceneView from "./scene"; import SceneView from "./scene";

View File

@ -3,8 +3,7 @@ import {
AppContext, AppContext,
AppSelectedUserDevice, AppSelectedUserDevice,
ModalContainer, ModalContainer,
NewEmptyDeviceScene, NewDeviceScene,
NewInitialDeviceScene,
} from "../../../../../utils"; } from "../../../../../utils";
import { FlatList } from "react-native"; import { FlatList } from "react-native";
import { MyPickerModalListItem } from "../../../../../Components/Modal"; import { MyPickerModalListItem } from "../../../../../Components/Modal";
@ -39,13 +38,33 @@ export default function CreateSceneModalContent({ navigation }) {
renderItem={({ item }) => ( renderItem={({ item }) => (
<MyPickerModalListItem <MyPickerModalListItem
itemName={item.name} itemName={item.name}
disabled={item.id === 2}
onPress={() => { onPress={() => {
appContext.setDeviceScenes((arr) => [ if (item.id === 0 || item.id === 1) {
...arr, const newDeviceScene = NewDeviceScene(
NewEmptyDeviceScene("Leere Szene"), item.id === 0 ? "Leere Szene" : "Standard Szene"
]); );
navigation.pop(2); appContext.setDeviceScenes((arr) => [...arr, newDeviceScene]);
navigation.pop(2);
appContext.setDevices((arr) => {
let newArr = [...arr];
const deviceIndex = newArr.findIndex(
(device) => device.id === AppSelectedUserDevice.current.id
);
if (deviceIndex !== -1) {
newArr[deviceIndex].selectedScene = newDeviceScene.sceneId;
}
return newArr;
});
} else {
console.log("copy a scene");
}
}} }}
/> />
)} )}

View File

@ -7,7 +7,7 @@ import {
ModalContainer, ModalContainer,
} from "../../../../utils"; } from "../../../../utils";
import MyDropdown from "../../../../Components/Dropdown"; import MyDropdown from "../../../../Components/Dropdown";
import { useContext } from "react"; import { useContext, useState } from "react";
import MyResult from "../../../../Components/Result"; import MyResult from "../../../../Components/Result";
import { MyPickerModalListItem } from "../../../../Components/Modal"; import { MyPickerModalListItem } from "../../../../Components/Modal";
import MyIcon from "../../../../Components/Icon"; import MyIcon from "../../../../Components/Icon";
@ -40,7 +40,12 @@ export default function EditActionAnimationsCardContent({
return ( return (
<> <>
<Card> <Card>
<Text style={[AppStyles.typography14, { fontWeight: "bold", color: appContext.appTheme.text }]}> <Text
style={[
AppStyles.typography14,
{ fontWeight: "bold", color: appContext.appTheme.text },
]}
>
{t( {t(
"screens.device.scenes.editActionAnimationsCardContent.animationsIn" "screens.device.scenes.editActionAnimationsCardContent.animationsIn"
)} )}
@ -75,7 +80,14 @@ export default function EditActionAnimationsCardContent({
))} ))}
<Text <Text
style={[AppStyles.typography14, { fontWeight: "bold", marginTop: 6, color: appContext.appTheme.text }]} style={[
AppStyles.typography14,
{
fontWeight: "bold",
marginTop: 6,
color: appContext.appTheme.text,
},
]}
> >
{t( {t(
"screens.device.scenes.editActionAnimationsCardContent.animationsOut" "screens.device.scenes.editActionAnimationsCardContent.animationsOut"
@ -195,6 +207,8 @@ export function EditActionAdjustmentContent({
appThemeText, appThemeText,
appLanguage, appLanguage,
}) { }) {
const [sliderValue, setSliderValue] = useState(0);
if (adjustment.type === "slider") { if (adjustment.type === "slider") {
return ( return (
<> <>
@ -209,10 +223,19 @@ export function EditActionAdjustmentContent({
style={[{ flex: 1 }, IsPlatformIos() && { marginLeft: 6 }]} style={[{ flex: 1 }, IsPlatformIos() && { marginLeft: 6 }]}
minimumValue={adjustment.min} minimumValue={adjustment.min}
maximumValue={adjustment.max} maximumValue={adjustment.max}
onValueChange={(v) => onValueChange={(v) => {
console.log(`change ${adjustment.variableName}`, v) console.log(`change ${adjustment.variableName}`, v);
} setSliderValue(v);
}}
value={sliderValue}
/> />
<View style={{ width: 32, alignItems: "center" }}>
<Text style={{ color: appThemeText }}>
{sliderValue}
{adjustment.unitOfMeasurement}
</Text>
</View>
</View> </View>
</> </>
); );

View File

@ -0,0 +1,39 @@
import { useContext } from "react";
import { AppContext, Constants } from "../../../../utils";
import { useTranslation } from "react-i18next";
import { MyTextInputModalContent } from "../../../../Components/Modal";
export default function SettingsChangeDeviceDisplayNameModalContent({
navigation,
route,
}) {
const appContext = useContext(AppContext);
const { t } = useTranslation();
return (
<MyTextInputModalContent
navigation={navigation}
textMaxLength={Constants.globals.max_device_name_length}
defaultValue={route.params.displayName}
onCheckIconPress={(newDeviceDisplayName) => {
appContext.setDevices((arr) => {
let newArr = [...arr];
const foundIndex = newArr.findIndex((d) => d.id === route.params.id);
if (foundIndex !== -1) {
newArr[foundIndex].displayName = newDeviceDisplayName;
}
return newArr;
});
}}
textInputTitle={t(
"screens.device.settings.modalSettingsChangeDeviceDisplayName.textTitle"
)}
textInputDescription={t(
"screens.device.settings.modalSettingsChangeDeviceDisplayName.textDescription"
)}
/>
);
}

View File

@ -0,0 +1,38 @@
import { useContext } from "react";
import { AppContext, Constants } from "../../../../utils";
import { MyTextInputModalContent } from "../../../../Components/Modal";
import { useTranslation } from "react-i18next";
export default function UpdateSceneNameModalContent({ navigation, route }) {
const appContext = useContext(AppContext);
const { t } = useTranslation();
const { deviceSelectedScene, sceneName } = route.params;
return (
<MyTextInputModalContent
navigation={navigation}
textMaxLength={Constants.globals.max_scene_name_length}
defaultValue={sceneName}
textInputTitle={t("screens.device.scenes.modalUpdateSceneName.textTitle")}
textInputDescription={t(
"screens.device.scenes.modalUpdateSceneName.textDescription"
)}
onCheckIconPress={(newValue) => {
appContext.setDeviceScenes((arr) => {
let newArr = [...arr];
const foundSceneIndex = newArr.findIndex(
(scene) => scene.sceneId === deviceSelectedScene
);
if (foundSceneIndex !== -1) {
newArr[foundSceneIndex].name = newValue;
}
return newArr;
});
}}
/>
);
}

View File

@ -6,7 +6,7 @@ import MyDropdown from "../../Components/Dropdown";
import MyIcon from "../../Components/Icon"; import MyIcon from "../../Components/Icon";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import MyResult from "../../Components/Result"; import MyResult from "../../Components/Result";
import { MyTextButton } from "../../Components/Button"; import { MyIconButton, MyTextButton } from "../../Components/Button";
import { useFocusEffect } from "@react-navigation/native"; import { useFocusEffect } from "@react-navigation/native";
export default function SceneView({ navigation }) { export default function SceneView({ navigation }) {
@ -15,15 +15,40 @@ export default function SceneView({ navigation }) {
const [device, setDevice] = useState(); const [device, setDevice] = useState();
const getSelectedDeviceSceneName = () => {
const scenes = appContext.deviceScenes.find(
(s) => s.sceneId === device.selectedScene
);
return scenes === undefined ? "None" : scenes.name;
};
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
setDevice(GetDevice(appContext.devices)); setDevice(GetDevice(appContext.devices));
console.log("callback");
if (device !== undefined) {
if (device.selectedScene !== null) {
navigation.setOptions({
headerRight: () => (
<MyIconButton
iconName="pencil"
iconSize={22}
style={{ marginRight: 20 }}
onPress={() =>
navigation.navigate("modalUpdateSceneName", {
deviceSelectedScene: device.selectedScene,
sceneName: getSelectedDeviceSceneName(),
})
}
/>
),
});
}
}
}, [device]) }, [device])
); );
console.log("device", device !== undefined);
if (device === undefined) return <></>; if (device === undefined) return <></>;
const getActionTypeIcon = (actionType) => { const getActionTypeIcon = (actionType) => {
@ -51,14 +76,6 @@ export default function SceneView({ navigation }) {
(a) => a.sceneId === device.selectedScene (a) => a.sceneId === device.selectedScene
); );
const getSelectedDeviceScene = () => {
const scenes = appContext.deviceScenes.find(
(s) => s.sceneId === device.selectedScene
);
return scenes === undefined ? "None" : scenes.name;
};
return ( return (
<> <>
<Card> <Card>
@ -74,7 +91,7 @@ export default function SceneView({ navigation }) {
? t( ? t(
"screens.device.scenes.dropdownSceneSelection.noSceneSelected" "screens.device.scenes.dropdownSceneSelection.noSceneSelected"
) )
: getSelectedDeviceScene() : getSelectedDeviceSceneName()
} }
/> />
</Card> </Card>

View File

@ -1,16 +1,10 @@
import { Text, View } from "react-native"; import { Text, View } from "react-native";
import Card from "../../Components/Card"; import Card from "../../Components/Card";
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { import { AppContext, AppSelectedUserDevice, AppStyles } from "../../utils";
AppContext,
AppSelectedUserDevice,
AppStyles,
ModalContainer,
} 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 MyTextInput from "../../Components/TextInput";
import { MyIconButton } from "../../Components/Button"; import { MyIconButton } from "../../Components/Button";
export default function SettingsView({ navigation }) { export default function SettingsView({ navigation }) {
@ -22,6 +16,12 @@ export default function SettingsView({ navigation }) {
(d) => d.id === AppSelectedUserDevice.current.id (d) => d.id === AppSelectedUserDevice.current.id
); );
useEffect(() => {
navigation.setOptions({
headerRight: null,
});
}, []);
return ( return (
<> <>
<Card> <Card>
@ -176,57 +176,3 @@ export default function SettingsView({ navigation }) {
</> </>
); );
} }
export function SettingsChangeDeviceDisplayName({ navigation, route }) {
const appContext = useContext(AppContext);
const { t } = useTranslation();
const [newDeviceDisplayName, setNewDeviceDisplayName] = useState(
route.params.displayName
);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<MyIconButton
usePrimaryColor
iconName="check"
iconSize={24}
style={{ marginRight: 20 }}
onPress={() => {
navigation.goBack();
appContext.setDevices((arr) => {
let newArr = [...arr];
const foundIndex = newArr.findIndex(
(d) => d.id === route.params.id
);
if (foundIndex !== -1) {
newArr[foundIndex].displayName = newDeviceDisplayName;
}
return newArr;
});
}}
disabled={newDeviceDisplayName.length === 0}
/>
),
});
}, [navigation, newDeviceDisplayName]);
return (
<ModalContainer>
<MyTextInput
textTitle={t(
"screens.device.settings.modalSettingsChangeDeviceDisplayName.textTitle"
)}
textDescription={t(
"screens.device.settings.modalSettingsChangeDeviceDisplayName.textDescription"
)}
value={newDeviceDisplayName}
onChangeText={(v) => setNewDeviceDisplayName(v)}
/>
</ModalContainer>
);
}

View File

@ -27,6 +27,10 @@ export const Constants = {
ambilight: 1, ambilight: 1,
motor: 2, motor: 2,
}, },
globals: {
max_device_name_length: 20,
max_scene_name_length: 20,
},
}; };
export const AppStyles = StyleSheet.create({ export const AppStyles = StyleSheet.create({
@ -220,6 +224,7 @@ const devDevicesFirmwareModes = {
}, },
min: 0, min: 0,
max: 6, max: 6,
unitOfMeasurement: "s",
}, },
], ],
}, },
@ -242,6 +247,7 @@ const devDevicesFirmwareModes = {
}, },
min: 0, min: 0,
max: 100, max: 100,
unitOfMeasurement: "s",
}, },
{ {
type: "slider", type: "slider",
@ -253,6 +259,7 @@ const devDevicesFirmwareModes = {
}, },
min: 1, min: 1,
max: 10, max: 10,
unitOfMeasurement: "s",
}, },
], ],
}, },
@ -285,6 +292,7 @@ const devDevicesFirmwareModes = {
iconName: "repeat-variant", iconName: "repeat-variant",
min: 1, min: 1,
max: 60, max: 60,
unitOfMeasurement: "s",
}, },
], ],
}, },
@ -317,6 +325,7 @@ const devDevicesFirmwareModes = {
iconName: "repeat-variant", iconName: "repeat-variant",
min: 1, min: 1,
max: 60, max: 60,
unitOfMeasurement: "s",
}, },
], ],
}, },
@ -370,7 +379,7 @@ const devDeviceScenes = [
}, },
]; ];
export function NewEmptyDeviceScene(name) { export function NewDeviceScene(name) {
return { return {
sceneId: GetUuid(), sceneId: GetUuid(),
deviceId: AppSelectedUserDevice.current.id, deviceId: AppSelectedUserDevice.current.id,