added dots modal

main
alex 2023-08-04 00:32:38 +00:00
parent c74be48e2b
commit 8edf96043a
9 changed files with 416 additions and 205 deletions

View File

@ -96,7 +96,7 @@
"modalUpdateSceneName": { "modalUpdateSceneName": {
"pageTitle": "Change scene name", "pageTitle": "Change scene name",
"textTitle": "Scene name", "textTitle": "Scene name",
"textDescription": "Give the scene a name to easily find it later" "textDescription": "Give the scene a name to easily find it later."
} }
} }
}, },

View File

@ -31,7 +31,14 @@ export default function MyButton({ title, style, disabled, onPress }) {
); );
} }
export function MyTextButton({ title, style, onPress, actionColor }) { export function MyTextButton({
title,
styleContainer,
style,
onPress,
actionColor,
iconName,
}) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
const color = actionColor const color = actionColor
@ -39,6 +46,7 @@ export function MyTextButton({ title, style, onPress, actionColor }) {
: appContext.appTheme.textSecondary; : appContext.appTheme.textSecondary;
return ( return (
<View style={styleContainer}>
<TouchableOpacity onPress={onPress}> <TouchableOpacity onPress={onPress}>
<View <View
style={[ style={[
@ -51,16 +59,19 @@ export function MyTextButton({ title, style, onPress, actionColor }) {
]} ]}
> >
<MyIcon <MyIcon
name="plus-circle-outline" name={iconName}
size={24} size={24}
style={{ style={{
marginRight: 10, marginRight: 10,
color: color, color: color,
}} }}
/> />
<Text style={[{ color: color }, AppStyles.typography16]}>{title}</Text> <Text style={[{ color: color }, AppStyles.typography16]}>
{title}
</Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</View>
); );
} }

View File

@ -20,7 +20,7 @@ import ColorPicker, {
} from "reanimated-color-picker"; } from "reanimated-color-picker";
import MyIcon from "../Icon"; import MyIcon from "../Icon";
import { AppContext, AppStyles, VibrateShort } from "../../utils"; import { AppContext, AppStyles, VibrateShort } from "../../utils";
/*
function ColorLayer({ layerNumber, sharedColor, selected, onPress }) { function ColorLayer({ layerNumber, sharedColor, selected, onPress }) {
const backgroundColorStyle = useAnimatedStyle(() => { const backgroundColorStyle = useAnimatedStyle(() => {
return { return {
@ -112,7 +112,7 @@ function ColorLayer({ layerNumber, sharedColor, selected, onPress }) {
</TouchableHighlight> </TouchableHighlight>
); );
} }
/*
export default function MyColorPicker({ colorLayer }) { export default function MyColorPicker({ colorLayer }) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
const [selectedGlasLayer, setSelectedGlasLayer] = useState(1); const [selectedGlasLayer, setSelectedGlasLayer] = useState(1);
@ -247,7 +247,7 @@ export default function MyColorPicker({ colorLayer }) {
</ColorPicker> </ColorPicker>
</Animated.View> </Animated.View>
); );
} } */
export function MyColorPickerV2({ export function MyColorPickerV2({
pickerRef, pickerRef,
@ -342,43 +342,9 @@ export function MyColorPickerV2({
VibrateShort(); VibrateShort();
}} }}
> >
{disabled ? ( <View>
<> <MyColorSwatch size={28} backgroundColor={color} />
<View </View>
style={{
position: "absolute",
zIndex: 2,
backgroundColor: appContext.appTheme.colorPickerDisabled,
width: 28,
height: 28,
borderRadius: 14,
}}
/>
<View
style={[
{
backgroundColor: color,
width: 28,
height: 28,
borderRadius: 16,
},
AppStyles.Shadow,
]}
/>
</>
) : (
<View
style={[
{
backgroundColor: color,
width: 28,
height: 28,
borderRadius: 16,
},
AppStyles.Shadow,
]}
/>
)}
</TouchableHighlight> </TouchableHighlight>
))} ))}
@ -436,6 +402,23 @@ export function MyColorPickerV2({
); );
} }
export function MyColorSwatch({ size, backgroundColor, style }) {
return (
<View
style={[
{
backgroundColor: backgroundColor,
width: size,
height: size,
borderRadius: 16,
},
AppStyles.Shadow,
style
]}
/>
);
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
previewTxtContainer: { previewTxtContainer: {
paddingTop: 20, paddingTop: 20,

View File

@ -1,10 +1,19 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { AppContext, AppStyles, ModalContainer } from "../../utils"; import { AppContext, AppStyles, ModalContainer } from "../../utils";
import { Pressable, Text, View } from "react-native"; import {
Modal,
Pressable,
SafeAreaView,
Text,
TouchableWithoutFeedback,
View,
} from "react-native";
import MyIcon from "../Icon"; import MyIcon from "../Icon";
import MyTextInput from "../TextInput"; import MyTextInput from "../TextInput";
import { MyIconButton } from "../Button"; import { MyIconButton } from "../Button";
import { Divider } from "../Divider"; import { Divider } from "../Divider";
import MyPressable from "../Pressable";
import { FlatList } from "react-native-gesture-handler";
//const modalContentStyle = { margin: 10, paddingTop: 10 }; //const modalContentStyle = { margin: 10, paddingTop: 10 };
@ -103,6 +112,93 @@ export function MyDefaultModalHeader({ title, closeModal }) {
); );
} */ } */
export function MyDotsModal({ isOpen, closeModal, data }) {
const appContext = useContext(AppContext);
const listItemStyle = (isFirst, isLast) => {
return {
borderTopLeftRadius: isFirst ? 10 : 0,
borderTopRightRadius: isFirst ? 10 : 0,
borderBottomLeftRadius: isLast ? 10 : 0,
borderBottomRightRadius: isLast ? 10 : 0,
};
};
const handleBackdropPress = (e) => {
const x = e.nativeEvent.locationX;
const y = e.nativeEvent.locationY;
const topLeftX = 0;
const topLeftY = 0;
const bottomRightX = 200; // width
const bottomRightY = 52; // height of item
if (x < topLeftX || x > bottomRightX || y < topLeftY || y > bottomRightY) {
closeModal();
}
};
return (
<Modal
visible={isOpen}
transparent
style={{ flex: 1 }}
onRequestClose={() => closeModal()}
>
<SafeAreaView style={{ flex: 1 }}>
<Pressable onPress={handleBackdropPress} style={{ flex: 1 }}>
<View
style={[
{
marginTop: 10,
backgroundColor: appContext.appTheme.card.backgroundColor,
width: 200,
borderRadius: 10,
alignSelf: "flex-end",
},
AppStyles.Shadow,
]}
>
<FlatList
data={data}
renderItem={({ item, index }) => (
<MyPressable
key={index}
style={listItemStyle(index === 0, index === data.length - 1)}
backgroundColor={appContext.appTheme.card.backgroundColor}
onPress={() => {
closeModal();
item.onPress();
}}
disabled={item.disabled}
>
{console.log("dis", item.disabled)}
<View
style={[
{
flexDirection: "row",
alignItems: "center",
gap: 10,
padding: 16,
},
item.disabled && AppStyles.disabled,
]}
>
<MyIcon name={item.icon} size={24} />
<Text style={{ color: appContext.appTheme.text }}>
{item.label}
</Text>
</View>
</MyPressable>
)}
/>
</View>
</Pressable>
</SafeAreaView>
</Modal>
);
}
export function MyPickerModalListItem({ export function MyPickerModalListItem({
onPress, onPress,
itemName, itemName,
@ -112,18 +208,7 @@ export function MyPickerModalListItem({
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
return ( return (
<> <MyPressable onPress={onPress} disabled={disabled}>
<Pressable
onPress={onPress}
disabled={disabled}
style={({ pressed }) => [
{
backgroundColor: pressed
? appContext.appTheme.modal.pressedPickerItemColor
: appContext.appTheme.backgroundColor,
},
]}
>
<View <View
style={{ style={{
flexDirection: "row", flexDirection: "row",
@ -157,8 +242,7 @@ export function MyPickerModalListItem({
)} )}
</View> </View>
<Divider withoutMarginVertical /> <Divider withoutMarginVertical />
</Pressable> </MyPressable>
</>
); );
} }

View File

@ -0,0 +1,32 @@
import { useContext } from "react";
import { Pressable } from "react-native";
import { AppContext } from "../../utils";
export default function MyPressable({
children,
onPress,
disabled,
style,
backgroundColor,
}) {
const appContext = useContext(AppContext);
return (
<Pressable
onPress={onPress}
disabled={disabled}
style={({ pressed }) => [
{
backgroundColor: pressed
? appContext.appTheme.modal.pressedPickerItemColor
: backgroundColor === undefined
? appContext.appTheme.backgroundColor
: backgroundColor,
},
style,
]}
>
{children}
</Pressable>
);
}

View File

@ -1,10 +1,11 @@
import { useContext } from "react"; import { useContext } from "react";
import { Image, Pressable, ScrollView, Text, View } from "react-native"; import { Image, ScrollView, Text, View } from "react-native";
import { AppContext, AppStyles, ModalContainer } from "../../../../utils"; import { AppContext, AppStyles, ModalContainer } from "../../../../utils";
import MyIcon from "../../../../Components/Icon"; import MyIcon from "../../../../Components/Icon";
import { Divider } from "../../../../Components/Divider"; import { Divider } from "../../../../Components/Divider";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import MyTag from "../../../../Components/Tag"; import MyTag from "../../../../Components/Tag";
import MyPressable from "../../../../Components/Pressable";
function Action({ function Action({
text, text,
@ -27,17 +28,7 @@ function Action({
return ( return (
<> <>
<Pressable <MyPressable onPress={onPress} disabled={!deviceFirmwareVersionSupported}>
onPress={onPress}
disabled={!deviceFirmwareVersionSupported}
style={({ pressed }) => [
{
backgroundColor: pressed
? appContext.appTheme.modal.pressedPickerItemColor
: appContext.appTheme.backgroundColor,
},
]}
>
<View <View
style={[ style={[
{ {
@ -69,7 +60,7 @@ function Action({
style={{ height: 150, width: 150, marginLeft: 12 }} style={{ height: 150, width: 150, marginLeft: 12 }}
/> />
)} )}
</Pressable> </MyPressable>
<Divider withoutMarginVertical /> <Divider withoutMarginVertical />
</> </>

View File

@ -1,5 +1,5 @@
import { useContext } from "react"; import { useContext } from "react";
import { FlatList } from "react-native"; import { FlatList, View } from "react-native";
import { import {
AppContext, AppContext,
AppSelectedUserDevice, AppSelectedUserDevice,
@ -22,14 +22,14 @@ export default function ChooseSceneModalContent({ navigation, route }) {
return ( return (
<ModalContainer withoutPadding> <ModalContainer withoutPadding>
{deviceScenes.length === 0 ? ( <FlatList
data={deviceScenes}
ListEmptyComponent={
<MyResult <MyResult
text={t("screens.device.scenes.modalChooseScene.noResult")} text={t("screens.device.scenes.modalChooseScene.noResult")}
iconName={"selection-search"} iconName={"selection-search"}
/> />
) : ( }
<FlatList
data={deviceScenes}
keyExtractor={(item) => item.sceneId} keyExtractor={(item) => item.sceneId}
renderItem={({ item }) => ( renderItem={({ item }) => (
<MyPickerModalListItem <MyPickerModalListItem
@ -52,14 +52,21 @@ export default function ChooseSceneModalContent({ navigation, route }) {
}} }}
/> />
)} )}
/> ListFooterComponent={
)}
<MyTextButton <MyTextButton
style={{ marginTop: 10 }} styleContainer={{
alignItems: "center",
marginTop: 10,
}}
style={{ padding: 8 }}
actionColor={!deviceScenes.length} actionColor={!deviceScenes.length}
onPress={() => navigation.navigate("modalCreateScene")} onPress={() => navigation.navigate("modalCreateScene")}
title={t("screens.device.scenes.modalChooseScene.textButtonAddScene")} title={t(
"screens.device.scenes.modalChooseScene.textButtonAddScene"
)}
iconName="plus-circle-outline"
/>
}
/> />
</ModalContainer> </ModalContainer>
); );

View File

@ -1,6 +1,12 @@
import { FlatList, Text, TouchableOpacity, View } from "react-native"; import { FlatList, Text, TouchableOpacity, View } from "react-native";
import Card from "../../Components/Card"; import Card from "../../Components/Card";
import { AppContext, AppStyles, Constants, GetDevice } from "../../utils"; import {
AppContext,
AppSelectedUserDevice,
AppStyles,
Constants,
GetDevice,
} from "../../utils";
import { useCallback, useContext, useState } from "react"; import { useCallback, useContext, useState } from "react";
import MyDropdown from "../../Components/Dropdown"; import MyDropdown from "../../Components/Dropdown";
import MyIcon from "../../Components/Icon"; import MyIcon from "../../Components/Icon";
@ -8,19 +14,29 @@ import { useTranslation } from "react-i18next";
import MyResult from "../../Components/Result"; import MyResult from "../../Components/Result";
import { MyIconButton, MyTextButton } from "../../Components/Button"; import { MyIconButton, MyTextButton } from "../../Components/Button";
import { useFocusEffect } from "@react-navigation/native"; import { useFocusEffect } from "@react-navigation/native";
import { Divider } from "../../Components/Divider";
import { MyColorSwatch } from "../../Components/ColorPicker";
import { MyDotsModal } from "../../Components/Modal";
export default function SceneView({ navigation }) { export default function SceneView({ navigation }) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const [device, setDevice] = useState(); const [device, setDevice] = useState();
const [isDotsModalOpen, setIsDotsModalOpen] = useState(false);
const getSelectedDeviceSceneName = () => { const getSelectedDeviceSceneName = () => {
const scenes = appContext.deviceScenes.find( let scene;
if (device.selectedScene !== "") {
scene = appContext.deviceScenes.find(
(s) => s.sceneId === device.selectedScene (s) => s.sceneId === device.selectedScene
); );
}
return scenes === undefined ? "None" : scenes.name; return scene === undefined
? t("screens.device.scenes.dropdownSceneSelection.noSceneSelected")
: scene.name;
}; };
useFocusEffect( useFocusEffect(
@ -32,15 +48,10 @@ export default function SceneView({ navigation }) {
navigation.setOptions({ navigation.setOptions({
headerRight: () => ( headerRight: () => (
<MyIconButton <MyIconButton
iconName="pencil" iconName="dots-vertical"
iconSize={22} iconSize={24}
style={AppStyles.headerNavigationIcons} style={AppStyles.headerNavigationIcons}
onPress={() => onPress={() => setIsDotsModalOpen(true)}
navigation.navigate("modalUpdateSceneName", {
deviceSelectedScene: device.selectedScene,
sceneName: getSelectedDeviceSceneName(),
})
}
/> />
), ),
}); });
@ -86,17 +97,11 @@ export default function SceneView({ navigation }) {
device: { selectedScene: device.selectedScene }, device: { selectedScene: device.selectedScene },
}) })
} }
selectedItemLabel={ selectedItemLabel={getSelectedDeviceSceneName()}
device.selectedScene === null
? t(
"screens.device.scenes.dropdownSceneSelection.noSceneSelected"
)
: getSelectedDeviceSceneName()
}
/> />
</Card> </Card>
{device.selectedScene === null ? ( {device.selectedScene === "" ? (
<MyResult <MyResult
text={t("screens.device.scenes.infoNoSceneSelected")} text={t("screens.device.scenes.infoNoSceneSelected")}
iconName={"selection-search"} iconName={"selection-search"}
@ -118,19 +123,7 @@ export default function SceneView({ navigation }) {
console.log("item", item); console.log("item", item);
return ( return (
<Card cardBackgroundColor={"#e67e22"}> <View style={{ marginLeft: 10, marginRight: 10 }}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
{getActionTypeIcon(item.type)}
<View>
<Text>Layer 1 wird auf rot setzen</Text>
<Text>Animation In: Fade in</Text>
<Text>Animation out: Fade out</Text>
</View>
<TouchableOpacity <TouchableOpacity
onPress={() => onPress={() =>
navigation.navigate("modalLayersEditAction", { navigation.navigate("modalLayersEditAction", {
@ -139,10 +132,63 @@ export default function SceneView({ navigation }) {
}) })
} }
> >
<MyIcon name="pencil" size={24} /> <View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<View
style={{ flexDirection: "row", alignItems: "center" }}
>
{getActionTypeIcon(item.type)}
<View style={{ marginLeft: 12 }}>
<Text
style={{
color: appContext.appTheme.text,
}}
>
Rainbow
</Text>
<View
style={{
flexDirection: "row",
alignItems: "center",
}}
>
<Text
style={{
color: appContext.appTheme.textSecondary,
}}
>
Set to
</Text>
<MyColorSwatch
style={{ marginLeft: 6 }}
size={16}
backgroundColor={"red"}
/>
<MyColorSwatch
size={16}
backgroundColor={"orange"}
/>
<MyColorSwatch
size={16}
backgroundColor={"blue"}
/>
</View>
</View>
</View>
<MyIcon name="menu" size={20} />
</View>
<Divider />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</Card>
); );
}} }}
/> />
@ -150,7 +196,11 @@ export default function SceneView({ navigation }) {
<MyTextButton <MyTextButton
title={t("screens.device.scenes.buttonAddAction")} title={t("screens.device.scenes.buttonAddAction")}
style={{ marginTop: 10 }} styleContainer={{
alignItems: "center",
marginTop: 10,
}}
style={{ padding: 8 }}
actionColor={ actionColor={
deviceSceneActions === undefined || deviceSceneActions === undefined ||
deviceSceneActions.length === 0 deviceSceneActions.length === 0
@ -160,9 +210,62 @@ export default function SceneView({ navigation }) {
deviceFirmwareVersion: device.firmware.version, deviceFirmwareVersion: device.firmware.version,
}) })
} }
iconName="plus-circle-outline"
/> />
</View> </View>
)} )}
<MyDotsModal
isOpen={isDotsModalOpen}
closeModal={() => setIsDotsModalOpen(false)}
data={[
{
icon: "pencil",
label: "Change scene name",
onPress: () =>
navigation.navigate("modalUpdateSceneName", {
deviceSelectedScene: device.selectedScene,
sceneName: getSelectedDeviceSceneName(),
}),
},
{
icon: "trash-can",
label: "Delete scene",
disabled: GetDevice(appContext.devices).selectedScene === "",
onPress: () => {
appContext.setDeviceScenes((scenes) =>
scenes.filter(
(scene) =>
scene.sceneId !==
GetDevice(appContext.devices).selectedScene
)
);
appContext.setDevices((arr) => {
const newArr = [...arr];
const foundDeviceIndex = arr.findIndex(
(d) => d.id === AppSelectedUserDevice.current.id
);
if (foundDeviceIndex !== -1) {
newArr[foundDeviceIndex].selectedScene = "";
appContext.setDeviceSceneActions((actions) =>
actions.filter(
(action) =>
action.sceneId !==
newArr[foundDeviceIndex].selectedScene
)
);
}
return newArr;
});
},
},
]}
/>
</> </>
); );
} }

View File

@ -363,7 +363,7 @@ const devDevices = [
version: "1.0.1", version: "1.0.1",
lastUpdated: "11.07.2023 um 20:33 Uhr", lastUpdated: "11.07.2023 um 20:33 Uhr",
}, },
selectedScene: null, selectedScene: "",
}, },
{ {
id: "5b331a12a-0bec-4336-99bb-df3f9fc9f537", // deviceId id: "5b331a12a-0bec-4336-99bb-df3f9fc9f537", // deviceId
@ -373,7 +373,7 @@ const devDevices = [
version: "1.0.1", version: "1.0.1",
lastUpdated: "11.07.2023 um 20:33 Uhr", lastUpdated: "11.07.2023 um 20:33 Uhr",
}, },
selectedScene: null, selectedScene: "",
}, },
]; ];