Приращение свойства Redux не работает, если вызов через дочерний компонент - PullRequest
1 голос
/ 17 апреля 2020

У меня есть плоский список изображений, видео. для изображений я определил длительность показа, а затем перешел к следующему элементу в плоском списке, но в случае видео после окончания видео перейдите к следующему элементу.

Я использую Redux для источника данных в настоящее время PlaylayIndex и flatlist. Если у меня есть только изображения в плоском списке, он работает нормально, но если у меня есть видео, в конце видео мне нужно передать событие от дочернего к его родителю. Родитель вызывает тот же метод для перехода к следующему индексу, что и для окончания длительности изображения, но в случае окончания видео приращения значения indexPlayingIndex на единицу не происходит. Код родительского компонента или обработчик плоского списка

 import React, { Component } from 'react'
import { TouchableWithoutFeedback, View,StatusBar,Dimensions,FlatList } from 'react-native'
import {FileType,getFileType,getFileExtension} from '../services/FileManagerService'
import VideoPlayer from '../components/PlayerTypes/VideoPlayer'
import ImagePlayer from '../components/PlayerTypes/ImagePlayer'
import PdfPlayer from '../components/PlayerTypes/PdfPlayer'
import { ScaledSheet } from 'react-native-size-matters';

import NothingTpPlay from '../components/PlayerTypes/NothingTpPlay'
//redux
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../database/actions/ScheduleActions.js'; //Import your actions
import * as Animatable from 'react-native-animatable';
import constants from '../config/constants'
import KeepAwake from 'react-native-keep-awake';
import AudioPlayer from '../components/PlayerTypes/AudioPlayer'
import { showToastMessage } from '../utils/ToastMessage'
import I18n from "../locales/i18n-js";
import WebsitePlayer from '../components/PlayerTypes/WebsitePlayer'
import FullScreen from "../NativeBridgingHeader/FullScreen";

let deviceWidth = Dimensions.get('window').width
let deviceHeight = Dimensions.get('window').height


 class PlaylistPlayerScreen extends Component {
    constructor() {
        super();
        this.state = {
          currentVisibleIndex:-1
        }
      this.playNextFile = this.playNextFile.bind(this)
      this.videoEnded = this.videoEnded.bind(this)
        this.schedulePlayDurationTimer = this.schedulePlayDurationTimer.bind(this)
        this.viewabilityConfig = {
          waitForInteraction: false,
          itemVisiblePercentThreshold: 99,
        }
      }

    static navigationOptions = {
        header: null,
    };

    onViewableItemsChanged = ({ viewableItems }) => {
      // viewableItems will show you what items are in view
      // console.log("onViewableItemsChanged called" + JSON.stringify(viewableItems))
       if(viewableItems.length >= 1) {
         const visibleFileIndex = viewableItems[0].index
        //  console.log("visible index " + visibleFileIndex)
        this.setState({currentVisibleIndex:visibleFileIndex}) 
        const file = this.props.schedulesFiles[visibleFileIndex]
         const fileType = getFileType(file)
         console.log("file type is " + fileType)
         if (fileType == FileType.Video) {
           console.log("video file type")
         } else {
           this.schedulePlayDurationTimer(visibleFileIndex)
         }
       }
    }

    getItemLayout = (data, index) => ({
      length: deviceWidth,
      offset: deviceWidth * index,
      index,
    })

    componentDidMount(){
      this.props.getScheduleFiles()  
    }

    shouldComponentUpdate(nextProps, nextState) {
      return true
    }

    componentDidUpdate(){
      console.log("componentDidUpdate")
    }


    schedulePlayDurationTimer(file_index) {
      const file = this.props.schedulesFiles[file_index]
      const playDuration = file.play_duration_in_milliseconds
      this.timer = setTimeout(() => {
        clearTimeout(this.timer)
        this.playNextFile()
      }, playDuration);
    }


   videoEnded = () => {
     console.log("video ended")
     this.playNextFile()
   }

    playNextFile = () => {
      if(this.props.currentlyPlayingIndex == (this.props.schedulesFiles.length - 1)) {
        //last file played
        this.props.getScheduleFiles() 
        this.props.playNextFile(this.props.schedulesFiles,this.props.currentlyPlayingIndex)
        this.listRef.scrollToIndex({animated: false, index: this.props.currentlyPlayingIndex})

      } else {
        console.log("playNextFile current index " + this.props.currentlyPlayingIndex)
        this.props.playNextFile(this.props.schedulesFiles,this.props.currentlyPlayingIndex)
        console.log("playNextFile next index " + this.props.currentlyPlayingIndex)
        this.listRef.scrollToIndex({animated: true, index: this.props.currentlyPlayingIndex})
      }
    }

    _renderItem = ({item, index}) => {
      return (
        this.renderPlayer(item,index)
      );
    }

    renderPlayer(file,index) {    
        switch (getFileType(file)) {
          case FileType.Video:
            return <VideoPlayer file={file}  onEnd={this.videoEnded} currentIndex={index} currentVisibleIndex={this.state.currentVisibleIndex} />
            case FileType.Audio:
              return <AudioPlayer file={file} onEnd={this.playNextFile} />
          case FileType.Image:
            return <ImagePlayer file={file} onEnd={this.playNextFile} />
          case FileType.Pdf:
              return <PdfPlayer file={file} onEnd={this.playNextFile} />
          case FileType.WebpageContent:
            return <WebsitePlayer file={file} onEnd={this.playNextFile} />
          default:
            showToastMessage(
              I18n.t('ErrorMessage.FormatNotSupported', {
                name: getFileExtension(file).toUpperCase()
              })
            )
            this.playNextFile()
        }
    }

    render() {
      if(this.props.schedulesFiles.length > 0 ) {
          return (

              <View style={{flex:1}}>
              <StatusBar hidden={true} />
              <FlatList
                style={{flex:1}}
                bounces={false}
                removeClippedSubviews={true}
                scrollEnabled={false}
                showsHorizontalScrollIndicator={false}

                ref={el => this.listRef = el}
                horizontal={true}

                keyExtractor={(item, index) => index.toString()}

                data={this.props.schedulesFiles}
                renderItem={this._renderItem}
                onViewableItemsChanged={this.onViewableItemsChanged}
                viewabilityConfig={this.viewabilityConfig}


                getItemLayout={this.getItemLayout}
                initialNumToRender={2}
                maxToRenderPerBatch={2}
                windowSize={this.props.schedulesFiles.length}
              />
            <KeepAwake />
            </View>
          )
      }else {
        return (
          <TouchableWithoutFeedback delayLongPress={constants.REVEAL_SIDE_BAR_MENU_PRESS_DURATION} onLongPress={() => this.props.navigation.openDrawer()}>
            <View style={styles.container}>
                <NothingTpPlay/>
                <KeepAwake />
            </View>
          </TouchableWithoutFeedback>
        )
      }
    }
}


const styles = ScaledSheet.create({
    container: {
      flex:1,
      backgroundColor : 'white', 
    }
  });


//redux binding
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
  return {
      loading: state.scheduleReducer.loading,
      schedulesFiles: state.scheduleReducer.data,
      currentlyPlayingIndex: state.scheduleReducer.nextFileIndex,
  }
}

// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
  return bindActionCreators(Actions, dispatch);
}

//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(PlaylistPlayerScreen);

Метод визуализации для видеоплеера Код дочернего компонента:

<VideoPlayer file={file}  onEnd={this.videoEnded} currentIndex={index} currentVisibleIndex={this.state.currentVisibleIndex} />

VideoPlayer. js соответствующий код

export default class VideoPlayer extends React.Component {

  constructor() {
    super();
    this.state = {
    }
    this.videoEnded = this.videoEnded.bind(this)
  }

 videoEnded() {
    if (this.props.shouldRepeat == true) {
    } else {
      this.video.paused = true
      this.video.seek(0)
    }
    this.props.onEnd()
  }

render() {
    return (
      <Video 
        ref={ref => {
            this.video = ref;
          }}
        onError={this.videoEnded}
        minLoadRetryCount={1}
        useTextureView={true}
        controls={false} 
        style={ContainerStyle.playerTypeStyle} 
        onEnd={this.videoEnded}
        repeat={this.props.shouldRepeat} 
        playInBackground={false}
        playWhenInactive={false}
        ignoreSilentSwitch={"ignore"}

        resizeMode={this.props.file.resize_mode} 
        source={{uri:getFileAbsolutePath(this.props.file)}} 
        paused={this.props.currentIndex != this.props.currentVisibleIndex}
        />      
    )
  }

}

Код редуктора

import { combineReducers } from 'redux';

import { SCHEDULE_REFRESHED,PLAY_NEXT_FILE } from "../actions/ScheduleActions.js" //Import the actions types constant we defined in our actions

let dataState = { data: [], loading:true };

const scheduleReducer = (state = dataState, action) => {
    switch (action.type) {
        case SCHEDULE_REFRESHED:
            state = Object.assign({}, state, { data: action.data, nextFileIndex:action.nextFileIndex });
            return state;
        case PLAY_NEXT_FILE:
            state = Object.assign({}, state, { nextFileIndex: action.nextFileIndex});
            return state;
        default:
            return state;
    }
}

// Combine all the reducers
const rootReducer = combineReducers({
    scheduleReducer
    // ,[ANOTHER REDUCER], [ANOTHER REDUCER] ....
})

export default rootReducer;

Код действия

//gets called initially on app launch to get files to be played
export function getScheduleFiles(){
    return (dispatch) => {
        getOfflineNextScheduleFiles().then((files)=>{//get offline files/schedule first
            plainFiles = convertToArray(files)
            dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
        }).catch((error)=>{//if offline schedules is not available to play, refresh online
            triggerPlaylistsRefresh().then((files)=>{
                plainFiles = convertToArray(files)
                dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
            }).catch((error)=>{
                console.log("nothing to play")
                dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
                showToastMessage(I18n.t("ErrorMessage.NoSchedulesAvailableForCurrentTimeError"))
            })
        })
    }
}

//get called from PlaylistPlayerScreen after each file played
export function playNextFile(files,filePlayedIndex){
    return (dispatch) => {
        if(filePlayedIndex < files.length-1) {
            dispatch({type: PLAY_NEXT_FILE, nextFileIndex:filePlayedIndex+1});
          }else {
            console.log("all files played")
            dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
          }
    }
}
...