Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Items return to original position after drag is complete #532

Open
MicahDavid opened this issue Apr 11, 2024 · 4 comments
Open

Items return to original position after drag is complete #532

MicahDavid opened this issue Apr 11, 2024 · 4 comments

Comments

@MicahDavid
Copy link

Describe the bug
After dragging an item to a new position, after a short period, the item returns to the original position.

Platform & Dependencies
react-native-draggable-flatlist version:
4.0.1

Platform:
iOS

React Native or Expo version:
0.72.7

Reanimated version:
3.6.1

React Native Gesture Handler version:
2.14.0

Simulator.Screen.Recording.-.iPhone.15.Pro.-.2024-04-11.at.12.54.42.mp4

My first guess was the list component was being re-rendered, but it is not.
Here's the basic code:

    const renderItem = ({item,drag}) => {
        return (
            item?.locationBeachId && (
                <ForecastItem
                    drag={drag}
                    key={item.locationBeachId}
                    item={item}
                    openDetails={handleShowDetail}
                />
            )
        )
    }

    return (
        <View>
            {
                favList && (
                        <DraggableFlatList
                            data={favList}
                            renderItem={renderItem}
                            keyExtractor={item => item?.locationBeachId}
                            onDragEnd={({ data, from, to }) => {
                                if (from !== to)
                                    reorderFaves(data)
                            }}
                        />
                )
            }
        </View>
    )

Any suggestions?

@zacharygameiro-ploutospay

it isn't clear. can you provide more code? does it always snap back or only some of the time?

@MicahDavid
Copy link
Author

it isn't clear. can you provide more code? does it always snap back or only some of the time?

Yes, it snaps back every time. Here is the entire component. Its worth noting, that this component lives inside a tab view, in case that has any relevance.

import React, {useState} from 'react'
import { connect } from 'react-redux'
import {FlatList, StyleSheet, Text} from 'react-native'
import { View } from 'native-base'
import { useNavigation } from '@react-navigation/native'

import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'

import Loading from '../../../components/Loading'
import { FavoriteWarning } from '../../../components/blanks'
import { ForecastItem } from '../../../components/items'
import { getUserUnits } from '../../../utils/localStorageUtil'

import locationActions from '../../../redux/locations/actions'
const { fetchForecastItemRequest} = locationActions

import { globalStyles } from "../../../assets/styles/globalStyles"

import DraggableFlatList, {ShadowDecorator} from 'react-native-draggable-flatlist'
import axios from "axios"
import {getEndpoint} from "../../../utils/urlHelper"
import {axiosHeader} from "../../../utils/authUtil"
import {AlertModal} from "../../../components/modals"

const FavouritesContent = (props) => {

    const navigation = useNavigation()
    const { forecastSlug, favList, fetchForecastItemRequest, isOffline, showErrorMsg } = props

    const handleShowDetail = async(locationSlug) => {
        if (!isOffline) {
            if (forecastSlug !== locationSlug) {
                let units = await getUserUnits()

                fetchForecastItemRequest({
                    slug: locationSlug,
                    units: units ? units : 1
                })
            }
            navigation.navigate('ForecastDetail')
        }
    }

    const reorderFaves = (data) => {
        let orderArray = []
        data.forEach(loc => {
            orderArray.push(loc.id)
        })

        axios.patch(getEndpoint('api/favorites/reorder'),
            {newOrder: orderArray},
            {headers: axiosHeader}
        ).then(function (response) {
            if (response.hasOwnProperty('data') && response.data.hasOwnProperty('errorMsg') && response.data.errorMsg)
                showErrorMsg(response.data.errorMsg)
        })
        .catch(function (error) {
            showErrorMsg('Error: '+error)
        })
    }

    const renderItem = ({item,drag,isActive}) => {
        return (
            item?.locationBeachId && (
                <ShadowDecorator>
                    <ForecastItem
                        drag={drag}
                        dragIsActive={isActive}
                        item={item}
                        openDetails={handleShowDetail}
                    />
                </ShadowDecorator>
            )
        )
    }

    return (
        <View style={{flexGrow:1}}>
            {
                favList && (
                    <View style={{position:'relative'}}>
                        <DraggableFlatList
                            data={favList}
                            renderItem={renderItem}
                            keyExtractor={item => item?.locationBeachId}
                            /*
                            onDragEnd={({ data, from, to }) => {
                                if (from !== to)
                                    reorderFaves(data)

                                //console.log('data',data)
                            }}
                             */
                            contentContainerStyle={{paddingBottom:40}}
                        />
                        <View style={styles.favReorderContainer}>
                            <Text style={styles.favReorderText}>Press and hold location, then drag to re-order.</Text>
                        </View>
                    </View>
                )
            }
        </View>
    )
}

const Favorites = (props) => {

    const { favList, fLoading, setShowSLocationModal } = props

    const [errorModal, setErrorModal] = useState({
        title: null,
        content: null,
        open: false
    })

    return (
        <View style={styles.container}>
            {
                fLoading === true && (
                    <Loading/>
                )
            }
            <View style={styles.mainContent}>
                {
                    (!favList || favList.length === 0) ?
                        <FavoriteWarning
                            setShowSLocationModal={setShowSLocationModal}
                        /> :
                        <FavouritesContent
                            favList={favList}
                            showErrorMsg={(message) => {
                                setErrorModal({
                                    title: 'Error',
                                    content: message,
                                    open: !!message
                                })
                            }}
                            {...props}
                        />
                }
            </View>

            <AlertModal
                data={errorModal}
                visible={errorModal.open}
                onClose={() =>
                    setErrorModal({
                        title: null,
                        content: null,
                        open: false
                    })
                }
            />
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor: globalStyles.colors.paleBlue,
    },
    mainContent: {
        marginTop: hp(1),
        marginBottom: hp(1),
        zIndex:1
    },
    mapView: {
        position: 'absolute',
        bottom: hp(3),
        right: wp(3)
    },
    favReorderContainer: {
        position:'absolute',
        bottom:0,
        left:0,
        width:'100%',
        backgroundColor: globalStyles.colors.paleBlue,
        paddingTop:10
    },
    favReorderText: {
        fontFamily:'Roboto',
        fontSize:14,
        color:globalStyles.colors.gray500,
        textAlign:'center',
    },
    errorMsgContainer: {
        backgroundColor:globalStyles.colors.warning500,
        borderRadius:4,
        alignItems:'center',
        marginHorizontal:10,
        marginVertical:8,
        flexBasis:'auto'
    },
    errorMsgText: {
        fontFamily:'Roboto-Medium',
        color:'#fff',
        fontSize:16,
        lineHeight:28
    }
})

const mapStateToProps = state => ({
    fLoading: state.locationReducer.favLoading,
    forecastSlug: state.locationReducer.forecastSlug,
    isOffline: state.authReducer.isOffline
})

const mapDispatchToProps = {
    fetchForecastItemRequest
}

export default connect(mapStateToProps, mapDispatchToProps)(Favorites)

Here is the rendered item component:

import React, {memo} from 'react'
import { connect } from 'react-redux'
import { useWindowDimensions, Pressable, Text } from 'react-native'
import { View, Icon } from 'native-base'
import HTML from 'react-native-render-html'

import { RFValue } from "react-native-responsive-fontsize"
import { forecastItemStyles } from "../../assets/styles/ForecastDayItem"

//import { WI_ICONS } from '../../assets/images/wx-icons'
import { globalStyles } from '../../assets/styles/globalStyles'
import WindIcon from '../../assets/images/icon/wind-arrow-summary.svg'
import {windArrowStyles} from "../../assets/styles/timeforecast"
import Ionicons from 'react-native-vector-icons/Ionicons'

const ForecastItem = (props) => {

    const {item, openDetails, drag, dragIsActive} = props

    const handleOpenDetails = () => {
        openDetails(item.locationBeachSlug)
    }

    const { width } = useWindowDimensions();

    //const WICON = item?.wxicon ? WI_ICONS[Object.keys(WI_ICONS).includes(item.wxicon) ? item.wxicon : 'none'] : WI_ICONS['none']

    return (
        <Pressable
            onLongPress={drag ? drag : null}
            disabled={dragIsActive ? dragIsActive : null}
            onPress={handleOpenDetails}
            style={({ pressed }) => [forecastItemStyles.container, {borderWidth: (dragIsActive ? 1 : 0),borderColor: globalStyles.colors.gray350, backgroundColor: dragIsActive || pressed ? globalStyles.colors.blue50 : 'white'}]}
        >
                <View style={forecastItemStyles.itemLeftPart}>
                    { /*
                    <View style={{flexDirect:'row'}}>
                        <View style={{flexDirection:'row',flex:1,flexWrap: 'wrap', justifyContent:'center'}}>
                            <WICON width={RFValue(30)} height={RFValue(30)} style={{...forecastItemStyles.itemWeatherIcon,width:30}} fill="#00111C" />
                            <Text style={{...forecastItemStyles.itemTempText,paddingLeft:2, fontSize:12}}>
                                {item?.weather.atmp + "\u00B0"}
                            </Text>
                        </View>
                    </View>
                    */
                    }
                    <View style={{flexDirect:'row', justifyContent:'center', alignItems: 'center'}}>
                        <WindIcon width={14} height={18}
                                  style={{ ...windArrowStyles[item?.weather?.wind_dir ? 'dir-'+(item.weather.wind_dir).toLowerCase() : 'dir-'], marginRight: 6}}
                        />
                        <Text style={{...forecastItemStyles.itemWindText, marginTop:16}}>{item?.weather.wind_dir+' '+item?.weather.wind_spd_digit}
                            <Text style={{fontSize:9}}>{item?.weather.wind_spd_unit}</Text>
                        </Text>
                    </View>
                </View>

                <View style={forecastItemStyles.itemRightPart}>
                    <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
                        <Text style={forecastItemStyles.itemTitle}>
                            {
                                item?.locationName && (
                                    <HTML
                                        defaultTextProps={{allowFontScaling:false}}
                                        contentWidth={width}
                                        source={{ html: `<div class="location-title">${item.locationName}</div>` }}
                                        classesStyles={{
                                            'location-title': {
                                                fontFamily: 'Roboto-Medium',
                                                color: globalStyles.colors.blue800,
                                                fontSize: 18,
                                            }
                                        }}
                                    />
                                )
                            }
                        </Text>
                        <Text style={forecastItemStyles.itemDistance}>

                        </Text>
                    </View>
                    <View style={forecastItemStyles.itemBottomBtnGroup}>
                        <View
                            style={[
                                forecastItemStyles.itemBtn,
                                item?.condAm ? item.condAm === 'choppy' ? forecastItemStyles.itemChoppyBtn : (item.condAm === 'fair' ? forecastItemStyles.itemFairBtn : (item.condAm === 'clean' ? forecastItemStyles.itemCleanBtn : {})) : {}
                            ]}
                        >
                            <Text style={forecastItemStyles.itemTimeBtnLText}>
                                AM
                            </Text>
                            <Text style={forecastItemStyles.itemTimeBtnRText}>
                                {item?.surfAm}
                            </Text>
                        </View>
                        <View
                            style={[
                                forecastItemStyles.itemBtn,
                                item?.condPm ? item.condPm === 'choppy' ? forecastItemStyles.itemChoppyBtn : (item.condPm === 'fair' ? forecastItemStyles.itemFairBtn : (item.condPm === 'clean' ? forecastItemStyles.itemCleanBtn : {})) : {}
                            ]}
                        >
                            <Text style={forecastItemStyles.itemTimeBtnLText}>
                                PM
                            </Text>
                            <Text style={forecastItemStyles.itemTimeBtnRText}>
                                {item?.surfPm}
                            </Text>
                        </View>
                        <View style={forecastItemStyles.itemDetails}>
                            <Icon
                                as={Ionicons}
                                name="chevron-forward"
                                size="lg"
                                style={forecastItemStyles.itemDetailsIcon}
                            />
                        </View>
                    </View>
                </View>
        </Pressable>
    )
}

export default memo(ForecastItem)

Thanks for any suggestions.

@MicahDavid
Copy link
Author

MicahDavid commented Apr 29, 2024

I got rid of everything except a simple test list, and still the draggable item returns to its original position.

Here is a test component that I rendered without anything else:

import React, {useState} from 'react'
import {Text, Pressable} from 'react-native'
import { View } from 'native-base'


import DraggableFlatList from 'react-native-draggable-flatlist'


const renderItem = ({item,drag,isActive}) => {
    return (
            <Pressable
                onLongPress={drag ? drag : null}
                disabled={isActive ? isActive : null}
            >
                <View style={{height:50,borderWidth:2,borderColor:'red'}}>
                    <Text>{item.name}</Text>
                </View>
            </Pressable>
    )
}

const Favorites = () => {

    //const { favList, fLoading, setShowSLocationModal } = props

    const favList = [
        {
            id: 1,
            name: 'A'
        },
        {
            id: 2,
            name: 'B'
        },
        {
            id: 3,
            name: 'C'
        }
    ]

    return (
        <View>
            {
                favList && (
                    <View>
                        <DraggableFlatList
                            data={favList}
                            renderItem={renderItem}
                            keyExtractor={item => item?.id}
                        />
                    </View>
                )
            }
        </View>
    )
}

 export default Favorites

Not sure what else to try.

@MicahDavid
Copy link
Author

I'm a colossal idiot. I somehow overlooked that I needed to set the data on the re-order function. Please accept my apology on behalf of my idiocy and for any waste of time. Hopefully if someone is as bit of an idiot as me, they will run across this thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants