0

So I'm working on a news app. The layout is basically card based with where we can scroll to access the news.

There is a variable currentItem which keeps a track of the current index.

The problem is when I switch to a new index, the currentItem retains the same value as in the previous category and resumes reading the news array from that index only.

I tried setting the currentItem to 0 when category changes but this lead to broken layout of the app.

Newscard.js

import React, { useEffect, useState,useContext } from 'react'
import { View, StyleSheet, Text, Dimensions, Animated, Image, Alert } from 'react-native'
import AuthorInfo from './AuthorInfo'
import Icon from 'react-native-vector-icons/FontAwesome'
import { PanGestureHandler, State, TapGestureHandler } from 'react-native-gesture-handler'
import FullPageNews from '../pages/FullPageNews/FullPageNews'
import {useBetweenPages,BetweenTypes} from 'between-pages'
import newsContext from '../context/newsContext'
const ScreenWidth = Dimensions.get("window").width

//edit the function below to include only a certain count of characters to the string

function reduceStringToWordCount(inputString, wordCount) {
    const wordsArray = inputString.trim().split(/\s+/);
    const reducedWordsArray = wordsArray.slice(0, wordCount);
    const reducedString = reducedWordsArray.join(" ");
    return reducedString;
}

function reduceStringToCharacterCount(inputString, charCount) {
    if (inputString.length <= charCount) {
        return inputString;
    } else {
        const reducedString = inputString.slice(0, charCount);
        return reducedString;
    }
}


const Newscard = (props) => {
    const translateX = new Animated.Value(0);
    const [isDragging, setIsDragging] = React.useState(false);
    const [liked, setLiked] = useState(false);
    const [bookmarked, setBookmarked] = useState(false);
    const doubleTapRef = React.useRef(null);
    const context = useContext(newsContext);
    const {colors,currentItem} = context
    const {startTransition} = useBetweenPages(<FullPageNews context={context}/>)

    const handleToNewsPage = () => {
        startTransition(
            {
                type:BetweenTypes.SPRING,
                duration: 300,
                endAnimation:true
            },
            ()=>{
                props.navigation.navigate("FullPageNews");
            }
        );
    }

    return (
        ((props.index == currentItem) || (props.index == currentItem + 1)) &&
        <PanGestureHandler
            enabled={props.index == currentItem}
            maxPointers={1}
            onGestureEvent={
                Animated.event(
                    [
                        {
                            nativeEvent: {
                                translationX: translateX,
                            },
                        },
                    ],
                    {
                        useNativeDriver: true,
                    }
                )
            }

            onHandlerStateChange={(e) => {
                if (e.nativeEvent.state === State.BEGAN) {
                    setIsDragging(true);
                }

                if (e.nativeEvent.state === State.ACTIVE) {
                    console.log("done")

                }
                if (e.nativeEvent.state === State.END) {
                    console.log("end")
                    setIsDragging(false);
                    if (e.nativeEvent.translationX < -50) {
                        props.setActiveIndex(currentItem + 1);
                    }
                    else if (e.nativeEvent.translationX > 50) {
                        props.setActiveIndex(currentItem - 1);
                    }
                }
            }}

        >
            <Animated.View style={{
                backgroundColor: colors[props.index % 8],
                paddingHorizontal: 25,
                paddingBottom: 10,
                paddingTop: 20,
                width: ScreenWidth/1.15,
                //height:Dimensions.get("window").height/2.1,
                marginBottom: 20,
                borderRadius: 25,
                display: 'flex',
                flexDirection: 'column',
                position: 'absolute',
                zIndex: props.zIndex,
                left: "-42%",
                transform: [
                    { translateX: (isDragging) ? translateX : props.animationProps[0] },
                    { rotate: props.animationProps[1] },
                    { translateY: props.animationProps[2] },
                ],
                opacity: props.animationProps[3],
            }
            }>
                <TapGestureHandler
                    enabled={props.index == currentItem}
                    onHandlerStateChange={(e) => {
                        if (e.nativeEvent.state === State.END) {
                            props.navigation.navigate("FullPageNews");
                            //handleToNewsPage();
                        }
                    }
                    }

                    waitFor={doubleTapRef}
                >
                    <TapGestureHandler
                        enabled={props.index == currentItem}
                        numberOfTaps={2}
                        ref={doubleTapRef}
                        onHandlerStateChange={(e) => {
                            if (e.nativeEvent.state === State.ACTIVE) {
                                setLiked(!liked);
                            }
                        }
                    }
                    >
                    <View>
                        <Text style={{
                            fontFamily: "Manrope-Bold",
                            color: "black",
                            fontSize: 30,
                            lineHeight: 40
                        }}>
                            {(props.news.description !== null) ? reduceStringToWordCount(props.news.title, 10) + "..." : props.news.title}
                        </Text>
                        <Text style={{
                            marginTop: 20,
                            fontFamily: "Manrope-Regular",
                            color: "#0000008e",
                            fontSize: 15,
                        }}>
                            {props.news.publishedAt.split("T")[0]}
                        </Text>
                        <View>
                            <AuthorInfo author={props.news.source.name} reduceStringToCharacterCount={reduceStringToCharacterCount} />
                        </View>
                        {!(props.news.description === null || props.news.description == "") && <View style={{
                            marginTop: 20
                        }}>
                            <Text style={{
                                color: 'black',
                                fontSize: 15,
                                fontFamily: "Manrope-Medium",

                                lineHeight: 25
                            }}>
                                {reduceStringToWordCount(props.news.description, 30) + "..."}
                            </Text>
                        </View>}

                        <View style={{
                            marginTop: 50
                        }}>
                            <View style={{
                                position: 'relative',
                                alignSelf: "flex-end",
                                display: 'flex',
                                flexDirection: 'row'
                            }}>
                                <TapGestureHandler
                                    enabled={props.index == currentItem}
                                    onHandlerStateChange={(e) => {
                                        if (e.nativeEvent.state === State.ACTIVE) {
                                            setLiked(!liked);
                                        }
                                    }
                                    }
                                >
                                    <View style={styles.actionButtons}>
                                        <Icon name={liked ? "thumbs-up" : "thumbs-o-up"} size={18} color="black" />
                                    </View>
                                </TapGestureHandler>

                                <TapGestureHandler
                                    enabled={props.index == currentItem}
                                    onHandlerStateChange={(e) => {
                                        if (e.nativeEvent.state === State.ACTIVE) {
                                            setBookmarked(!bookmarked);
                                        }
                                    }
                                    }
                                >
                                <View style={styles.actionButtons}>
                                    <Icon name={bookmarked ? "bookmark" : "bookmark-o"} size={18} color="black" />
                                </View>
                                </TapGestureHandler>

                                <View style={styles.actionButtons}>
                                    <Icon name="share-square-o" size={18} color="black" />
                                </View>
                            </View>
                        </View>
                    </View>
                    </TapGestureHandler>
                </TapGestureHandler>
            </Animated.View>
            {/* </FlingGestureHandler> */}
        </PanGestureHandler>
    );
}


const styles = StyleSheet.create({
    actionButtons: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        borderWidth: 0.5,
        borderColor: 'black',
        borderStyle: 'solid',
        borderRadius: 50,
        width: 30,
        height: 30,
        marginLeft: 10
    }
})
export default Newscard

NewscardHolder.js

import React, { useState, useEffect, useContext } from 'react'
import newsContext from '../context/newsContext'
import Newscard from './Newscard'
import { Alert, Animated, Dimensions, View } from 'react-native'



const width = Dimensions.get("window").width
const height = Dimensions.get("window").height

const NewscardHolder = (props) => {
  const context = useContext(newsContext)
  const { fetchData,news,dataReady,category,colors,currentItem,setCurrentItem } = context;
  const scrollXIndex = React.useRef(new Animated.Value(0)).current;
  const scrollXAnimated = React.useRef(new Animated.Value(0)).current;
  // const [activeData, setActiveData] = useState([]);
  const [dataNeeded, setDataNeeded] = useState(false);


  console.log("Newscardholder component built")
  useEffect(() => {
    Animated.spring(scrollXAnimated, {
      toValue: scrollXIndex,
      useNativeDriver: true,
      speed: 20
    }).start();
  }, [])

  useEffect(() => {
    fetchData()
  }, [category])

  console.log(news.length + " NEws length")
  const setActiveIndex = React.useCallback((activeIndex) => {
    if (activeIndex == -1 || activeIndex == news.length) {
      return;
    }

    console.log(activeIndex)
    setCurrentItem(activeIndex)
    scrollXIndex.setValue(activeIndex);
  })
  return (
    <View style={
      {
        height: height/1.50
      }
    }>
      {
        dataReady && news.map((item, index) => {
          const inputRange = [index - 1, index, index + 1];
          const translateX = scrollXAnimated.interpolate({
            inputRange,
            outputRange: [70, 0, -width - 50],
          });
          const zIndex = news.length - index;
          const scale = scrollXAnimated.interpolate({
            inputRange,
            outputRange: [0.8, 1, 1],
          });

          const rotate = scrollXAnimated.interpolate({
            inputRange,
            outputRange: ['10deg', '0deg', '-10deg'],
          });

          const translateY = scrollXAnimated.interpolate({
            inputRange,
            outputRange: [20, 0, 0],
          });

          const opacity = scrollXAnimated.interpolate({
            inputRange,
            outputRange: [1 - 1 / 2, 1, 1],
          });
          return (
            <Newscard
              key={index}
              index={index}
              news={item}
              animationProps={[translateX, rotate, translateY, opacity]}
              zIndex={zIndex}
              setActiveIndex={setActiveIndex}
              navigation = {props.navigation}
            />

          )
        }
        )
      }
    </View>
  )
}

export default NewscardHolder

The GitHub repo is: https://github.com/amalthomas-exe/newsapp

1 Answers1

0

I am assuming your object "currentItem" is dependent on category state, whenever category is changed, "currentItem" should be set to zero. I hope I understood it correct. If yes then you can try one thing and change:

  useEffect(() => {
    fetchData()
  }, [category])

to

  useEffect(() => {
    fetchData()
    setCurrentState(0)
  }, [category])

Since, whenever with category change our CurrentState would also get updated

Sandeep Bisht
  • 116
  • 2
  • 11
  • I tried that. But that would lead to the animation being broken. The first card would animate as normal. But the second card, while being active, does not assume the styles of the active card. – Amal Thomas Aug 03 '23 at 01:15