summaryrefslogtreecommitdiff
path: root/animism-align/frontend/app/views/editor/align/containers/timeline.container.js
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2021-03-08 22:11:55 +0100
committerJules Laplace <julescarbon@gmail.com>2021-03-08 22:11:55 +0100
commitd2cb17038b8537a609be06be2ed7013dbe27117e (patch)
tree028ceac9edddafc03ce80c49d5a05981bec3fcbe /animism-align/frontend/app/views/editor/align/containers/timeline.container.js
parentb5ceb782f40fc1e402d1e58bc1ced2e4038fd787 (diff)
beginning the BIG refactor. moving editor stuff into per-episode hierarchy
Diffstat (limited to 'animism-align/frontend/app/views/editor/align/containers/timeline.container.js')
-rw-r--r--animism-align/frontend/app/views/editor/align/containers/timeline.container.js196
1 files changed, 196 insertions, 0 deletions
diff --git a/animism-align/frontend/app/views/editor/align/containers/timeline.container.js b/animism-align/frontend/app/views/editor/align/containers/timeline.container.js
new file mode 100644
index 0000000..feb4f6a
--- /dev/null
+++ b/animism-align/frontend/app/views/editor/align/containers/timeline.container.js
@@ -0,0 +1,196 @@
+import React, { Component } from 'react'
+// import { Link } from 'react-router-dom'
+import { connect } from 'react-redux'
+
+import actions from 'app/actions'
+
+import Annotations from 'app/views/editor/align/containers/annotations.container'
+import Waveform from 'app/views/editor/align/components/timeline/waveform.component'
+import Ticks from 'app/views/editor/align/components/timeline/ticks.component'
+import Cursor from 'app/views/editor/align/components/timeline/cursor.component'
+import PlayCursor from 'app/views/editor/align/components/timeline/playCursor.component'
+import CursorRegion from 'app/views/editor/align/components/timeline/cursorRegion.component'
+
+import { WAVEFORM_SIZE, ZOOM_STEPS, INNER_HEIGHT } from 'app/constants'
+import { clamp } from 'app/utils'
+import { positionToTime } from 'app/utils/align.utils'
+
+class Timeline extends Component {
+ state = {
+ dragging: false,
+ a_ts: -1,
+ }
+ constructor(props){
+ super(props)
+ this.handleKeydown = this.handleKeydown.bind(this)
+ this.handleMouseMove = this.handleMouseMove.bind(this)
+ this.handleWheel = this.handleWheel.bind(this)
+ this.handleContainerClick = this.handleContainerClick.bind(this)
+ this.handleTimelineMouseDown = this.handleTimelineMouseDown.bind(this)
+ this.handleTimelineMouseUp = this.handleTimelineMouseUp.bind(this)
+ }
+ componentDidMount() {
+ this.bind()
+ }
+ componentWillUnmount() {
+ this.unbind()
+ }
+ shouldComponentUpdate(nextProps) {
+ return (
+ nextProps.timeline !== this.props.timeline ||
+ nextProps.annotation !== this.props.annotation
+ )
+ }
+ bind() {
+ document.addEventListener('keydown', this.handleKeydown)
+ }
+ unbind() {
+ document.removeEventListener('keydown', this.handleKeydown)
+ }
+ handleKeydown(e) {
+ if (document.activeElement !== document.body) {
+ return
+ }
+ console.log(e.keyCode)
+ if (e.metaKey && this.props.selectedAnnotation.id) {
+ const { selectedAnnotation } = this.props
+ switch (e.keyCode) {
+ case 38: // up
+ e.preventDefault()
+ selectedAnnotation.start_ts = Math.max(selectedAnnotation.start_ts - (e.shiftKey ? 1 : 0.1), 0)
+ actions.align.updateSelectedAnnotation(selectedAnnotation)
+ actions.audio.seek(selectedAnnotation.start_ts)
+ actions.align.setCursor(selectedAnnotation.start_ts)
+ break
+ case 40: // down
+ e.preventDefault()
+ selectedAnnotation.start_ts += e.shiftKey ? 1 : 0.1
+ actions.align.updateSelectedAnnotation(selectedAnnotation)
+ actions.audio.seek(selectedAnnotation.start_ts)
+ actions.align.setCursor(selectedAnnotation.start_ts)
+ break
+ case 68: // D
+ e.preventDefault()
+ actions.align.cloneSelectedAnnotation(selectedAnnotation)
+ }
+ return
+ }
+ if (e.shiftKey) {
+ switch (e.keyCode) {
+ case 187: // plus
+ actions.align.setZoom(this.props.timeline.zoom - 1)
+ break
+ case 189: // minus
+ actions.align.setZoom(this.props.timeline.zoom + 1)
+ break
+ }
+ } else {
+ // console.log(e.keyCode)
+ switch (e.keyCode) {
+ case 27: // escape
+ actions.align.hideAnnotationForm()
+ break
+ case 65: // A - add
+ e.preventDefault()
+ actions.align.showNewAnnotationForm(this.props.audio.play_ts, this.props.text)
+ break
+ case 32: // spacebar
+ actions.audio.toggle()
+ break
+ case 38: // up
+ actions.audio.jump(- ZOOM_STEPS[this.props.timeline.zoom] * 0.1)
+ break
+ case 40: // down
+ actions.audio.jump(ZOOM_STEPS[this.props.timeline.zoom] * 0.1)
+ break
+ }
+ }
+ }
+ handleWheel(e) {
+ let { start_ts, zoom, duration } = this.props.timeline
+ let { deltaY } = e
+ let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step
+ let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel
+ start_ts += Math.round((deltaY) * ZOOM_STEPS[zoom])
+ start_ts = clamp(start_ts, 0, Math.max(0, duration - widthTimeDuration / 2))
+ if (e.shiftKey) {
+ if (Math.abs(deltaY) < 2) return
+ if (e.deltaY > 0) {
+ actions.align.throttledSetZoom(this.props.timeline.zoom + 1)
+ } else {
+ actions.align.throttledSetZoom(this.props.timeline.zoom - 1)
+ }
+ } else if (e.altKey) {
+ actions.audio.jump(e.deltaY * ZOOM_STEPS[zoom])
+ } else {
+ actions.align.setScrollPosition(start_ts)
+ }
+ }
+ handleContainerClick(e) {
+ actions.align.clearSelectedAnnotation()
+ actions.align.clearSelectedParagraph()
+ }
+ handleTimelineMouseDown(e) {
+ const cursor_ts = positionToTime(e.pageY, this.props.timeline)
+ actions.align.clearCursorRegion()
+ actions.align.setCursor(cursor_ts)
+ this.setState({
+ dragging: true,
+ a_ts: cursor_ts,
+ })
+ }
+ handleMouseMove(e) {
+ const cursor_ts = positionToTime(e.pageY, this.props.timeline)
+ if (this.state.dragging) {
+ actions.align.setCursorRegion(
+ Math.min(this.state.a_ts, cursor_ts),
+ Math.max(this.state.a_ts, cursor_ts),
+ )
+ } else {
+ actions.align.setCursor(cursor_ts)
+ }
+ }
+ handleTimelineMouseUp(e) {
+ this.setState({ dragging: false })
+ const play_ts = positionToTime(e.pageY, this.props.timeline)
+ if (e.metaKey) {
+ actions.align.spliceTime(play_ts)
+ } else if (e.pageX < WAVEFORM_SIZE * 0.67) {
+ actions.audio.seek(play_ts)
+ } else {
+ actions.align.showNewAnnotationForm(play_ts, this.props.text)
+ }
+ }
+ render() {
+ return (
+ <div
+ className='timeline'
+ onClick={this.handleContainerClick}
+ onWheel={this.handleWheel}
+ onMouseMove={this.handleMouseMove}
+ >
+ <div className='timelineColumn'>
+ <Waveform
+ onMouseDown={this.handleTimelineMouseDown}
+ onMouseUp={this.handleTimelineMouseUp}
+ />
+ <Ticks timeline={this.props.timeline} />
+ <Cursor timeline={this.props.timeline} annotation={this.props.annotation} />
+ <CursorRegion timeline={this.props.timeline} />
+ </div>
+ <Annotations timeline={this.props.timeline} />
+ <PlayCursor />
+ </div>
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ timeline: state.align.timeline,
+ annotation: state.align.annotation,
+ selectedAnnotation: state.align.selectedAnnotation,
+ audio: state.audio,
+ text: state.site.text,
+})
+
+export default connect(mapStateToProps)(Timeline)