fivecar / react-native-draglist

FlatList that can be reordered by dragging its items
MIT License
112 stars 20 forks source link

Bugging when dragging #46

Open MathiasSvDK opened 2 months ago

MathiasSvDK commented 2 months ago

The video speaks for itself. I have no idea what to say haha. I know the code is messy, its rough :)) The libary from computerjazz doesn't do it - the reason for using this is because the other libary for some reason won't work with updating data - which i do often and i don't want pull down to update.

Code: `// CustomComponent.js

import React, { useEffect, useState, useCallback } from 'react'; import { Pressable, ScrollView, StyleSheet, Text, View, Image, Dimensions, TouchableOpacity } from 'react-native'; import GlobalStyles from '../GlobalStyles'; import { useEntitiesContext } from '../EntitiesContext'; import HAService from '../Services/HAService'; import FontText from '../components/FontText'; import colors from '../colors'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; import { faHousePersonLeave } from '@fortawesome/pro-solid-svg-icons'; import { faChevronRight, faArrowUpRight, faShieldExclamation, faTemperature0, faHeat, faDroplet, faBolt, faCameraWeb } from '@fortawesome/pro-light-svg-icons'; import ClickAnimationComponent from '../components/ClickAnimationComponent'; import { useNavigation } from '@react-navigation/native'; import { BlurView } from 'expo-blur'; import { faShieldCheck } from '@fortawesome/pro-light-svg-icons'; import { faLock, faUnlock, faPlugCircleBolt, faPlugCircleXmark, faPlugCircleCheck, faPlug, faSun, faCog, faLockOpen } from '@fortawesome/pro-regular-svg-icons';

import DragList, { DragListRenderItemInfo } from 'react-native-draglist'; import as Haptics from 'expo-haptics'; import as SecureStore from "expo-secure-store"

const HomeView = () => {

convertMinutes = (minutes) => {
    // Calculate the number of hours
    const hours = Math.floor(minutes / 60);
    // Calculate the remaining minutes
    const remainingMinutes = minutes % 60;

    // Construct the result string
    let result = "";

    // Add hours to the result string if there are any
    if (hours > 0) {
        result += hours + "h" + (hours > 1 ? "s" : "");
    }

    // Add minutes to the result string if there are any
    if (remainingMinutes > 0) {
        if (hours > 0) {
            result += " ";
        }
        result += remainingMinutes + "m";
    }

    return result;
}

const width = Dimensions.get('window').width - 40; // - x is the padding of the page
const navigation = useNavigation();
const { entities, rooms, setRooms, url, homeName, user, unitSystem, scenes } = useEntitiesContext();

const [people, setPeople] = useState();
const [loggedInUser, setLoggedInUser] = useState();

useEffect(() => {
    let people = entities.filter(x => (x.type == "person" || x.entity_id == "device_tracker.broom_broom_route") && x.state != "unknown")
    let formatted = [];
    people.forEach(person => {
        person.picture = person.attributes.entity_picture ? url + person.attributes.entity_picture : null;
        formatted.push(person)
    });
    setPeople(people);

    let clone = rooms;
    clone.forEach(room => {
        if (room.humidity) {
            room.humidity = entities.find(x => x.entity_id == room.humidity.entity_id);
        }
        if (room.temperature) {
            room.temperature = entities.find(x => x.entity_id == room.temperature.entity_id);
        }
        let climateEntities = entities.filter(x => x.type == "climate" && x.area == room.area_id);
        console.log("climateEntities " + room.area_id)
        console.log(climateEntities)
        if (climateEntities.length > 0) {
            console.log("SET")
            room.climate = {
                min: climateEntities.reduce((min, e) => e.attributes.temperature < min.attributes.temperature ? e : min).attributes.temperature,
                max: climateEntities.reduce((max, e) => e.attributes.temperature > max.attributes.temperature ? e : max).attributes.temperature
            }
        }
    });
    setRooms(clone);

}, [entities, rooms])

useEffect(() => {
    if (people?.length > 0) {
        setLoggedInUser(people.find(x => x.entity_id.toLowerCase().replace("person.", "") == user.toLowerCase()))
    }
}, [people])

const formatNumber = num => (typeof num === 'undefined') ? 'Undefined' : (Number.isInteger(num) ? num.toString() : num.toFixed(1));

let personSize = 40;
let personLocationSize = 12;
let personSpacing = personSize / 3;
let pfpSize = 70;

let imageHeight = 70;

const getGreeting = () => {
    const currentHour = new Date().getHours();
    if (currentHour < 12) {
        return 'Good morning';
    } else if (currentHour < 18) {
        return 'Good Afternoon';
    } else {
        return 'Good Evening';
    }
};

async function onReordered(fromIndex, toIndex) {
    const copy = [...data]; // Don't modify react data in-place
    const removed = copy.splice(fromIndex, 1);

    copy.splice(toIndex, 0, removed[0]); // Now insert at the new pos
    setRooms(copy);
}

function keyExtractor(str) {
    return str;
}

const renderItem = (info) => {
    const { item, onDragStart, onDragEnd, isActive } = info;
    let room = item;
    return (
        <TouchableOpacity
            style={{
                width: width / 1 - 10,
                borderRadius: colors.radius, flexDirection: "column", alignItems: "center", justifyContent: "space-between",
                position: "relative", height: width / 3.25 - 10, overflow: "hidden",
                marginBottom: 10
            }}
            onPressIn={onDragStart}
            onPressOut={onDragEnd}
        >

            <View style={{
                position: "absolute", top: 15, left: 20, width: width - 10, zIndex: 3,
                flexDirection: "row", paddingRight: 10, alignItems: "center"
            }}>
                <FontText style={{ color: "white" }} weight={500} size={20}>{room.name}</FontText>
            </View>

            <View style={{
                position: "absolute", bottom: 10, left: 0, width: width - 10, zIndex: 3,
                flexDirection: "row", paddingLeft: 10, paddingRight: 10, alignItems: "center",
                gap: 10
            }}>

                <BlurView intensity={3} style={{
                    borderRadius: colors.radius, overflow: "hidden", flexDirection: "column",
                    padding: 5, paddingLeft: 10, paddingRight: 10, borderRadius: 360
                }}>
                    <FontText light size={12}>Temp</FontText>
                    {room.temperature?.state ?
                        <FontText size={16} weight={500} style={{ color: "white" }}>
                            {formatNumber(parseFloat(room.temperature.state))}
                            {unitSystem?.temperature}
                        </FontText>
                        :
                        <FontText size={16} weight={500} style={{ color: "white" }}>
                            -
                        </FontText>
                    }
                </BlurView>

                <BlurView intensity={3} style={{
                    borderRadius: colors.radius, overflow: "hidden", flexDirection: "column",
                    padding: 5, paddingLeft: 10, paddingRight: 10, borderRadius: 360
                }}>
                    <FontText light size={12}>Hum</FontText>
                    {room.humidity?.state ?
                        <FontText size={16} weight={500} style={{ color: "white" }}>
                            {formatNumber(parseFloat(room.humidity.state))}
                            %
                        </FontText>
                        :
                        <FontText size={16} weight={500} style={{ color: "white" }}>
                            -
                        </FontText>
                    }
                </BlurView>

                {room.climate &&
                    <BlurView intensity={3} style={{
                        borderRadius: colors.radius, overflow: "hidden", flexDirection: "column",
                        padding: 5, paddingLeft: 10, paddingRight: 10, borderRadius: 360
                    }}>
                        <FontText light size={12}>Climate</FontText>
                        {room?.climate?.min == room?.climate?.max ?
                            <FontText size={16} weight={500} style={{ color: "white" }}>
                                {room.climate.min}
                                {unitSystem?.temperature}
                            </FontText>
                            :
                            <FontText size={16} weight={500} style={{ color: "white" }}>
                                {room.climate.min}-{room.climate.max}
                                {unitSystem?.temperature}
                            </FontText>
                        }
                    </BlurView>
                }

            </View>

            <Image
                width={width / 1 - 10}
                height={width / 2 - 10}
                style={{ objectFit: "cover", height: width / 3 - 10, width: width / 1 - 10, borderRadius: colors.radius }}
                source={{
                    uri: room.picture
                }}
            />
            <View style={{
                position: "absolute",
                top: 0, left: 0,
                width: width / 1 - 10,
                height: width / 3 - 10,
                backgroundColor: "rgba(0,0,0,.4)",
                zIndex: 2
            }}></View>

        </TouchableOpacity>
    );
}

const handleDragStart = (drag, data) => {
    drag(data);
    Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
}
const handleDragEnd = (data) => {
    setRooms(data);
    SecureStore.setItem("roomOrder", data.map(item => item.key).toString())
}

return (
    <>
        <View style={[GlobalStyles.page, { flexDirection: "column", gap: 20 }]}>

            <View>

                <View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
                    <FontText>{getGreeting()},</FontText>
                    <FontText>
                        {new Date().toLocaleDateString('da-DK', { day: 'numeric', month: 'long' })}
                    </FontText>
                </View>

                <View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginTop: 7 }}>
                    <FontText weight={500} size={30}>{user} 👋</FontText>
                    <View style={{ backgroundColor: colors.light, padding: 8, borderRadius: colors.radius }}>
                        <FontAwesomeIcon icon={faCog} size={15} color={colors.text} />
                    </View>
                </View>
            </View>

            <View style={{ flexDirection: "column", gap: 10 }}>

                <View style={{ flexDirection: "row", gap: 10 }}>
                    <View style={{ width: width / 2 - 5, borderRadius: colors.radius, padding: 10, height: 60, backgroundColor: colors.light, flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
                        <Image
                            width={32}
                            height={24}
                            style={{ objectFit: "contain", marginRight: 5, height: 24, width: 32, borderRadius: colors.radius }}
                            source={require('../assets/porsche2.png')}
                        />
                        <View style={{ paddingRight: 10 }}>
                            <View style={{ flexDirection: "row", justifyContent: "space-between" }}>
                                <FontText weight={500} size={16}>100%</FontText>
                            </View>
                            <FontText size={12} light>Porsche Taycan</FontText>
                        </View>
                        <FontAwesomeIcon icon={faLockOpen} color={colors.red} />
                    </View>

                    <View style={{ width: width / 2 - 5, gap: 20, overflow: "hidden", borderRadius: colors.radius, padding: 10, height: 60, backgroundColor: colors.light, flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
                        <View>
                            <View style={{ flexDirection: "row", justifyContent: "space-between" }}>
                                <FontText weight={500} size={16}>Family</FontText>
                            </View>
                            <FontText size={12} light>{people?.length} people</FontText>
                        </View>
                        <View style={{ flexDirection: "row" }}>
                            {people?.filter(x => x != loggedInUser).slice(0, 2).map((person, index) => (
                                <Image
                                    width={32}
                                    height={32}
                                    style={{ objectFit: "contain", marginLeft: (index > 0 ? index * -1 - 13 : 0), height: 32, width: 32, borderRadius: 360 }}
                                    source={{
                                        uri: person.picture
                                    }}
                                />
                            ))}
                        </View>
                    </View>
                </View>

                <View style={{ flexDirection: "row", gap: 10 }}>
                    <ScrollView horizontal contentContainerStyle={{ gap: 10 }}>
                        {scenes.map((scene) => (
                            <View style={{ borderRadius: colors.radius, padding: 10, height: 60, backgroundColor: colors.light, flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
                                <Icon name={scene.attributes.icon?.replace("mdi:", "")} size={25} style={[GlobalStyles.text, { paddingRight: 10 }]} />
                                <View>
                                    <FontText>{scene.attributes.friendly_name.length > 15 ? scene.attributes.friendly_name.slice(0, 15) + "..." : scene.attributes.friendly_name}</FontText>
                                </View>
                            </View>
                        ))}
                    </ScrollView>
                </View>
            </View>

            <View style={{ flex: 1 }}>
                <View style={{ flexDirection: "row", gap: 5 }}>
                    <FontText size={24} weight={500}>Rooms</FontText>
                </View>
                <DragList
                    data={rooms}
                    keyExtractor={(item) => keyExtractor(item.key)}
                    onReordered={onReordered}
                    renderItem={renderItem}
                />
            </View >
        </View >
    </>
);

};

function getPastelColor(index) { const pastelColors = ["#FFB6C1", "#FFC0CB", "#FF69B4", "#FFA07A", "#FA8072", "#FF6347", "#FFE4E1", "#FFDAB9", "#FFF5EE", "#F0FFF0", "#F5FFFA", "#B0E0E6", "#ADD8E6", "#87CEEB", "#87CEFA", "#00BFFF", "#B0C4DE", "#1E90FF", "#6495ED", "#4682B4", "#5F9EA0", "#AFEEEE", "#00CED1", "#20B2AA", "#48D1CC", "#40E0D0", "#00FFFF", "#E0FFFF", "#7FFFD4", "#66CDAA", "#00FA9A", "#F5FFFA", "#00FF7F", "#3CB371", "#2E8B57", "#F0FFF0", "#90EE90", "#98FB98", "#8FBC8F", "#32CD32", "#00FF00", "#228B22", "#008000", "#006400", "#7FFF00", "#7CFC00", "#ADFF2F", "#556B2F", "#9ACD32"]; return pastelColors[index] }

https://github.com/user-attachments/assets/79ac25db-83cb-4699-98e0-870391100d2b

export default HomeView; `

fivecar commented 3 weeks ago

@MathiasSvDK : I'm not sure what you mean when you say the video speaks for itself... since the video doesn't show where your fingers are dragging, could you describe in words what the problem is?