summaryrefslogtreecommitdiff
path: root/animism-align/frontend/app/views/editor/align
diff options
context:
space:
mode:
Diffstat (limited to 'animism-align/frontend/app/views/editor/align')
-rw-r--r--animism-align/frontend/app/views/editor/align/align.container.js5
-rw-r--r--animism-align/frontend/app/views/editor/align/align.css73
-rw-r--r--animism-align/frontend/app/views/editor/align/components/sidebar/script.component.js22
-rw-r--r--animism-align/frontend/app/views/editor/align/components/sidebar/tableOfContents.component.js29
-rw-r--r--animism-align/frontend/app/views/editor/align/components/sidebar/waveUpload.component.js157
-rw-r--r--animism-align/frontend/app/views/editor/align/components/timeline/cursor.component.js26
-rw-r--r--animism-align/frontend/app/views/editor/align/components/timeline/cursorRegion.component.js28
-rw-r--r--animism-align/frontend/app/views/editor/align/components/timeline/playCursor.component.js31
-rw-r--r--animism-align/frontend/app/views/editor/align/components/timeline/ticks.component.js88
-rw-r--r--animism-align/frontend/app/views/editor/align/components/timeline/waveform.component.js100
-rw-r--r--animism-align/frontend/app/views/editor/align/containers/sidebar.container.js42
-rw-r--r--animism-align/frontend/app/views/editor/align/containers/timeline.container.js196
-rw-r--r--animism-align/frontend/app/views/editor/align/sidebar.css84
13 files changed, 2 insertions, 879 deletions
diff --git a/animism-align/frontend/app/views/editor/align/align.container.js b/animism-align/frontend/app/views/editor/align/align.container.js
index 46954e8..d61eccd 100644
--- a/animism-align/frontend/app/views/editor/align/align.container.js
+++ b/animism-align/frontend/app/views/editor/align/align.container.js
@@ -2,10 +2,9 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import './align.css'
-import './sidebar.css'
-import Timeline from './containers/timeline.container.js'
-import Sidebar from './containers/sidebar.container.js'
+import Timeline from 'app/views/editor/timeline/timeline.container.js'
+import Sidebar from 'app/views/editor/sidebar/sidebar.container.js'
class Container extends Component {
componentDidMount() {
diff --git a/animism-align/frontend/app/views/editor/align/align.css b/animism-align/frontend/app/views/editor/align/align.css
index 58adcf8..83d3777 100644
--- a/animism-align/frontend/app/views/editor/align/align.css
+++ b/animism-align/frontend/app/views/editor/align/align.css
@@ -22,79 +22,6 @@
height: calc(100% - 3rem);
}
-/* Timeline */
-
-canvas {
- display: block;
-}
-.timeline {
- display: flex;
- flex-direction: row;
- position: relative;
- width: 300px;
- cursor: crosshair;
-}
-.timelineColumn {
- position: relative;
-}
-.ticks .tick {
- position: absolute;
- right: 0;
- width: 4px;
- height: 1px;
- background: #ddd;
- pointer-events: none;
-}
-.ticks .tickLabel {
- pointer-events: none;
- position: absolute;
- right: 6px;
- font-size: 12px;
- width: 40px;
- margin-top: -7px;
- text-align: right;
- text-shadow: 0 0 2px #00f;
-}
-.timeline .cursor {
- width: 100%;
- position: absolute;
- left: 0;
- pointer-events: none;
-}
-.timeline .cursor .line {
- width: 100%;
- height: 1px;
- background: #00f;
-}
-.timeline .cursor.playCursor .line {
- background: #ddd;
-}
-.timeline .cursor .tickLabel {
- position: absolute;
- pointer-events: none;
- right: 6px;
- font-size: 12px;
- width: 40px;
- margin-top: -7px;
- text-align: right;
- text-shadow: 0 0 2px #000, 0 0 2px #000, 0 0 2px #000;
- user-select: none;
-}
-.timeline .cursor_region {
- position: absolute;
- left: 0;
- width: 100%;
- border-top: 1px solid #33f;
- border-bottom: 1px solid #33f;
- background: rgba(32,64,255,0.2);
-}
-.timeline .cursor_region .tickLabel {
- position: absolute;
- top: 2px;
- left: 6px;
- user-select: none;
-}
-
/* Audio player */
.playButton {
diff --git a/animism-align/frontend/app/views/editor/align/components/sidebar/script.component.js b/animism-align/frontend/app/views/editor/align/components/sidebar/script.component.js
deleted file mode 100644
index d23b3da..0000000
--- a/animism-align/frontend/app/views/editor/align/components/sidebar/script.component.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React, { Component } from 'react'
-// import { Link } from 'react-router-dom'
-import { connect } from 'react-redux'
-
-import actions from 'app/actions'
-
-const Script = ({ text }) => {
- if (text.loading) return null
- return (
- <textarea
- className='script'
- onChange={e => actions.site.updateText(e.target.value)}
- value={text}
- />
- )
-}
-
-const mapStateToProps = state => ({
- text: state.align.text,
-})
-
-export default connect(mapStateToProps)(Script)
diff --git a/animism-align/frontend/app/views/editor/align/components/sidebar/tableOfContents.component.js b/animism-align/frontend/app/views/editor/align/components/sidebar/tableOfContents.component.js
deleted file mode 100644
index 8fb3d54..0000000
--- a/animism-align/frontend/app/views/editor/align/components/sidebar/tableOfContents.component.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React, { Component } from 'react'
-// import { Link } from 'react-router-dom'
-import { connect } from 'react-redux'
-
-import { ROMAN_NUMERALS } from 'app/constants'
-import actions from 'app/actions'
-
-class TableOfContents extends Component {
- render() {
- const { loading, order, lookup } = this.props.annotation
- if (loading || !order) return null
- const sectionIds = order.filter(id => lookup[id].type === "section_heading")
- return (
- <div className="sidebar-content toc">
- {sectionIds.map((id, i) => (
- <div key={id} onClick={() => actions.align.setScrollPosition(lookup[id].start_ts)}>
- {ROMAN_NUMERALS[i]}{'. '}{lookup[id].text}
- </div>
- ))}
- </div>
- )
- }
-}
-
-const mapStateToProps = state => ({
- annotation: state.annotation.index,
-})
-
-export default connect(mapStateToProps)(TableOfContents)
diff --git a/animism-align/frontend/app/views/editor/align/components/sidebar/waveUpload.component.js b/animism-align/frontend/app/views/editor/align/components/sidebar/waveUpload.component.js
deleted file mode 100644
index 27047ef..0000000
--- a/animism-align/frontend/app/views/editor/align/components/sidebar/waveUpload.component.js
+++ /dev/null
@@ -1,157 +0,0 @@
-import React, { Component } from 'react'
-import { connect } from 'react-redux'
-import extractPeaks from 'webaudio-peaks'
-
-import actions from 'app/actions'
-import { formatSize, timestampHMS } from 'app/utils'
-import { Loader } from 'app/common'
-
-class WaveUpload extends Component {
- state = {
- working: false,
- status: "",
- filename: "",
- duration: 0,
- }
-
- upload(e) {
- e.preventDefault()
- document.body.className = ''
- const files = e.dataTransfer ? e.dataTransfer.files : e.target.files
- let i
- let file
- for (i = 0; i < files.length; i++) {
- file = files[i]
- if (file && file.type.match('image.*')) break
- }
- if (!file) {
- console.log('No file specified')
- return
- }
- this.setState({ working: true, status: "Loading MP3...", filename: file.name, size: file.size, duration: 0 })
- const fileReader = new FileReader()
- fileReader.onload = event => {
- fileReader.onload = null
- this.processAudioFile(file, event.target.result)
- }
- fileReader.readAsArrayBuffer(file)
- }
-
- processAudioFile(audioFile, arrayBuffer) {
- this.setState({ working: true, status: "Extracting peaks..." })
- var audioContext = new (window.AudioContext || window.webkitAudioContext)();
- audioContext.decodeAudioData(arrayBuffer, (audioBuffer) => {
- // buffer, samplesPerPixel, isMono, startOffset, endOffset, bitResolution
- this.setState({ duration: audioBuffer.duration })
- var peaks = extractPeaks(audioBuffer, 441, true);
- console.log(peaks)
- const array = Array.from(peaks.data[0])
- const peaksBlob = new Blob([ JSON.stringify(array) ], {type: "application/json"});
- this.uploadAudioAndPeaks(audioFile, peaksBlob)
- })
- }
-
- uploadAudioAndPeaks(audioFile, peaksBlob) {
- const { episode } = this.props
- const updatedEpisode = { ...episode }
- this.setState({ status: "Removing old files..." })
- this.destroyTaggedFile('peaks')
- this.destroyTaggedFile('audio')
- .then(() => {
- return this.uploadTaggedFile(peaksBlob, 'peaks', 'episode-' + this.props.episode.id + '-peaks.json')
- })
- .then(peaksResult => {
- updatedEpisode.settings.peaks = peaksResult
- return this.uploadTaggedFile(audioFile, 'audio', this.state.filename)
- })
- .then(audioResult => {
- updatedEpisode.settings.audio = audioResult
- return actions.episode.update(updatedEpisode)
- })
- .then(res => {
- this.setState({ status: "Upload complete", working: false })
- })
- }
-
- uploadTaggedFile(file, tag, fn) {
- return new Promise((resolve, reject) => {
- this.setState({ status: "Uploading " + tag + "..." })
- const uploadData = {
- tag,
- file,
- __file_filename: fn,
- username: this.props.currentUser.username,
- }
- console.log(uploadData)
- return actions.upload.upload(uploadData).then(data => {
- console.log(data)
- resolve(data.res)
- })
- })
- }
-
- destroyTaggedFile(tag) {
- return new Promise((resolve, reject) => {
- if (!this.props.episode.settings[tag]) {
- return resolve();
- }
- actions.upload.destroy(this.props.episode.settings[tag])
- .then(() => {
- console.log('Destroy successful')
- resolve()
- })
- .catch(() => {
- console.log('Error deleting the image')
- reject()
- })
- })
- }
-
- render() {
- const { episode, peaks } = this.props
- return (
- <div className="sidebar-content wave-upload">
- {peaks.length && (
- <div>
- Peaks: {peaks.length}
- </div>
- )}
- <div className="uploadButton">
- <button>
- <span>
- {episode.settings.audio
- ? "Upload a new audio file"
- : "Upload an audio file"
- }
- </span>
- </button>
- <input
- type="file"
- accept="audio/mp3"
- onChange={this.upload.bind(this)}
- required={this.props.required}
- />
- </div>
- <small>Upload an MP3, encoded 192kbit constant bitrate, 44.1kHz stereo</small>
- {this.state.status && (
- <div className='status'>
- {this.state.working && <Loader />}
- <div className='status-message'>{this.state.status}</div>
- {this.state.filename && <small>{this.state.filename}</small>}
- {this.state.size && <small>{'Size: '}{formatSize(this.state.size)}</small>}
- {!!this.state.duration && <small>{'Duration: '}{timestampHMS(this.state.duration)}</small>}
- </div>
- )}
- </div>
- )
- }
-}
-
-const mapStateToProps = state => ({
- peaks: state.align.peaks,
- currentUser: state.auth.user,
- project: state.site.project,
- episode: state.site.episode,
-})
-
-export default connect(mapStateToProps)(WaveUpload)
diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/cursor.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/cursor.component.js
deleted file mode 100644
index 4a94100..0000000
--- a/animism-align/frontend/app/views/editor/align/components/timeline/cursor.component.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React, { Component } from 'react'
-
-import { ZOOM_STEPS } from 'app/constants'
-import { timestamp } from 'app/utils'
-
-const Cursor = ({ timeline, annotation }) => {
- const { start_ts, zoom, cursor_ts, duration } = timeline
- const ts = annotation.start_ts || cursor_ts
- const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1
- const y = (ts - start_ts) / secondsPerPixel
- return (
- <div
- className='cursor'
- style={{
- top: y,
- }}
- >
- <div className='line' />
- <div className='tickLabel'>
- {timestamp(ts, 1)}
- </div>
- </div>
- )
-}
-
-export default Cursor \ No newline at end of file
diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/cursorRegion.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/cursorRegion.component.js
deleted file mode 100644
index a0c9bd7..0000000
--- a/animism-align/frontend/app/views/editor/align/components/timeline/cursorRegion.component.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react'
-
-import { ZOOM_STEPS } from 'app/constants'
-import { timestamp } from 'app/utils'
-
-const CursorRegion = ({ timeline }) => {
- const { start_ts, zoom, cursor_region } = timeline
- if (!cursor_region) return null
- const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1
- const duration = cursor_region.b_ts - cursor_region.a_ts
- const y = (cursor_region.a_ts - start_ts) / secondsPerPixel
- const height = (duration) / secondsPerPixel
- return (
- <div
- className='cursor_region'
- style={{
- top: y,
- height,
- }}
- >
- <div className='tickLabel'>
- {timestamp(duration, 1, true)}
- </div>
- </div>
- )
-}
-
-export default CursorRegion \ No newline at end of file
diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/playCursor.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/playCursor.component.js
deleted file mode 100644
index 80da31f..0000000
--- a/animism-align/frontend/app/views/editor/align/components/timeline/playCursor.component.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React, { Component } from 'react'
-// import { Link } from 'react-router-dom'
-import { connect } from 'react-redux'
-
-import { ZOOM_STEPS } from 'app/constants'
-import { timestamp } from 'app/utils'
-
-const PlayCursor = ({ timeline, audio }) => {
- const { start_ts, zoom, duration } = timeline
- const { play_ts } = audio
- const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1
- const y = (play_ts - start_ts) / secondsPerPixel
- // console.log(play_ts, y)
- return (
- <div
- className='cursor playCursor'
- style={{
- top: y,
- }}
- >
- <div className='line' />
- </div>
- )
-}
-
-const mapStateToProps = state => ({
- timeline: state.align.timeline,
- audio: state.audio,
-})
-
-export default connect(mapStateToProps)(PlayCursor)
diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/ticks.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/ticks.component.js
deleted file mode 100644
index 4530863..0000000
--- a/animism-align/frontend/app/views/editor/align/components/timeline/ticks.component.js
+++ /dev/null
@@ -1,88 +0,0 @@
-import React, { Component } from 'react'
-
-import { ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS, INNER_HEIGHT } from 'app/constants'
-import { timestamp } from 'app/utils'
-
-export default class Ticks extends Component {
- render() {
- let { start_ts, zoom, duration } = this.props.timeline
-
- let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step
-
- let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel
-
- let timeMin = start_ts
- let timeMax = Math.min(start_ts + widthTimeDuration, duration)
- let timeWidth = timeMax - timeMin
-
- let pixelMin = timeMin / secondsPerPixel
-
- let secondsPerLabel = ZOOM_LABEL_STEPS[zoom] // secs
- let pixelsPerLabel = secondsPerLabel / secondsPerPixel
- let secondsPerTick = ZOOM_TICK_STEPS[zoom]
- let pixelsPerTick = secondsPerTick / secondsPerPixel
-
- let startOffset = pixelsPerLabel - (pixelMin % pixelsPerLabel)
- let startTiming = (pixelMin + startOffset) * secondsPerPixel
-
- let labelCount = Math.ceil(INNER_HEIGHT / pixelsPerLabel) + 1
- let offset, timing, tickLabels = [], ticks = []
- for (var i = -1; i < labelCount; i++) {
- offset = i * pixelsPerLabel + startOffset
- if (offset > INNER_HEIGHT) continue
- timing = i * secondsPerLabel + startTiming
- if (timing > duration) {
- break
- }
- tickLabels.push(
- <div className='tickLabel' key={"tickLabel_" + i}
- style={{
- top: Math.floor(offset)
- }}>
- {timestamp(timing)}
- </div>
- )
- }
-
- let durationOffset = duration / secondsPerPixel - pixelMin
- if (timing > duration) {
- tickLabels.push(
- <div className='tickLabel tickLabelTotal' key={"tickLabel_total"}
- style={{
- top: durationOffset
- }}>
- {timestamp(duration, 1)}
- </div>
- )
- ticks.push(
- <div className='tick' key={"tick_total"}
- style={{
- top: Math.floor(durationOffset),
- }}
- />
- )
- }
- let tickCount = Math.ceil(INNER_HEIGHT / pixelsPerTick) + 6
- for (var i = 0; i < tickCount; i += 1) {
- offset = i * pixelsPerTick + startOffset - pixelsPerLabel
- if (offset > durationOffset) {
- break
- }
- ticks.push(
- <div className='tick' key={"tick_" + i}
- style={{
- top: Math.floor(offset),
- }}
- />
- )
- }
- // console.log(ticks.length)
-
- return (
- <div className='ticks'>
- {ticks}
- {tickLabels}
- </div>
- )
- }
-}
diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/waveform.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/waveform.component.js
deleted file mode 100644
index 0161129..0000000
--- a/animism-align/frontend/app/views/editor/align/components/timeline/waveform.component.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import React, { Component } from 'react'
-// import { Link } from 'react-router-dom'
-import { connect } from 'react-redux'
-
-import actions from 'app/actions'
-
-import {
- WAVEFORM_SIZE, INNER_HEIGHT,
- ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS,
-} from 'app/constants'
-
-class Waveform extends Component {
- constructor(props){
- super(props)
- this.canvasRef = React.createRef()
- }
- componentDidMount() {
- this.resize()
- this.draw()
- }
- componentDidUpdate() {
- this.draw()
- }
- resize() {
- const canvas = this.canvasRef.current
- canvas.width = WAVEFORM_SIZE
- canvas.height = INNER_HEIGHT
- }
- draw() {
- const canvas = this.canvasRef.current
- const ctx = canvas.getContext('2d')
- const h = INNER_HEIGHT
- this.clearCanvas(ctx, h)
- this.drawCurve(ctx, h)
- }
- clearCanvas(ctx, h) {
- const w = WAVEFORM_SIZE
- ctx.clearRect(0, 0, w, h)
- ctx.fillStyle = 'rgba(0,0,0,0.5)'
- ctx.fillRect(0, 0, w, h)
- ctx.fillStyle = 'rgba(64,128,192,0.5)'
- }
- drawCurve(ctx, h) {
- let { peaks, timeline } = this.props
- let { start_ts, zoom, duration } = timeline
-
- let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step
- let stepsPerPixel = ZOOM_STEPS[zoom] // 0.1 sec / step
-
- let widthTimeDuration = h * secondsPerPixel // secs per pixel
-
- let timeMin = Math.round(start_ts / secondsPerPixel) * secondsPerPixel
- let timeMax = Math.min(timeMin + widthTimeDuration, duration)
- let timeWidth = timeMax - timeMin
-
- let stepMin = Math.floor(timeMin * 10)
- let pixelWidth = Math.ceil(timeWidth / secondsPerPixel)
-
- let i = 0
- let step = stepMin
- let waveformPeak = WAVEFORM_SIZE / 2
- let origin = (1 - peaks[step]) * waveformPeak
- let y
- let peak
- // console.log(stepMin, pixelWidth * stepsPerPixel + stepMin)
- ctx.beginPath()
- ctx.moveTo(origin, 0)
- for (i = 0; i < pixelWidth; i++) {
- step = i * stepsPerPixel + stepMin
- peak = peaks[step]
- y = (1 - peak) * waveformPeak
- ctx.lineTo(y, i)
- }
- for (i = pixelWidth - 1; i > 0; i--) {
- step = i * stepsPerPixel + stepMin
- peak = peaks[step]
- y = (1 + peak) * waveformPeak
- ctx.lineTo(y, i)
- }
- ctx.lineTo(origin, 0)
- ctx.fillStyle = 'rgba(255,255,255,0.8)'
- ctx.fill()
- }
- render() {
- return (
- <canvas
- ref={this.canvasRef}
- onMouseDown={this.props.onMouseDown}
- onMouseUp={this.props.onMouseUp}
- />
- )
- }
-}
-
-const mapStateToProps = state => ({
- timeline: state.align.timeline,
- peaks: state.align.peaks,
-})
-
-export default connect(mapStateToProps)(Waveform)
diff --git a/animism-align/frontend/app/views/editor/align/containers/sidebar.container.js b/animism-align/frontend/app/views/editor/align/containers/sidebar.container.js
deleted file mode 100644
index 8ff8181..0000000
--- a/animism-align/frontend/app/views/editor/align/containers/sidebar.container.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React, { Component } from 'react'
-import { connect } from 'react-redux'
-// import { Link } from 'react-router-dom'
-
-import actions from 'app/actions'
-
-import Script from '../components/sidebar/script.component.js'
-import TableOfContents from '../components/sidebar/tableOfContents.component.js'
-import WaveUpload from '../components/sidebar/waveUpload.component.js'
-
-class Sidebar extends Component {
- state = {
- mode: "toc",
- }
- componentDidMount(){
- if (!this.props.peaks.length) {
- this.setState({ mode: "wav" })
- }
- }
-
- render() {
- const { mode } = this.state
- return (
- <div className='sidebar'>
- <div className='buttons'>
- <button onClick={() => this.setState({ mode: "txt" })}>text</button>
- <button onClick={() => this.setState({ mode: "wav" })}>wav</button>
- <button onClick={() => this.setState({ mode: "toc" })}>contents</button>
- </div>
- {mode === 'toc' && <TableOfContents />}
- {mode === 'txt' && <Script />}
- {mode === 'wav' && <WaveUpload />}
- </div>
- )
- }
-}
-
-const mapStateToProps = state => ({
- peaks: state.align.peaks,
-})
-
-export default connect(mapStateToProps)(Sidebar)
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
deleted file mode 100644
index dbef60f..0000000
--- a/animism-align/frontend/app/views/editor/align/containers/timeline.container.js
+++ /dev/null
@@ -1,196 +0,0 @@
-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/annotation/annotations.container'
-import Waveform from '../components/timeline/waveform.component'
-import Ticks from '../components/timeline/ticks.component'
-import Cursor from '../components/timeline/cursor.component'
-import PlayCursor from '../components/timeline/playCursor.component'
-import CursorRegion from '../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.align.text,
-})
-
-export default connect(mapStateToProps)(Timeline)
diff --git a/animism-align/frontend/app/views/editor/align/sidebar.css b/animism-align/frontend/app/views/editor/align/sidebar.css
deleted file mode 100644
index 7c19797..0000000
--- a/animism-align/frontend/app/views/editor/align/sidebar.css
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Sidebar */
-
-.sidebar {
- position: absolute;
- top: 0;
- right: 0;
- width: 15rem;
- z-index: 8;
- padding-top: 2rem;
-}
-
-/* sidebar header */
-
-.sidebar .buttons {
- position: absolute;
- top: 0;
- right: 0;
- width: 15rem;
- height: 2rem;
- z-index: 9;
- display: flex;
- justify-content: flex-end;
- align-items: flex-start;
- background: #111;
-}
-.sidebar .buttons button {
- border: 0;
- background: transparent;
-}
-.sidebar .buttons button:hover {
- background: #333;
-}
-.sidebar-content {
- background: #222;
- width: 15rem;
- padding: 0.5rem 0;
-}
-
-/* wavefile upload */
-
-.sidebar-content.wave-upload {
- padding: 0.5rem 0.75rem 1.5rem 0.75rem;
-}
-
-.wave-upload .uploadButton {
- position: relative;
- text-align: center;
- margin: 1rem 0;
-}
-.wave-upload small {
- display: block;
- text-align: center;
-}
-.wave-upload .status {
- padding: 1rem 0 0 0;
- text-align: center;
-}
-.wave-upload .circular-loader {
- margin: 0 auto 1rem auto;
-}
-.wave-upload .status-message {
-}
-.wave-upload .status small {
- display: block;
- margin-top: 0.25rem;
-}
-
-/* table of contents */
-
-.toc div {
- width: 15rem;
- padding: 0.25rem 0.75rem;
- cursor: pointer;
-}
-.toc div:hover {
- background: #213;
-}
-
-
-/* script */
-
-.sidebar textarea {
- height: calc(100vh - 5.15rem);
-}