summaryrefslogtreecommitdiff
path: root/animism-align/frontend/app/views/viewer/sections
diff options
context:
space:
mode:
Diffstat (limited to 'animism-align/frontend/app/views/viewer/sections')
-rw-r--r--animism-align/frontend/app/views/viewer/sections/sections.css68
-rw-r--r--animism-align/frontend/app/views/viewer/sections/share.css54
-rw-r--r--animism-align/frontend/app/views/viewer/sections/subscribe.css69
-rw-r--r--animism-align/frontend/app/views/viewer/sections/viewer.sections.js82
-rw-r--r--animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js86
-rw-r--r--animism-align/frontend/app/views/viewer/sections/viewer.sections.nav.js34
-rw-r--r--animism-align/frontend/app/views/viewer/sections/viewer.sections.share.js71
-rw-r--r--animism-align/frontend/app/views/viewer/sections/viewer.sections.subscribe.js21
8 files changed, 376 insertions, 109 deletions
diff --git a/animism-align/frontend/app/views/viewer/sections/sections.css b/animism-align/frontend/app/views/viewer/sections/sections.css
index 4858d7a..b357efe 100644
--- a/animism-align/frontend/app/views/viewer/sections/sections.css
+++ b/animism-align/frontend/app/views/viewer/sections/sections.css
@@ -4,13 +4,13 @@
position: absolute;
bottom: 0;
left: 0;
+ height: 22rem;
width: 100%;
background: black;
color: white;
display: block;
transition: transform 0.2s;
transform: translateZ(0) translateY(25rem);
- border-top: 1px solid #fff;
}
.nav-open .viewer-sections {
transform: translateZ(0) translateY(0);
@@ -19,15 +19,46 @@
transform: translateZ(0) translateY(calc(3rem - 100vh));
z-index: 20;
}
+
+/* sections nav */
+
.sections-nav {
z-index: 22;
+ border-top: 1px solid white;
+ background: black;
+}
+.sections-nav.viewer-nav > .nav-row {
+ z-index: 24;
+ background: black;
+ padding: 0;
+}
+.sections-nav .link {
+ position: relative;
+ height: 3rem;
+ padding: 0.25rem 1rem 0.25rem 0.25rem;
+}
+.sections-nav .subscribe-link {
+ border-right: 1px solid white;
+}
+.sections-nav .share-link {
+ border-right: 1px solid white;
+}
+.sections-nav .footnotes-link {
+ border-left: 1px solid white;
+}
+.sections-nav .transcript-link {
+ padding-left: 1rem;
+ border-left: 1px solid white;
}
/* scrolling part */
.viewer-sections .viewer-sections-scroll {
+ position: absolute;
+ top: 0;
+ left: 0;
width: 100%;
- height: 22rem;
+ height: 19rem;
white-space: nowrap;
overflow-x: scroll;
overflow-y: hidden;
@@ -35,25 +66,6 @@
.checklist-open .viewer-sections .viewer-sections-scroll {
overflow-x: hidden;
}
-.viewer-sections-scroll::-webkit-scrollbar {
- cursor: pointer;
- user-select: none;
- height: 4px;
- width: 4px;
-}
-.viewer-sections-scroll::-webkit-scrollbar-button {
- display: block;
- width: 0;
- height: 0;
-}
-.viewer-sections-scroll::-webkit-scrollbar-track-piece {
- background: rgba(211,211,211,0.8);
-}
-.viewer-sections-scroll::-webkit-scrollbar-thumb {
- display: block;
- background: #000;
-}
-
/* clickable section indicators */
@@ -128,4 +140,16 @@
left: 0;
height: 3px;
background: #fff;
-} \ No newline at end of file
+}
+
+/* Boxes that emerge from the nav */
+
+.nav-return {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-start;
+ position: relative;
+ left: -0.5rem;
+ padding: 0.5rem 0 0.2rem 0;
+}
diff --git a/animism-align/frontend/app/views/viewer/sections/share.css b/animism-align/frontend/app/views/viewer/sections/share.css
new file mode 100644
index 0000000..ae5dd53
--- /dev/null
+++ b/animism-align/frontend/app/views/viewer/sections/share.css
@@ -0,0 +1,54 @@
+/* share box */
+
+.nav-share {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 20rem;
+ transform: translateY(30rem) translateZ(0);
+ transition: opacity 0.01s, transform 0.2s;
+ padding: 0.5rem 0.75rem 0 0.75rem;
+ background: black;
+ border-top: 1px solid white;
+ border-right: 1px solid white;
+ color: white;
+ font-size: 18px;
+}
+.checklist-open .nav-share {
+ top: 0rem;
+ z-index: 21;
+ transform: translateY(-30rem) translateZ(0);
+ border-top: 0;
+ border-bottom: 1px solid white;
+ bottom: auto;
+}
+.share-open .nav-share {
+ transform: translateY(0) translateZ(0);
+ opacity: 1;
+ z-index: 10;
+}
+
+.nav-share a {
+ color: white;
+ text-decoration: none;
+}
+.nav-share .share-success {
+ display: none;
+}
+.nav-share.copying .share-success {
+ margin-left: 1rem;
+ display: inline;
+ opacity: 1;
+ color: white;
+ font-size: 0.75rem;
+}
+.nav-share.copying.copied .share-success {
+ opacity: 0;
+ transition: opacity 1.0s 1.0s;
+}
+.nav-share .share-option {
+ padding: 0.5rem 0;
+}
+.nav-share .share-copy {
+ cursor: pointer;
+}
diff --git a/animism-align/frontend/app/views/viewer/sections/subscribe.css b/animism-align/frontend/app/views/viewer/sections/subscribe.css
new file mode 100644
index 0000000..9c27614
--- /dev/null
+++ b/animism-align/frontend/app/views/viewer/sections/subscribe.css
@@ -0,0 +1,69 @@
+/* subscription form */
+
+.nav-subscribe {
+ position: absolute;
+ bottom: 0;
+ left: -1px;
+ width: 35rem;
+ transform: translateY(30rem) translateZ(0);
+ transition: opacity 0.01s, transform 0.2s;
+ padding: 0.5rem 0.75rem 0 0.75rem;
+ background: black;
+ border-top: 1px solid white;
+ border-right: 1px solid white;
+ border-left: 1px solid white;
+ color: white;
+ font-size: 18px;
+}
+.checklist-open .nav-subscribe {
+ top: 0rem;
+ z-index: 21;
+ transform: translateY(-30rem) translateZ(0);
+ border-top: 0;
+ border-bottom: 1px solid white;
+ bottom: auto;
+}
+.subscribe-open .nav-subscribe {
+ transform: translateY(0) translateZ(0);
+ opacity: 1;
+ z-index: 10;
+}
+
+/* subscription form */
+
+.nav-subscribe .subscription-form {
+ width: 100%;
+ white-space: normal;
+ position: relative;
+}
+.nav-subscribe .subscription-form input {
+ font-size: 18px;
+ background: transparent;
+ border: 0;
+ padding: 5px 0;
+ width: 100%;
+ color: #fff;
+}
+.nav-subscribe .subscription-form input::placeholder {
+ color: #888;
+}
+.nav-subscribe .subscription-form input:focus,
+.nav-subscribe .subscription-form input:focus:invalid {
+ background: transparent;
+ border: 0;
+ outline: 0;
+ color: #fff;
+}
+.nav-subscribe .subscription-form .arrow {
+ position: absolute;
+ bottom: -0.25rem;
+ right: -1rem;
+ width: 2.5rem;
+ height: 2.5rem;
+}
+.nav-subscribe .subscription-form svg polygon {
+ fill: #666;
+}
+.nav-subscribe .subscription-callout {
+ width: 30rem;
+} \ 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 8e6f39b..a2f906c 100644
--- a/animism-align/frontend/app/views/viewer/sections/viewer.sections.js
+++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.js
@@ -1,91 +1,15 @@
import React, { Component } from 'react'
-// import { Link } from 'react-router-dom'
-import { connect } from 'react-redux'
-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, sectionProgressPercentage } from 'app/utils/annotation.utils'
-import { SpeakerIcon } from '../nav/viewer.icons'
+import ViewerSectionsList from './viewer.sections.list'
-class ViewerSections extends Component {
- constructor(props) {
- super(props)
- this.scrollRef = React.createRef()
- this.handleWheel = this.handleWheel.bind(this)
- }
- shouldComponentUpdate(nextProps) {
- if (nextProps.nav !== this.props.nav) return true
- return nextProps.nav
- }
- handleWheel(e) {
- let delta = Math.abs(e.deltaY) > Math.abs(e.deltaX) ? e.deltaY : e.deltaX
- this.scrollRef.current.scrollLeft += delta
- }
+export default class ViewerSections extends Component {
render() {
- const { play_ts, sections, currentSection } = this.props
return (
<div className="viewer-sections">
- <div
- ref={this.scrollRef}
- className="viewer-sections-scroll"
- onWheel={e => this.handleWheel(e)}
- >
- {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 = sectionProgressPercentage(section, play_ts)
- return (
- <div
- className={(!currentSection || section.index === currentSection.index) ? "viewer-section current-section" : "viewer-section"}
- key={section.index}
- onClick={() => actions.viewer.seekToSection(section)}
- >
- <div>
- <div className="section-thumbnail" style={{
- backgroundImage: media && 'url(' + thumbnailURL(media) + ')',
- }}>
- {!no_audio &&
- <div className={"section-duration-" + section_nav_color}>
- <div className="section-duration">
- {timestamp(section.duration)}
- </div>
- <div className="section-has-audio">
- {SpeakerIcon}
- </div>
- <div className="section-progress-bar">
- <div className="section-progress"
- style={{ width: progress }}
- />
- </div>
- </div>
- }
- </div>
- <div className="section-title">
- {ROMAN_NUMERALS[section.index]}<br />
- {section.title}
- </div>
- <div className="section-media">
- {section.mediaLabels}
- </div>
- </div>
- </div>
- )
- })}
- </div>
+ <ViewerSectionsList />
<ViewerSectionsNav />
</div>
)
}
}
-
-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/sections/viewer.sections.list.js b/animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js
new file mode 100644
index 0000000..34a944d
--- /dev/null
+++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.list.js
@@ -0,0 +1,86 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+
+import actions from 'app/actions'
+import { ROMAN_NUMERALS } from 'app/constants'
+import { timestamp } from 'app/utils'
+import { thumbnailURL, sectionProgressPercentage } from 'app/utils/annotation.utils'
+import { SpeakerIcon } from '../nav/viewer.icons'
+
+class ViewerSectionsList extends Component {
+ constructor(props) {
+ super(props)
+ this.scrollRef = React.createRef()
+ this.handleWheel = this.handleWheel.bind(this)
+ }
+ shouldComponentUpdate(nextProps) {
+ if (nextProps.nav !== this.props.nav) return true
+ return nextProps.nav
+ }
+ handleWheel(e) {
+ let delta = Math.abs(e.deltaY) > Math.abs(e.deltaX) ? e.deltaY : e.deltaX
+ this.scrollRef.current.scrollLeft += delta
+ }
+ render() {
+ const { play_ts, sections, currentSection } = this.props
+ return (
+ <div
+ ref={this.scrollRef}
+ className="viewer-sections-scroll"
+ onWheel={e => this.handleWheel(e)}
+ >
+ {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 = sectionProgressPercentage(section, play_ts)
+ return (
+ <div
+ className={(!currentSection || section.index === currentSection.index) ? "viewer-section current-section" : "viewer-section"}
+ key={section.index}
+ onClick={() => actions.viewer.seekToSection(section)}
+ >
+ <div>
+ <div className="section-thumbnail" style={{
+ backgroundImage: media && 'url(' + thumbnailURL(media) + ')',
+ }}>
+ {!no_audio &&
+ <div className={"section-duration-" + section_nav_color}>
+ <div className="section-duration">
+ {timestamp(section.duration)}
+ </div>
+ <div className="section-has-audio">
+ {SpeakerIcon}
+ </div>
+ <div className="section-progress-bar">
+ <div className="section-progress"
+ style={{ width: progress }}
+ />
+ </div>
+ </div>
+ }
+ </div>
+ <div className="section-title">
+ {ROMAN_NUMERALS[section.index]}<br />
+ {section.title}
+ </div>
+ <div className="section-media">
+ {section.mediaLabels}
+ </div>
+ </div>
+ </div>
+ )
+ })}
+ </div>
+ )
+ }
+}
+
+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)(ViewerSectionsList)
diff --git a/animism-align/frontend/app/views/viewer/sections/viewer.sections.nav.js b/animism-align/frontend/app/views/viewer/sections/viewer.sections.nav.js
index 5a2971b..56046c3 100644
--- a/animism-align/frontend/app/views/viewer/sections/viewer.sections.nav.js
+++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.nav.js
@@ -4,6 +4,8 @@ import { connect } from 'react-redux'
import actions from 'app/actions'
import { Arrow } from '../nav/viewer.icons'
+import ViewerSectionsShare from './viewer.sections.share'
+import ViewerSectionsSubscribe from './viewer.sections.subscribe'
class ViewerSectionsNav extends Component {
render() {
@@ -12,21 +14,37 @@ class ViewerSectionsNav extends Component {
<div className="viewer-nav sections-nav">
<div className='nav-row'>
<div>
- <span className="share-link link" onClick={() => actions.viewer.toggleComponent('share')}>
- <Arrow type={viewer.checklist ? 'down' : 'up'} />
- {'Share'}
- </span>
+ <div className="share-link link">
+ <ViewerSectionsShare />
+ <span onClick={() => actions.viewer.toggleComponent('share')}>
+ <Arrow type={'up'} />
+ {'Share'}
+ </span>
+ </div>
+ <div className="subscribe-link link">
+ <ViewerSectionsSubscribe />
+ <span onClick={() => actions.viewer.toggleComponent('subscribe')}>
+ <Arrow type={viewer.subscribe ? 'down' : 'up'} />
+ {'Subscribe'}
+ </span>
+ </div>
</div>
<div>
- <span className="checklist-link link" onClick={() => actions.viewer.toggleComponent('checklist')}>
+ <div className="checklist-link link" onClick={() => actions.viewer.toggleComponent('checklist')}>
<Arrow type={viewer.checklist ? 'down': 'up'} />
{'Checklist'}
- </span>
+ </div>
</div>
<div>
- <span className="transcript-link link" onClick={() => actions.viewer.openTranscript()}>
+ <div className="footnotes-link link">
+ <span onClick={() => actions.viewer.toggleComponent('footnotes')}>
+ <Arrow type={viewer.footnotes ? 'down' : 'up'} />
+ {'Footnotes'}
+ </span>
+ </div>
+ <div className="transcript-link link" onClick={() => actions.viewer.openTranscript()}>
{'Transcript'}
- </span>
+ </div>
</div>
</div>
</div>
diff --git a/animism-align/frontend/app/views/viewer/sections/viewer.sections.share.js b/animism-align/frontend/app/views/viewer/sections/viewer.sections.share.js
new file mode 100644
index 0000000..607d4d5
--- /dev/null
+++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.share.js
@@ -0,0 +1,71 @@
+import React, { Component } from 'react'
+
+import actions from 'app/actions'
+import { writeToClipboard } from 'app/utils'
+import { Arrow } from '../nav/viewer.icons'
+
+export default class ViewerSectionsShare extends Component {
+ state = {
+ copied: false,
+ }
+ constructor(props){
+ super(props)
+ this.copyToClipboard = this.copyToClipboard.bind(this)
+ }
+ copyToClipboard() {
+ const url = "https://e-flux.com/"
+ writeToClipboard(url).then(() =>{
+ clearTimeout(this.timeout)
+ this.setState({ copying: true, copied: false, })
+ this.timeout = setTimeout(() => {
+ this.setState({ copying: true, copied: true })
+ this.timeout = setTimeout(() => this.setState({ copied: false, copying: false, }), 2200)
+ }, 50)
+ })
+ }
+ render() {
+ const { viewer } = this.props
+ let className = "nav-share"
+ if (this.state.copying) className += " copying"
+ if (this.state.copied) className += " copied"
+ const title = "Animism Episode 1"
+ const url = "https://e-flux.com/"
+ const mailtoURL = (
+ "mailto:?subject=" + encodeURIComponent(title) +
+ "&body=" + encodeURIComponent("I want to share this post on e-flux: " + url + "\n\n\n")
+ )
+ const facebookURL = (
+ "https://www.facebook.com/sharer.php" +
+ "?u=" + encodeURIComponent(url) +
+ "&t=" + encodeURIComponent(title)
+ )
+ const twitterURL = (
+ "https://twitter.com/intent/tweet" +
+ "?url=" + encodeURIComponent(url) +
+ "&text=" + encodeURIComponent(title)
+ )
+ return (
+ <div className={className}>
+ <div className="share-option">
+ <a href={mailtoURL}>Email</a>
+ </div>
+ <div className="share-option">
+ <a href={facebookURL}>Facebook</a>
+ </div>
+ <div className="share-option">
+ <a href={twitterURL}>Twitter</a>
+ </div>
+ <div className="share-option">
+ <div className="share-copy" onClick={this.copyToClipboard}>
+ Copy Link
+ <span className="share-success">success</span>
+ </div>
+ </div>
+ <div className="nav-return" onClick={() => actions.viewer.hideComponent('share')}>
+ <Arrow type={'down'} />
+ {'Share'}
+ </div>
+ </div>
+ )
+ }
+}
diff --git a/animism-align/frontend/app/views/viewer/sections/viewer.sections.subscribe.js b/animism-align/frontend/app/views/viewer/sections/viewer.sections.subscribe.js
new file mode 100644
index 0000000..24ccf99
--- /dev/null
+++ b/animism-align/frontend/app/views/viewer/sections/viewer.sections.subscribe.js
@@ -0,0 +1,21 @@
+import React, { Component } from 'react'
+
+import actions from 'app/actions'
+import { Arrow } from '../nav/viewer.icons'
+import SubscriptionForm from '../forms/subscription.form'
+
+export default class ViewerSectionsShare extends Component {
+ render() {
+ return (
+ <div className='nav-subscribe'>
+ <div>
+ <SubscriptionForm />
+ </div>
+ <div className="nav-return" onClick={() => actions.viewer.hideComponent('subscribe')}>
+ <Arrow type={'down'} />
+ {'Subscribe'}
+ </div>
+ </div>
+ )
+ }
+}