diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-11-21 16:44:07 +0100 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-11-21 16:44:07 +0100 |
| commit | 49bf241d0fa275053424db3458a057de7b9d418f (patch) | |
| tree | 8df9a9c5748c2bece60de2403d1cddbe0cd56b56 | |
| parent | ee04ccd494b8a2dd1e535168979596c9907cd0ab (diff) | |
messaging if rest of chapters are disabled. grayscale all disabled media
12 files changed, 130 insertions, 16 deletions
diff --git a/animism-align/frontend/app/constants.js b/animism-align/frontend/app/constants.js index 0125ec9..af6f004 100644 --- a/animism-align/frontend/app/constants.js +++ b/animism-align/frontend/app/constants.js @@ -206,6 +206,8 @@ export const GROWL = { export const EPILEPSY_WARNING = "WARNING: This video may potentially trigger seizures for people with photosensitive epilepsy. Viewer discretion is advised." +export const SECTION_LIMITED_MESSAGE = "The full episode is not currently supported on mobile devices." + export const VIDEO_SCRUBBER_HIDE_DELAY = 1000 export const VIDEO_SCRUBBER_HOVER_AREA = 12 * 16 // 12 rem diff --git a/animism-align/frontend/app/utils/index.js b/animism-align/frontend/app/utils/index.js index 0ed8898..4519861 100644 --- a/animism-align/frontend/app/utils/index.js +++ b/animism-align/frontend/app/utils/index.js @@ -16,7 +16,7 @@ export const isiPhone = !!((navigator.userAgent.match(/iPhone/i)) || (navigator. export const isiPad = !!(navigator.userAgent.match(/iPad/i)) export const isAndroid = !!(navigator.userAgent.match(/Android/i)) export const isMobile = isiPhone || isiPad || isAndroid -export const isHandheld = isiPhone || isAndroid +export const isHandheld = isiPhone || (isAndroid && window.innerWidth <= 640) export const isTablet = isMobile && window.innerWidth > 640 export const isDesktop = !isMobile @@ -94,6 +94,7 @@ export const px = (n, w) => Math.round(n * w) + 'px' export const clamp = (n, a=0, b=1) => n < a ? a : n < b ? n : b export const dist = (x1, y1, x2, y2) => Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) +export const xor = (a,b) => ((!a && !!b) || (!!a && !b)) 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 lerp = (n,a,b) => (b-a)*n+a diff --git a/animism-align/frontend/app/views/viewer/checklist/checklist.content.js b/animism-align/frontend/app/views/viewer/checklist/checklist.content.js index ba44a1b..f756b36 100644 --- a/animism-align/frontend/app/views/viewer/checklist/checklist.content.js +++ b/animism-align/frontend/app/views/viewer/checklist/checklist.content.js @@ -9,6 +9,7 @@ import { PlayIcon } from '../nav/viewer.icons' class ChecklistContent extends Component { handleMediaSelection(section, mediaItem, i) { + if (this.props.onlyEnableFirstSection) return // when clicking a work in the checklist, // if it's the first work in the section, seek to the beginning of the section // OTHERWISE seek to the work itself. might have to add this as another option on sections @@ -83,6 +84,7 @@ class ChecklistContent extends Component { const mapStateToProps = state => ({ sections: state.viewer.sections, + onlyEnableFirstSection: state.viewer.onlyEnableFirstSection, }) export default connect(mapStateToProps)(ChecklistContent) 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 6da6f76..f2f02d2 100644 --- a/animism-align/frontend/app/views/viewer/nav/eflux.chrome.js +++ b/animism-align/frontend/app/views/viewer/nav/eflux.chrome.js @@ -48,10 +48,20 @@ class EfluxChrome extends Component { } } render() { - const { navStyle, playing, transcriptOpen, growlOpen, growlMessage, atEndOfSection, currentSection } = this.props + const { + navStyle, navGradient, + playing, transcriptOpen, + growlOpen, growlMessage, + atEndOfSection, currentSection + } = this.props const showingTranscriptTooltip = ((atEndOfSection && currentSection.index === 0) || this.state.transcript) + let className = "eflux-header" + if (navStyle) className += ' ' + navStyle + if (navGradient) className += ' gradient' + if (transcriptOpen) className += ' transcript-open' return ( - <div className={"eflux-header " + navStyle + (transcriptOpen ? ' transcript-open' : '')}> + <div className={className}> + <div className="eflux-gradient" /> <div className="eflux-logo"> <a href={URLS.eflux_logo} @@ -99,6 +109,7 @@ class EfluxChrome extends Component { const mapStateToProps = state => ({ navStyle: state.viewer.navStyle, + navGradient: state.viewer.navGradient, playing: state.audio.playing, transcriptOpen: state.viewer.transcript, growlOpen: state.viewer.growlOpen, diff --git a/animism-align/frontend/app/views/viewer/nav/eflux.css b/animism-align/frontend/app/views/viewer/nav/eflux.css index e25cb09..eb7f973 100644 --- a/animism-align/frontend/app/views/viewer/nav/eflux.css +++ b/animism-align/frontend/app/views/viewer/nav/eflux.css @@ -9,6 +9,20 @@ .eflux-header.transcript-open { width: calc(100vw - 31.875rem); } +.eflux-gradient { + position: fixed; + top: 0; left: 0; + width: 100%; + height: 4.5rem; + pointer-events: none; + background-image: linear-gradient(0deg, rgba(255,255,255,0.0), rgba(255,255,255,1.0)); + opacity: 0.0; + transition: opacity 2.0s; + z-index: 0; +} +.gradient .eflux-gradient { + opacity: 1.0; +} .eflux-logo { position: absolute; top: 20px; 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 1ce98ef..7a9d4aa 100644 --- a/animism-align/frontend/app/views/viewer/player/player.transcript.js +++ b/animism-align/frontend/app/views/viewer/player/player.transcript.js @@ -4,7 +4,7 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import actions from 'app/actions' -import { timestampToSeconds } from 'app/utils' +import { isHandheld, xor, timestampToSeconds } from 'app/utils' import ParagraphList from 'app/views/paragraph/components/paragraph.list' import { inlineComponents } from './components.inline' @@ -47,12 +47,20 @@ class PlayerTranscript extends Component { } handleScroll(e) { + if (isHandheld) { + const isScrolledPastIntro = this.containerRef.current.scrollTop > 100 + if (xor(this.props.viewer.navGradient, isScrolledPastIntro)) { + console.log('toggle nav gradient', isScrolledPastIntro) + actions.viewer.toggleNavGradient(isScrolledPastIntro) + } + } if (this.props.viewer.growlOpen) { actions.viewer.closeGrowl() if (this.props.viewer.currentSection.index === 0) { actions.audio.play() } } + } render() { diff --git a/animism-align/frontend/app/views/viewer/sections/sections.css b/animism-align/frontend/app/views/viewer/sections/sections.css index ca1d86e..77cd12f 100644 --- a/animism-align/frontend/app/views/viewer/sections/sections.css +++ b/animism-align/frontend/app/views/viewer/sections/sections.css @@ -117,19 +117,20 @@ margin: 1rem 0 1rem 1rem; font-size: 16px; cursor: pointer; - opacity: 0.6; - transition: opacity 0.2s; + position: relative; } -.viewer-sections .viewer-section:hover { - opacity: 1.0; +.viewer-sections .viewer-section .section-content { + transition: opacity 0.2s; + opacity: 0.6; } -.viewer-sections .viewer-section.current-section { +.viewer-sections .viewer-section.current-section .section-content, +.viewer-sections .viewer-section:hover .section-content { opacity: 1.0; } .viewer-sections .viewer-section:last-child { margin-right: 1rem; } -.viewer-section > div { +.viewer-section .section-content { height: calc(20rem - 4px); width: 12rem; } diff --git a/animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js b/animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js index 0d34a45..8fc1238 100644 --- a/animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js +++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import actions from 'app/actions' -import { ROMAN_NUMERALS } from 'app/constants' +import { ROMAN_NUMERALS, SECTION_LIMITED_MESSAGE } from 'app/constants' import { timestamp } from 'app/utils' import { thumbnailURL, sectionProgressPercentage } from 'app/utils/annotation.utils' import { SpeakerIcon } from '../nav/viewer.icons' @@ -10,6 +10,9 @@ import { SpeakerIcon } from '../nav/viewer.icons' const SECTION_WIDTH = 13 * 16 class ViewerSectionsList extends Component { + state = { + selectedSectionIndex: -1, + } constructor(props) { super(props) this.scrollRef = React.createRef() @@ -33,8 +36,20 @@ class ViewerSectionsList extends Component { let delta = Math.abs(e.deltaY) > Math.abs(e.deltaX) ? e.deltaY : e.deltaX this.scrollRef.current.scrollLeft += delta } + handleSectionClick(e, section) { + if (this.props.onlyEnableFirstSection) { + this.setState({ selectedSectionIndex: section.index }) + clearTimeout(this.warningFadeTimeout) + this.warningFadeTimeout = setTimeout(() => { + this.setState({ selectedSectionIndex: -1 }) + }, 10000) + } else { + actions.viewer.seekToSection(section) + } + } render() { - const { play_ts, sections, media, currentSection } = this.props + const { play_ts, sections, media, currentSection, onlyEnableFirstSection } = this.props + const { selectedSectionIndex } = this.state return ( <div ref={this.scrollRef} @@ -55,9 +70,9 @@ class ViewerSectionsList extends Component { <div className={(!currentSection || section.index === currentSection.index) ? "viewer-section current-section" : "viewer-section"} key={section.index} - onClick={() => actions.viewer.seekToSection(section)} + onClick={e => this.handleSectionClick(e, section)} > - <div> + <div className="section-content"> <div className="section-thumbnail" style={{ backgroundImage: mediaItem && 'url(' + thumbnailURL(mediaItem) + ')', }}> @@ -85,6 +100,11 @@ class ViewerSectionsList extends Component { {section.mediaLabels} </div> </div> + {!!(onlyEnableFirstSection && section.index) && ( + <div className={section.index === selectedSectionIndex ? 'section-limited visible' : 'section-limited'}> + {SECTION_LIMITED_MESSAGE} + </div> + )} </div> ) })} @@ -96,6 +116,7 @@ class ViewerSectionsList extends Component { const mapStateToProps = state => ({ media: state.media.index, nav: state.viewer.nav, + onlyEnableFirstSection: state.viewer.onlyEnableFirstSection, play_ts: state.audio.play_ts, sections: state.viewer.sections, currentSection: state.viewer.currentSection, diff --git a/animism-align/frontend/app/views/viewer/viewer.actions.js b/animism-align/frontend/app/views/viewer/viewer.actions.js index 43c40da..9131763 100644 --- a/animism-align/frontend/app/views/viewer/viewer.actions.js +++ b/animism-align/frontend/app/views/viewer/viewer.actions.js @@ -303,6 +303,10 @@ export const toggleNavComponent = key => dispatch => { dispatch({ type: types.viewer.toggle_nav_component, key, value: !store.getState().viewer[key] }) } +export const toggleNavGradient = value => dispatch => { + dispatch({ type: types.viewer.toggle_component, key: 'navGradient', value: value }) +} + export const toggleComponent = key => dispatch => { hideNavElementsNotMatchedBy(key)(dispatch) const state = store.getState().viewer diff --git a/animism-align/frontend/app/views/viewer/viewer.container.js b/animism-align/frontend/app/views/viewer/viewer.container.js index 6068f80..0f9a5e5 100644 --- a/animism-align/frontend/app/views/viewer/viewer.container.js +++ b/animism-align/frontend/app/views/viewer/viewer.container.js @@ -62,6 +62,9 @@ class ViewerContainer extends Component { } else if (viewer.footnotes) { className += ' footnotes-open' } + if (viewer.onlyEnableFirstSection) { + className += ' first-section-only' + } return ( <div> <div className='viewer-parent'> diff --git a/animism-align/frontend/app/views/viewer/viewer.mobile.css b/animism-align/frontend/app/views/viewer/viewer.mobile.css index 27d713d..ce75471 100644 --- a/animism-align/frontend/app/views/viewer/viewer.mobile.css +++ b/animism-align/frontend/app/views/viewer/viewer.mobile.css @@ -1,4 +1,4 @@ -@media (max-width: 600px) { +@media (max-device-width: 600px) { /* document sizing */ @@ -58,6 +58,13 @@ /* logo and header icons */ + .eflux-header { + } + .site-intro { + height: calc(100vh - 4.5rem); + margin-top: 4.5rem; + background-size: auto 105%; + } .eflux-nav .transcript-icon { display: none; } @@ -65,6 +72,7 @@ .eflux-nav .transcript-icon { width: 2.5rem; height: 2.5rem; + box-shadow: 0.3px 0.6px 2.6px rgba(0,0,0,0.3); } .eflux-logo { left: 50%; @@ -89,7 +97,6 @@ .sections-nav > .nav-row > div.checklist-element { } - .viewer-nav .link.next-link, .sections-nav > .nav-row > div.subscribe-link, .sections-nav > .nav-row > div.footnotes-link, .sections-nav > .nav-row > div.transcript-link { @@ -222,3 +229,40 @@ padding-right: 1.0rem; } } + + +/* if the introduction is the only section that's enabled... */ + +.first-section-only .checklist-section, +.first-section-only .viewer-section .section-thumbnail { + filter: grayscale(100%); + opacity: 0.5; +} +.first-section-only .checklist-section:first-child, +.first-section-only .viewer-section.current-section .section-thumbnail { + filter: grayscale(0%); + opacity: 1.0; +} +.first-section-only .viewer-nav .link.next-link { + display: none; +} +.first-section-only .section-limited { + position: absolute; + left: 0; + top: 0; + width: 12rem; + height: 8rem; + padding: 0.0 1.0rem; + line-height: 1.4; + z-index: 1; + text-align: center; + pointer-events: none; + opacity: 0.0; + transition: 0.5s opacity; + display: flex; + justify-content: center; + align-items: center; +} +.first-section-only .section-limited.visible { + opacity: 1.0; +}
\ No newline at end of file diff --git a/animism-align/frontend/app/views/viewer/viewer.reducer.js b/animism-align/frontend/app/views/viewer/viewer.reducer.js index e1a0167..6ec615a 100644 --- a/animism-align/frontend/app/views/viewer/viewer.reducer.js +++ b/animism-align/frontend/app/views/viewer/viewer.reducer.js @@ -1,5 +1,6 @@ import * as types from 'app/types' import { GROWL } from 'app/constants' +import { isHandheld } from 'app/utils' const navComponents = { /* UI components that close if anything else opens */ @@ -22,6 +23,7 @@ const initialState = { nextSection: null, autoAdvance: false, atEndOfSection: false, + onlyEnableFirstSection: isHandheld, /* footnotes */ footnoteList: [], @@ -29,6 +31,7 @@ const initialState = { /* color of the bar / logo / icons */ navStyle: 'white', + navGradient: false, // mobile only? /* title of media if in fullscreen mode */ mediaTitle: null, |
