import React, { Component } from 'react' import { Route } from 'react-router-dom' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' import actions from '../../../actions' import ParagraphForm from '../components/paragraph.form' const floatLT = (a,b) => ((a*10|0) < (b*10|0)) const floatLTE = (a,b) => ((a*10|0) === (b*10|0) || floatLT(a,b)) const MEDIA_TYPES = new Set(['image', 'gallery', 'vitrine', 'video']) class ParagraphList extends Component { state = { paragraphs: [], currentParagraph: -1, currentAnnotation: -1, selectedParagraph: null, selectedParagraphOffset: 0, } constructor(props) { super(props) this.handleAnnotationClick = this.handleAnnotationClick.bind(this) this.handleParagraphDoubleClick = this.handleParagraphDoubleClick.bind(this) this.handleCloseParagraphForm = this.handleCloseParagraphForm.bind(this) this.updateSelectedParagraph = this.updateSelectedParagraph.bind(this) } componentDidMount() { this.build() } componentDidUpdate(prevProps) { if (this.props.paragraph !== prevProps.paragraph) { this.build() } if (this.props.audio.play_ts === prevProps.audio.play_ts) return this.setCurrentParagraph() } setCurrentParagraph() { const { play_ts } = this.props.audio const insideParagraph = this.state.paragraphs.some(paragraph => { if (floatLTE(paragraph.start_ts, play_ts) && floatLT(play_ts, paragraph.end_ts)) { this.setCurrentAnnotation(paragraph, play_ts) return true } return false }) if (!insideParagraph) { this.setState({ currentParagraph: -1, currentAnnotation: -1, }) } } setCurrentAnnotation(paragraph, play_ts) { const { id: currentParagraph, annotations } = paragraph let currentAnnotation let annotation let i = 0 let len = annotations.length for (let i = 0; i < len - 1; i++) { if (floatLT(play_ts, annotations[i+1].start_ts)) { currentAnnotation = annotations[i].id break } } if (!currentAnnotation) { currentAnnotation = annotations[len-1].id } this.setState({ currentParagraph, currentAnnotation }) } build() { const { order: annotationOrder, lookup: annotationLookup } = this.props.annotation const { lookup: paragraphLookup } = this.props.paragraph let currentParagraph = {} const paragraphs = [] // loop over the annotations in time order annotationOrder.forEach((annotation_id, i) => { const annotation = annotationLookup[annotation_id] const paragraph = paragraphLookup[annotation.paragraph_id] // if this annotation is media, insert it after the current paragraph if (MEDIA_TYPES.has(annotation.type)) { paragraphs.push({ id: ('index_' + i), type: annotation.type, start_ts: annotation.start_ts, end_ts: 0, annotations: [annotation], }) return } // if this annotation is from a different paragraph, make a new paragraph if (annotation.paragraph_id !== currentParagraph.id) { const paragraph_type = getParagraphType(annotation, paragraph) currentParagraph = { id: annotation.paragraph_id || ('index_' + i), type: paragraph_type, start_ts: annotation.start_ts, end_ts: 0, annotations: [], } paragraphs.push(currentParagraph) } // if this annotation is a paragraph_end, set the end timestamp if (annotation.type === 'paragraph_end') { currentParagraph.end_ts = annotation.start_ts } // otherwise, just append this annotation to the paragraph else { currentParagraph.annotations.push(annotation) } }) for (let i = 0; i < (paragraphs.length - 1); i++) { if (!paragraphs[i].end_ts) { paragraphs[i].end_ts = paragraphs[i+1].start_ts - 0.1 } } this.setState({ paragraphs }) } handleAnnotationClick(e, paragraph, annotation){ actions.audio.seek(annotation.start_ts) } handleParagraphDoubleClick(e, paragraph) { console.log(e.target.parentNode) let paragraphNode = e.target if (!paragraphNode.classList.contains('paragraph')) { paragraphNode = paragraphNode.parentNode } this.setState({ selectedParagraph: { ...paragraph }, selectedParagraphOffset: paragraphNode.offsetTop }) } updateSelectedParagraph(selectedParagraph) { this.setState({ selectedParagraph }) } handleCloseParagraphForm() { this.setState({ selectedParagraph: null }) } render() { const { media, paragraphElementLookup } = this.props const { paragraphs, selectedParagraph, selectedParagraphOffset, currentParagraph, currentAnnotation } = this.state return (
{paragraphs.map(paragraph => { if (selectedParagraph && selectedParagraph.id === paragraph.id) { paragraph = selectedParagraph } if (paragraph.type in paragraphElementLookup) { const ParagraphElement = paragraphElementLookup[paragraph.type] return ( ) } else { return
{'(waiting to implement' + paragraph.type + ')'}
} })} {selectedParagraph && }
) } } const getParagraphType = (annotation, paragraph) => { if (!paragraph) { return annotation.type } return paragraph.type } const mapStateToProps = state => ({ paragraph: state.paragraph.index, annotation: state.annotation.index, audio: state.audio, media: state.media.index, }) const mapDispatchToProps = dispatch => ({ }) export default connect(mapStateToProps, mapDispatchToProps)(ParagraphList)