summaryrefslogtreecommitdiff
path: root/animism-align/frontend/app
diff options
context:
space:
mode:
Diffstat (limited to 'animism-align/frontend/app')
-rw-r--r--animism-align/frontend/app/constants.js2
-rw-r--r--animism-align/frontend/app/utils/index.js3
-rw-r--r--animism-align/frontend/app/views/viewer/checklist/checklist.content.js2
-rw-r--r--animism-align/frontend/app/views/viewer/nav/eflux.chrome.js15
-rw-r--r--animism-align/frontend/app/views/viewer/nav/eflux.css14
-rw-r--r--animism-align/frontend/app/views/viewer/player/player.transcript.js10
-rw-r--r--animism-align/frontend/app/views/viewer/sections/sections.css13
-rw-r--r--animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js29
-rw-r--r--animism-align/frontend/app/views/viewer/viewer.actions.js4
-rw-r--r--animism-align/frontend/app/views/viewer/viewer.container.js3
-rw-r--r--animism-align/frontend/app/views/viewer/viewer.mobile.css48
-rw-r--r--animism-align/frontend/app/views/viewer/viewer.reducer.js3
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,