diff options
Diffstat (limited to 'animism-align')
10 files changed, 105 insertions, 23 deletions
diff --git a/animism-align/frontend/app/types.js b/animism-align/frontend/app/types.js index eca6cf3..855385c 100644 --- a/animism-align/frontend/app/types.js +++ b/animism-align/frontend/app/types.js @@ -29,6 +29,7 @@ export const viewer = with_type('viewer', [ 'set_current_section', 'reached_end_of_section', 'set_nav_style', 'open_vitrine_modal', 'close_vitrine_modal', 'set_vitrine_index', + 'open_growl', 'close_growl', ]) export const site = with_type('site', [ diff --git a/animism-align/frontend/app/utils/annotation.utils.js b/animism-align/frontend/app/utils/annotation.utils.js index 89e6aea..8640fa9 100644 --- a/animism-align/frontend/app/utils/annotation.utils.js +++ b/animism-align/frontend/app/utils/annotation.utils.js @@ -1,4 +1,4 @@ -import { timestampToSeconds } from 'app/utils' +import { clamp, timestampToSeconds } from 'app/utils' export const annotationFadeTimings = annotation => { const fadeInDuration = timestampToSeconds(annotation.settings.fade_in_duration || '0') || 0.1 @@ -39,3 +39,11 @@ export const thumbnailURL = media => { return null } } + +export const sectionProgress = (section, play_ts) => { + return (clamp(play_ts, section.start_ts, section.end_ts) - section.start_ts) / section.duration +} + +export const sectionProgressPercentage = (section, play_ts) => { + return (Math.round(sectionProgress(section, play_ts) * 2000) / 20) + '%' +}
\ No newline at end of file diff --git a/animism-align/frontend/app/views/viewer/nav/eflux.chrome.js b/animism-align/frontend/app/views/viewer/nav/eflux.chrome.js index 40c8365..f8c2892 100644 --- a/animism-align/frontend/app/views/viewer/nav/eflux.chrome.js +++ b/animism-align/frontend/app/views/viewer/nav/eflux.chrome.js @@ -7,13 +7,16 @@ import { import { PlayButton } from './viewer.icons' import actions from 'app/actions' -const EfluxChrome = React.memo(({ navStyle, playing, transcriptOpen }) => ( +const EfluxChrome = React.memo(({ navStyle, playing, transcriptOpen, started, growlOpen, growlMessage }) => ( <div className={"eflux-header " + navStyle + (transcriptOpen ? ' transcript-open' : '')}> <div className="eflux-logo"> <a href="/"> {EfluxLogo} </a> </div> + <div className={growlOpen ? "growl-message open" : "growl-message"}> + {growlMessage} + </div> <div className="eflux-nav"> <PlayButton playing={playing} /> <div className="transcript-icon" onClick={() => actions.viewer.toggleComponent('transcript')}> @@ -27,6 +30,8 @@ const mapStateToProps = state => ({ navStyle: state.viewer.navStyle, playing: state.audio.playing, transcriptOpen: state.viewer.transcript, + growlOpen: state.viewer.growlOpen, + growlMessage: state.viewer.growlMessage, }) export default connect(mapStateToProps)(EfluxChrome) diff --git a/animism-align/frontend/app/views/viewer/nav/eflux.css b/animism-align/frontend/app/views/viewer/nav/eflux.css index 487c04e..fe57779 100644 --- a/animism-align/frontend/app/views/viewer/nav/eflux.css +++ b/animism-align/frontend/app/views/viewer/nav/eflux.css @@ -75,3 +75,18 @@ .eflux-logo.black svg line { stroke: #fff; } + +.growl-message { + position: absolute; + top: 0; left: 50%; + transition: transform 0.2s; + transform: translateZ(0) translateY(-5rem) translateX(-50%); + background: black; + color: white; + border: 1px solid; + padding: 0.75rem; + margin-top: 1rem; +} +.growl-message.open { + transform: translateZ(0) translateY(0) translateX(-50%); +}
\ No newline at end of file diff --git a/animism-align/frontend/app/views/viewer/nav/nav.css b/animism-align/frontend/app/views/viewer/nav/nav.css index 922bd01..a6b9a3b 100644 --- a/animism-align/frontend/app/views/viewer/nav/nav.css +++ b/animism-align/frontend/app/views/viewer/nav/nav.css @@ -14,7 +14,7 @@ transform: translateZ(0) translateY(0); border-top: 1px solid transparent; } -.main-nav.hovering-nav { +.viewer-nav.hovering-nav .main-nav { background: black; color: white; } @@ -110,6 +110,24 @@ transform: translateZ(0) translateY(-9rem); } +/* Inverse progress bar */ + +.nav-progress-percentage { + position: absolute; + bottom: 0; + left: 0; + height: calc(3rem - 1px); + pointer-events: none; + backdrop-filter: invert(0%); + transition: opacity 0.2s, backdrop-filter 0.2s, width 0.25s linear; + opacity: 0; +} +.nav-open .nav-progress-percentage, +.viewer-nav.hovering-nav .nav-progress-percentage { + backdrop-filter: invert(100%); + opacity: 1; +} + /* Arrows */ .arrow svg { @@ -139,8 +157,8 @@ } .viewer-nav.black .arrow path, .viewer-nav.black .arrow polygon, -.viewer-nav .main-nav.hovering-nav .arrow path, -.viewer-nav .main-nav.hovering-nav .arrow polygon, +.viewer-nav.hovering-nav .main-nav .arrow path, +.viewer-nav.hovering-nav .main-nav .arrow polygon, .nav-open .viewer-nav .arrow path, .nav-open .viewer-nav .arrow polygon, .checklist-open .viewer-nav .arrow path, @@ -157,7 +175,7 @@ .nav-player.playing { opacity: 0.0; } -.main-nav.hovering-nav .nav-player.playing, +.viewer-nav.hovering-nav .nav-player.playing, .nav-open .nav-player.playing { opacity: 1.0; } @@ -177,11 +195,11 @@ stroke: #000; stroke-width: 0.5; } -.main-nav.hovering-nav .volume path, +.viewer-nav.hovering-nav .volume path, .nav-open .volume path { fill: #fff; } -.main-nav.hovering-nav .volume.muted path, +.viewer-nav.hovering-nav .volume.muted path, .nav-open .volume.muted path { fill: #000; stroke: #fff; @@ -198,8 +216,8 @@ .playToggle polygon { fill: #000; } -.main-nav.hovering-nav .playToggle path, -.main-nav.hovering-nav .playToggle polygon, +.viewer-nav.hovering-nav .playToggle path, +.viewer-nav.hovering-nav .playToggle polygon, .nav-open .playToggle path, .nav-open .playToggle polygon { fill: #fff; diff --git a/animism-align/frontend/app/views/viewer/nav/nav.parent.js b/animism-align/frontend/app/views/viewer/nav/nav.parent.js index 8ad0cd1..ca1c148 100644 --- a/animism-align/frontend/app/views/viewer/nav/nav.parent.js +++ b/animism-align/frontend/app/views/viewer/nav/nav.parent.js @@ -5,7 +5,7 @@ import actions from 'app/actions' import { ROMAN_NUMERALS } from 'app/constants' import { Arrow } from './viewer.icons' import NavPlayer from './nav.player' -import { thumbnailURL } from 'app/utils/annotation.utils' +import { thumbnailURL, sectionProgressPercentage } from 'app/utils/annotation.utils' class NavParent extends Component { state = { @@ -38,11 +38,11 @@ class NavParent extends Component { }, 50) } render() { - const { viewer } = this.props + const { viewer, play_ts } = this.props let containerClassName = "viewer-nav " + viewer.navStyle let navClassName = 'nav-row main-nav' - if (this.state.hoveringNav) navClassName += ' hovering-nav' - if (this.state.hoveringNext || viewer.atEndOfSection) containerClassName += ' hovering-next' + if (this.state.hoveringNav) containerClassName += ' hovering-nav' + if ((this.state.hoveringNext || viewer.atEndOfSection) && !viewer.nav) containerClassName += ' hovering-next' return ( <div className={containerClassName} onMouseLeave={this.handleMouseLeave}> <div className={navClassName}> @@ -73,6 +73,9 @@ class NavParent extends Component { </span> } </div> + <div className="nav-progress-percentage" style={{ + width: viewer.currentSection ? sectionProgressPercentage(viewer.currentSection, play_ts) : 0 + }} /> </div> {viewer.nextSection && <div className="next-section-thumbnail" @@ -91,6 +94,7 @@ class NavParent extends Component { const mapStateToProps = state => ({ viewer: state.viewer, + play_ts: state.audio.play_ts, }) export default connect(mapStateToProps)(NavParent) diff --git a/animism-align/frontend/app/views/viewer/player/player.transcript.js b/animism-align/frontend/app/views/viewer/player/player.transcript.js index 17218d5..cee70a8 100644 --- a/animism-align/frontend/app/views/viewer/player/player.transcript.js +++ b/animism-align/frontend/app/views/viewer/player/player.transcript.js @@ -14,6 +14,7 @@ class PlayerTranscript extends Component { this.handleClose = this.handleClose.bind(this) this.handleAnnotationClick = this.handleAnnotationClick.bind(this) this.handleParagraphDoubleClick = this.handleParagraphDoubleClick.bind(this) + this.handleScroll = this.handleScroll.bind(this) this.containerRef = React.createRef() } @@ -37,10 +38,16 @@ class PlayerTranscript extends Component { handleClose() { } + handleScroll(e) { + if (this.props.viewer.growlOpen) { + actions.viewer.closeGrowl() + } + } + render() { const { paragraphs } = this.props.section return ( - <div className="player-transcript" ref={this.containerRef}> + <div className="player-transcript" ref={this.containerRef} onScroll={this.handleScroll}> <div className='content'> <ParagraphList paragraphs={paragraphs} @@ -55,7 +62,7 @@ class PlayerTranscript extends Component { } const mapStateToProps = state => ({ - // viewer: state.viewer, + viewer: state.viewer, }) const mapDispatchToProps = dispatch => ({ 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 05eef9c..e0ec212 100644 --- a/animism-align/frontend/app/views/viewer/sections/viewer.sections.js +++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.js @@ -6,7 +6,7 @@ import actions from 'app/actions' import ViewerSectionsNav from './viewer.sections.nav' 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 { thumbnailURL, sectionProgressPercentage } from 'app/utils/annotation.utils' import { SpeakerIcon } from '../nav/viewer.icons' class ViewerSections extends Component { @@ -23,7 +23,7 @@ class ViewerSections extends Component { // 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) + const progress = sectionProgressPercentage(section, play_ts) return ( <div className={(!currentSection || section.index === currentSection.index) ? "viewer-section current-section" : "viewer-section"} @@ -44,7 +44,7 @@ class ViewerSections extends Component { </div> <div className="section-progress-bar"> <div className="section-progress" - style={{ width: progress + '%' }} + style={{ width: progress }} /> </div> </div> @@ -68,10 +68,6 @@ 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, diff --git a/animism-align/frontend/app/views/viewer/viewer.actions.js b/animism-align/frontend/app/views/viewer/viewer.actions.js index f5f0630..e8aaf0d 100644 --- a/animism-align/frontend/app/views/viewer/viewer.actions.js +++ b/animism-align/frontend/app/views/viewer/viewer.actions.js @@ -218,3 +218,10 @@ export const closeVitrineModal = (media, id) => dispatch => { export const setVitrineIndex = (index) => dispatch => { dispatch({ type: types.viewer.set_vitrine_index, index }) } + +export const openGrowl = message => dispatch => { + dispatch({ type: types.viewer.open_growl, message }) +} +export const closeGrowl = () => dispatch => { + dispatch({ type: types.viewer.close_growl }) +} diff --git a/animism-align/frontend/app/views/viewer/viewer.reducer.js b/animism-align/frontend/app/views/viewer/viewer.reducer.js index 2ab5f57..4c25d64 100644 --- a/animism-align/frontend/app/views/viewer/viewer.reducer.js +++ b/animism-align/frontend/app/views/viewer/viewer.reducer.js @@ -11,6 +11,8 @@ const initialState = { navStyle: 'white', autoAdvance: false, atEndOfSection: false, + growlOpen: true, + growlMessage: "Start the episode by clicking play or scroll to browse on your own.", vitrineModal: { open: false, media: null, @@ -29,6 +31,25 @@ export default function viewerReducer(state = initialState, action) { [action.key]: action.value, } + case types.audio.play: + return { + ...state, + growlOpen: false, + } + + case types.viewer.open_growl: + return { + ...state, + growlMessage: action.message, + growlOpen: true, + } + + case types.viewer.close_growl: + return { + ...state, + growlOpen: false, + } + case types.viewer.load_sections: return { ...state, |
