From fe515fa4bc940183ba9253e67461c1f009a5d94b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 26 Aug 2020 22:28:13 +0200 Subject: times and stuff on the section nav --- animism-align/frontend/app/constants.js | 5 +++ animism-align/frontend/app/utils/index.js | 12 ++++-- .../annotationForms/annotationForm.text.js | 38 ++++++++++++++++- .../frontend/app/views/viewer/nav/viewer.icons.js | 7 ++++ .../app/views/viewer/sections/sections.css | 48 +++++++++++++++++++++- .../app/views/viewer/sections/viewer.sections.js | 42 ++++++++++++++++--- .../frontend/app/views/viewer/viewer.actions.js | 5 ++- 7 files changed, 144 insertions(+), 13 deletions(-) (limited to 'animism-align/frontend') diff --git a/animism-align/frontend/app/constants.js b/animism-align/frontend/app/constants.js index 6650bee..857c20a 100644 --- a/animism-align/frontend/app/constants.js +++ b/animism-align/frontend/app/constants.js @@ -73,6 +73,11 @@ export const CURTAIN_COLORS = [ { label: 'black', backgroundColor: '#000000', textColor: '#ffffff' }, ] +export const BLACK_WHITE_SELECT_OPTIONS = [ + { label: 'white', name: 'white'}, + { label: 'black', name: 'black'}, +] + export const CURTAIN_COLOR_SELECT_OPTIONS = CURTAIN_COLORS.map(color => ({ label: color.label, name: color.label, diff --git a/animism-align/frontend/app/utils/index.js b/animism-align/frontend/app/utils/index.js index 7f9100c..0f5a1dd 100644 --- a/animism-align/frontend/app/utils/index.js +++ b/animism-align/frontend/app/utils/index.js @@ -53,9 +53,9 @@ export const capitalize = s => s.split(' ').map(capitalizeWord).join(' ') export const capitalizeWord = s => s.substr(0, 1).toUpperCase() + s.substr(1) export const padSeconds = n => n < 10 ? '0' + n : n -export const timestamp = (n = 0, fps = 1, ms = false) => { +export const timestamp = (n = 0, fps = 1, ms = false, h_label = ':', m_label = ':', s_label = '') => { if (n < 0) { - return '0:00' + return '0' + m_label + '00' + s_label } let s = '' n /= fps @@ -67,11 +67,13 @@ export const timestamp = (n = 0, fps = 1, ms = false) => { s = padSeconds(n % 60) + s n = Math.floor(n / 60) if (n > 60) { - return Math.floor(n / 60) + ':' + padSeconds(n % 60) + ':' + s + return Math.floor(n / 60) + h_label + padSeconds(n % 60) + m_label + s + s_label } - return (n % 60) + ':' + s + return (n % 60) + m_label + s + s_label } +export const timestampHMS = n => timestamp(n, 1, false, 'h', 'm', 's') + export const timestampToSeconds = time_str => { const time_str_parts = (time_str || "").trim().split(":").map(s => parseFloat(s)) if (time_str_parts.length === 3) { @@ -93,6 +95,8 @@ export const mod = (n, m) => n - (m * Math.floor(n / m)) export const angle = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1) export const floatLT = (a,b) => ((a*10|0) < (b*10|0)) export const floatLTE = (a,b) => ((a*10|0) === (b*10|0) || floatLT(a,b)) +export const floatGT = (a,b) => ((a*10|0) > (b*10|0)) +export const floatGTE = (a,b) => ((a*10|0) === (b*10|0) || floatGT(a,b)) export const floatInRange = (a,b,c) => floatLTE(a, b) && floatLT(b, c) export const simpleArraysEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b) diff --git a/animism-align/frontend/app/views/align/components/annotations/annotationForms/annotationForm.text.js b/animism-align/frontend/app/views/align/components/annotations/annotationForms/annotationForm.text.js index a632e70..d3009d8 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotationForms/annotationForm.text.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotationForms/annotationForm.text.js @@ -1,6 +1,7 @@ import React, { Component } from 'react' -import { Select, Checkbox } from 'app/common' +import { CURTAIN_COLOR_SELECT_OPTIONS, BLACK_WHITE_SELECT_OPTIONS } from 'app/constants' +import { Select, Checkbox, LabelDescription } from 'app/common' export const AnnotationFormSectionHeading = ({ annotation, handleSettingsSelect, handleSettingsChange }) => { return ( @@ -11,6 +12,41 @@ export const AnnotationFormSectionHeading = ({ annotation, handleSettingsSelect, checked={annotation.settings.hidden} onChange={handleSettingsSelect} /> + + + {'Set the color of the duration icon on the section navigation'} + + + } ) } diff --git a/animism-align/frontend/app/views/viewer/nav/viewer.icons.js b/animism-align/frontend/app/views/viewer/nav/viewer.icons.js index 7bc0219..059f0c0 100644 --- a/animism-align/frontend/app/views/viewer/nav/viewer.icons.js +++ b/animism-align/frontend/app/views/viewer/nav/viewer.icons.js @@ -92,3 +92,10 @@ export const ZoomPlus = ( ) + +export const SpeakerIcon = ( + + + +) \ No newline at end of file diff --git a/animism-align/frontend/app/views/viewer/sections/sections.css b/animism-align/frontend/app/views/viewer/sections/sections.css index 1ddb9d2..a60a063 100644 --- a/animism-align/frontend/app/views/viewer/sections/sections.css +++ b/animism-align/frontend/app/views/viewer/sections/sections.css @@ -58,6 +58,14 @@ margin: 1rem 0 1rem 1rem; font-size: 16px; cursor: pointer; + opacity: 0.6; + transition: opacity 0.2s; +} +.viewer-sections .viewer-section:hover { + opacity: 1.0; +} +.viewer-sections .viewer-section.current-section { + opacity: 1.0; } .viewer-sections .viewer-section:last-child { margin-right: 1rem; @@ -66,7 +74,7 @@ height: calc(20rem - 4px); width: 12rem; } -.viewer-sections .section-thumbnail { +.viewer-section .section-thumbnail { display: block; border-radius: 1rem; width: 12rem; @@ -74,8 +82,44 @@ margin-bottom: 0.5rem; background-size: cover; background-position: center center; + position: relative; + overflow: hidden; } -.viewer-sections .section-media { +.viewer-section .section-duration { + position: absolute; + bottom: 0.5rem; + left: 0.5rem; + font-size: 0.75rem; +} +.section-duration-white { color: white; } +.section-duration-black { color: black; } +.section-duration-white svg path { fill: white; } +.section-duration-black svg path { fill: black; } +.viewer-section .section-media { margin-top: 0.75rem; font-size: 12px; } +.viewer-section .section-has-audio { + position: absolute; + bottom: -0.25rem; + right: 0.25rem; +} +.viewer-section .section-has-audio svg { + width: 2.5rem; + height: 2.5rem; +} +.viewer-section .section-progress-bar { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 3px; + background: #333; +} +.viewer-section .section-progress { + position: absolute; + top: 0; + left: 0; + height: 3px; + background: #fff; +} \ No newline at end of file diff --git a/animism-align/frontend/app/views/viewer/sections/viewer.sections.js b/animism-align/frontend/app/views/viewer/sections/viewer.sections.js index 4bb61ec..05eef9c 100644 --- a/animism-align/frontend/app/views/viewer/sections/viewer.sections.js +++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.js @@ -4,27 +4,52 @@ import { connect } from 'react-redux' import actions from 'app/actions' import ViewerSectionsNav from './viewer.sections.nav' -import { ROMAN_NUMERALS } from 'app/constants' +import { ROMAN_NUMERALS, CURTAIN_COLOR_LOOKUP } from 'app/constants' +import { clamp, timestamp, floatInRange, floatLT } from 'app/utils' import { thumbnailURL } from 'app/utils/annotation.utils' +import { SpeakerIcon } from '../nav/viewer.icons' class ViewerSections extends Component { + shouldComponentUpdate(nextProps) { + if (nextProps.nav !== this.props.nav) return true + return nextProps.nav + } render() { - const { sections } = this.props + const { play_ts, sections, currentSection } = this.props return (
{sections.map(section => { // console.log(section) + const media = section.media.length ? section.media[0].media : null + const { no_audio, section_nav_color } = section + const progress = Math.round(sectionProgress(section, play_ts) * 100) return (
actions.viewer.seekToSection(section)} >
+ backgroundImage: media && 'url(' + thumbnailURL(media) + ')', + }}> + {!no_audio && +
+
+ {timestamp(section.duration)} +
+
+ {SpeakerIcon} +
+
+
+
+
+ } +
{ROMAN_NUMERALS[section.index]}
{section.title} @@ -43,8 +68,15 @@ class ViewerSections extends Component { } } +const sectionProgress = (section, play_ts) => { + return (clamp(play_ts, section.start_ts, section.end_ts) - section.start_ts) / section.duration +} + const mapStateToProps = state => ({ + nav: state.viewer.nav, + play_ts: state.audio.play_ts, sections: state.viewer.sections, + currentSection: state.viewer.currentSection, }) export default connect(mapStateToProps)(ViewerSections) diff --git a/animism-align/frontend/app/views/viewer/viewer.actions.js b/animism-align/frontend/app/views/viewer/viewer.actions.js index 097f01b..f5f0630 100644 --- a/animism-align/frontend/app/views/viewer/viewer.actions.js +++ b/animism-align/frontend/app/views/viewer/viewer.actions.js @@ -18,6 +18,8 @@ const newSection = (annotation, index, mediaIndex) => ({ media: [], index, mediaIndex, + no_audio: !!annotation.settings.no_audio, + section_nav_color: annotation.settings.section_nav_color || 'white', }) // build the list of sections from the raw annotation list. @@ -125,7 +127,7 @@ export const loadSections = () => dispatch => { if (currentSection) { currentSection.mediaLabels = Object.keys(currentMediaLabels).sort().join(', ') currentSection.paragraphs = buildParagraphs(sectionTextAnnotationOrder, currentSection.index) - currentSection.end_ts = timeline.duration + currentSection.duration = timeline.duration } // set the end_ts for each section (i.e. just before the next section starts) @@ -134,6 +136,7 @@ export const loadSections = () => dispatch => { if (currentSection.end_ts === 0) { currentSection.end_ts = sections[i+1].start_ts - 1 } + currentSection.duration = currentSection.end_ts - currentSection.start_ts } // console.log(sections) -- cgit v1.2.3-70-g09d2