import React, { Component } from 'react' import { connect } from 'react-redux' import { TransitionGroup, CSSTransition } from 'react-transition-group' import actions from 'app/actions' import { clamp, floatInRange, floatLT } from 'app/utils' import { PERSISTENT_FULLSCREEN_ELEMENTS, CURTAIN_COLOR_LOOKUP } from 'app/constants' import { fullscreenComponents } from './components.fullscreen' import { makeFullscreenEvent } from '../viewer.actions' class PlayerFullscreen extends Component { state = { elements: [], video: false, isSingleton: false, atChapterStart: false, } componentDidMount() { this.setCurrentElements() } componentDidUpdate(prevProps) { const sectionChanged = this.props.currentSection !== prevProps.currentSection const playTimeChanged = this.props.audio.play_ts !== prevProps.audio.play_ts const seekTimeChanged = this.props.audio.seek_ts !== prevProps.audio.seek_ts if (sectionChanged || playTimeChanged || seekTimeChanged) { this.setCurrentElements() } } setCurrentElements() { const { audio, timeline, currentSection, media } = this.props let { play_ts, seek_ts } = audio play_ts = clamp(play_ts, currentSection.start_ts, currentSection.end_ts) let elements, isSingleton = false, atChapterStart = false // console.log(timeline) // some classes can be singleton media for an entire chapter, like vitrines // console.log(timeline.map(t => t.type)) if (timeline.length === 2 && timeline[1].type === 'vitrine') { elements = timeline isSingleton = true } else { // get elements at this play position elements = timeline.filter(element => ( floatInRange(element.start_ts, play_ts, element.fade_out_start_ts + 0.1) )) // if the audio is paused, and the first el is a curtain, remove it if (elements.length && !audio.playing && elements[0].type === 'curtain') { elements.shift() } if (elements.length > 1) { elements = elements.map((e, i) => [(e.type === 'curtain' ? i + 100 : i), e]) .sort((a,b) => a[0] - b[0]) .map(p => p[1]) } // if the seek time matches any element, we wanna show that element immediately. // additionally, if we were still inside another earlier element, dont show it at all. // did we recently seek to an element? // console.log(elements) const justSeeked = Math.abs(seek_ts - play_ts) < 5.0 && (Math.abs(seek_ts - currentSection.start_ts) > 2.0) const seekedElements = elements.filter(e => (e.start_ts >= seek_ts || e.fade_out_start_ts > seek_ts + 5.0)) // console.log(seekedElements.length, elements.length, play_ts, seek_ts) // if we did, then we want to use the filtered elements i.e. dont show earlier nodes // also any elements starting at this point should transition immediately, // to prevent flash of the underlying content. if (justSeeked && seekedElements.length) { atChapterStart = true // console.log(play_ts, seek_ts, 'seeked', seekedElements) elements = seekedElements.map(e => { if (e.start_ts === seek_ts && e.type !== 'curtain') { return { ...e, fadeInDuration: 0, } } return e }) // const sectionColor = currentSection.paragraphs[0].annotations[0].settings.color || 'white' // CURTAIN_COLOR_LOOKUP[sectionColor], elements.unshift(makeFullscreenEvent(100, { start_ts: seek_ts, end_ts: seek_ts + 1.3, type: 'curtain', settings: { color: elements[0].annotation.settings.color, fade_in_duration: '0:00', fade_out_duration: '0:00.8', duration: '0:01.3', } })) } } // console.log(elements) // set nav style from top-most element // furthermore, the fullscreen view should persist unless the user has closed it (or the chapter ends) let persist = elements.length if (elements.length) { const lastElement = elements[elements.length - 1] // persist = elements.filter(e => PERSISTENT_FULLSCREEN_ELEMENTS.has(e.type)) if (lastElement.color && lastElement.color.textColor === '#ffffff') { actions.viewer.setNavStyle('black') } else { actions.viewer.setNavStyle('white') } if (lastElement.type === 'video') { const item = media.lookup[lastElement.settings.media_id] actions.viewer.setMediaTitle(item.title) } else { actions.viewer.setMediaTitle(null) } } else { actions.viewer.setNavStyle(currentSection.color) actions.viewer.setMediaTitle(null) } // if fullscreen mode started or ended... if (!!elements.length !== this.props.isFullscreen || isSingleton !== this.props.isSingleton) { actions.viewer.updateFullscreenStatus(!!elements.length, persist, isSingleton) } // elements.reverse() this.setState({ elements, persist, isSingleton, atChapterStart }) } render() { const { audio, media, currentSection, fullscreenVisible } = this.props const { play_ts, playing } = audio const { elements, persist, atChapterStart } = this.state // console.log(elements) let className = "viewer-fullscreen" // console.log(elements.length && fullscreenVisible) if (elements.length) { className += " active" } if (!fullscreenVisible) { className += " hidden" } if (persist) { className += " persist" } if (atChapterStart) { className += " seeked" } className += playing ? " playing" : " paused" // console.log(className) return (
{elements.map(element => { if (!(element.type in fullscreenComponents)) { return null } let { type, index, fadeInDuration, fadeOutDuration, start_ts, end_ts, fade_in_end_ts, fade_out_start_ts, } = element const isEntering = floatInRange(start_ts, play_ts, fade_in_end_ts) const isLeaving = floatInRange(fade_out_start_ts, play_ts, end_ts) const FullscreenComponent = fullscreenComponents[type] fadeInDuration *= 1000 fadeOutDuration *= 1000 if (!isEntering) { fadeInDuration = 0 } if (!isLeaving) { fadeOutDuration = 0 } const transitionDuration = (isEntering ? fadeInDuration : fadeOutDuration) + 'ms' // console.log(play_ts, isEntering, isLeaving, fadeInDuration, fadeOutDuration) return ( ) })}
) } } const FirstChild = (props) => { const childrenArray = React.Children.toArray(props.children); return childrenArray[0] || null; } const mapStateToProps = state => ({ currentSection: state.viewer.currentSection, isFullscreen: state.viewer.isFullscreen, isSingleton: state.viewer.isFullscreenSingleton, fullscreenVisible: state.viewer.fullscreenVisible, audio: state.audio, media: state.media.index, timeline: state.viewer.currentSection ? state.viewer.currentSection.fullscreenTimeline : [], }) export default connect(mapStateToProps)(PlayerFullscreen)