import * as types from 'app/types' import { store, history, dispatch } from 'app/store' import actions from 'app/actions' import { MEDIA_ANNOTATION_TYPES, MEDIA_LABEL_TYPES, TEXT_ANNOTATION_TYPES, INLINE_UTILITY_ANNOTATION_TYPES, FULLSCREEN_UTILITY_ANNOTATION_TYPES, CURTAIN_COLOR_LOOKUP, } from 'app/constants' import { buildParagraphs } from 'app/utils/transcript.utils' import { annotationFadeTimings } from 'app/utils/annotation.utils' const newSection = (annotation, index, mediaIndex) => ({ start_ts: annotation.start_ts, end_ts: 0, title: annotation.text, media: [], index, mediaIndex, }) // build the list of sections from the raw annotation list. export const loadSections = () => dispatch => { // list of all sections let sections = [] // current section being processed (i.e. last section) let currentSection // keep tally of all media, so that we can display them with correct IDs in the checklist let mediaIndex = 0 let eventIndex = 0 // dedupe the labels that we see in each section let currentMediaLabels = {} // keep track of all annotations that constitute the "text" of the essay // these include sentences, headings, and inline media. used to build paragraphs, then reset. let sectionTextAnnotationOrder = [] // keep track of all annotations that constitute fullscreen events. // these include curtains, title cards, and fullscreen media. let fullscreenTimeline = [] // fetch all annotations and media const state = store.getState() const { timeline } = state.align const { order: annotationOrder, lookup: annotationLookup } = state.annotation.index const { lookup: mediaLookup } = state.media.index // loop over the annotations in time order. annotationOrder.forEach((annotation_id, i) => { // fetch the current annotation const annotation = annotationLookup[annotation_id] // we have reached a new section. if (annotation.type === 'section_heading') { // finish off the previous section. if (currentSection) { currentSection.mediaLabels = Object.keys(currentMediaLabels).join(', ') currentSection.paragraphs = buildParagraphs(sectionTextAnnotationOrder, currentSection.index) currentSection.end_ts = currentSection.paragraphs[currentSection.paragraphs.length - 1].end_ts } // create a new section and reset state variables currentSection = newSection(annotation, sections.length, mediaIndex) currentMediaLabels = {} sectionTextAnnotationOrder = [] // add this new section to the list! sections.push(currentSection) } // sanity check. ignore everything before the first section. if (!currentSection) { return } // add media to the current section. if (MEDIA_ANNOTATION_TYPES.has(annotation.type)) { // fetch the media and add it to the list of media (TODO: handle carousels) const media = mediaLookup[annotation.settings.media_id] currentSection.media.push({ start_ts: annotation.start_ts, media }) // get the display string for this media type if (media.type in MEDIA_LABEL_TYPES) { currentMediaLabels[MEDIA_LABEL_TYPES[media.type]] = true } // increment the media tally mediaIndex += 1 // non-fullscreen, inline media should be displayed in the transcript. if (!annotation.settings.fullscreen || annotation.settings.inline) { sectionTextAnnotationOrder.push(annotation.id) } } // build timeline of special inline items if (INLINE_UTILITY_ANNOTATION_TYPES.has(annotation.type)) { sectionTextAnnotationOrder.push(annotation.id) } // build timeline of fullscreen events if (FULLSCREEN_UTILITY_ANNOTATION_TYPES.has(annotation.type) || annotation.settings.fullscreen) { const event = makeFullscreenEvent(eventIndex++, annotation) fullscreenTimeline.push(event) } // add text annotations to section annotation order if (TEXT_ANNOTATION_TYPES.has(annotation.type)) { sectionTextAnnotationOrder.push(annotation.id) } }) // finished processing all annotations. finish off the last section. if (currentSection) { currentSection.mediaLabels = Object.keys(currentMediaLabels).join(', ') currentSection.paragraphs = buildParagraphs(sectionTextAnnotationOrder, currentSection.index) currentSection.end_ts = timeline.duration } // set the end_ts for each section (i.e. just before the next section starts) for (let i = 0; i < sections.length - 1; i++) { currentSection = sections[i] if (currentSection.end_ts === 0) { currentSection.end_ts = sections[i+1].start_ts - 1 } } // console.log(sections) // console.log(fullscreenTimeline) dispatch({ type: types.viewer.load_sections, sections, fullscreenTimeline }) } const makeFullscreenEvent = (index, annotation) => { const timing = annotationFadeTimings(annotation) const event = { ...timing, annotation, index, settings: annotation.settings, type: annotation.type, } if (annotation.settings.color) { event.color = CURTAIN_COLOR_LOOKUP[annotation.settings.color] || CURTAIN_COLOR_LOOKUP.white } return event } export const setCurrentSection = (currentSection, nextSection) => dispatch => { dispatch({ type: types.viewer.set_current_section, currentSection, nextSection }) } export const setNavStyle = color => dispatch => { dispatch({ type: types.viewer.set_nav_style, color }) } export const showComponent = key => dispatch => { dispatch({ type: types.viewer.toggle_component, key, value: true }) } export const hideComponent = key => dispatch => { dispatch({ type: types.viewer.toggle_component, key, value: false }) } export const toggleComponent = key => dispatch => { dispatch({ type: types.viewer.toggle_component, key, value: !store.getState().viewer[key] }) } export const seekToSection = section => dispatch => { actions.audio.seek(section.start_ts) actions.audio.play() actions.viewer.hideComponent('nav') } export const seekToMediaItem = mediaItem => dispatch => { actions.audio.seek(mediaItem.start_ts) actions.audio.play() actions.viewer.hideComponent('nav') actions.viewer.hideComponent('checklist') }