summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2020-07-04 17:24:21 +0200
committerJules Laplace <julescarbon@gmail.com>2020-07-04 17:24:21 +0200
commit82c2ac11f4ef2112a0332f2f9c7cdd52444f0d2a (patch)
tree38ce15d8cb9dd71ec756a6b3a9d7cbebe5ff084a
parente973412b5ea29685f4fa260d8eb44baae095fb81 (diff)
displaying annotation list, click to select, doubleclick to show form, updating annotations
-rw-r--r--animism-align/cli/app/sql/models/annotation.py2
-rw-r--r--animism-align/cli/app/sql/models/paragraph.py4
-rw-r--r--animism-align/cli/app/sql/versions/202007041642_create_database.py (renamed from animism-align/cli/app/sql/versions/202007041633_create_database.py)8
-rw-r--r--animism-align/frontend/app.js1
-rw-r--r--animism-align/frontend/views/align/align.actions.js8
-rw-r--r--animism-align/frontend/views/align/align.css20
-rw-r--r--animism-align/frontend/views/align/components/annotations/annotation.form.js6
-rw-r--r--animism-align/frontend/views/align/components/annotations/annotation.index.js83
-rw-r--r--animism-align/frontend/views/align/components/annotations/annotation.types.js40
-rw-r--r--animism-align/frontend/views/align/containers/annotations.container.js8
-rw-r--r--animism-align/frontend/views/align/containers/timeline.container.js3
11 files changed, 171 insertions, 12 deletions
diff --git a/animism-align/cli/app/sql/models/annotation.py b/animism-align/cli/app/sql/models/annotation.py
index 6cc476c..cc53bd6 100644
--- a/animism-align/cli/app/sql/models/annotation.py
+++ b/animism-align/cli/app/sql/models/annotation.py
@@ -27,7 +27,7 @@ class Annotation(Base):
'paragraph_id': self.paragraph_id,
'start_ts': self.start_ts,
'end_ts': self.end_ts,
- 'sentence': self.description,
+ 'text': self.text,
'settings': self.settings,
}
diff --git a/animism-align/cli/app/sql/models/paragraph.py b/animism-align/cli/app/sql/models/paragraph.py
index 7c7bcd7..790c9f0 100644
--- a/animism-align/cli/app/sql/models/paragraph.py
+++ b/animism-align/cli/app/sql/models/paragraph.py
@@ -14,12 +14,16 @@ class Paragraph(Base):
__tablename__ = 'paragraph'
id = Column(Integer, primary_key=True)
type = Column(String(16, convert_unicode=True), nullable=False)
+ start_ts = Column(Float, nullable=False)
+ end_ts = Column(Float, nullable=True)
settings = Column(JSON, default={}, nullable=True)
def toJSON(self):
return {
'id': self.id,
'type': self.type,
+ 'start_ts': self.start_ts,
+ 'end_ts': self.end_ts,
'settings': self.settings,
}
diff --git a/animism-align/cli/app/sql/versions/202007041633_create_database.py b/animism-align/cli/app/sql/versions/202007041642_create_database.py
index f8336e5..bec0cc3 100644
--- a/animism-align/cli/app/sql/versions/202007041633_create_database.py
+++ b/animism-align/cli/app/sql/versions/202007041642_create_database.py
@@ -1,8 +1,8 @@
"""create database
-Revision ID: f8936a84e584
+Revision ID: 4ede1524f909
Revises:
-Create Date: 2020-07-04 16:33:01.643193
+Create Date: 2020-07-04 16:42:00.443043
"""
from alembic import op
@@ -11,7 +11,7 @@ import sqlalchemy_utc
# revision identifiers, used by Alembic.
-revision = 'f8936a84e584'
+revision = '4ede1524f909'
down_revision = None
branch_labels = None
depends_on = None
@@ -22,6 +22,8 @@ def upgrade():
op.create_table('paragraph',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('type', sa.String(length=16, _expect_unicode=True), nullable=False),
+ sa.Column('start_ts', sa.Float(), nullable=False),
+ sa.Column('end_ts', sa.Float(), nullable=True),
sa.Column('settings', sa.JSON(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
diff --git a/animism-align/frontend/app.js b/animism-align/frontend/app.js
index 975b5b2..1c40c09 100644
--- a/animism-align/frontend/app.js
+++ b/animism-align/frontend/app.js
@@ -19,6 +19,7 @@ export default class App extends Component {
componentDidMount() {
actions.site.loadText()
actions.site.loadPeaks()
+ actions.annotation.index()
}
render() {
return (
diff --git a/animism-align/frontend/views/align/align.actions.js b/animism-align/frontend/views/align/align.actions.js
index b3883ae..b10f257 100644
--- a/animism-align/frontend/views/align/align.actions.js
+++ b/animism-align/frontend/views/align/align.actions.js
@@ -38,11 +38,19 @@ export const showNewAnnotationForm = (start_ts, text) => dispatch => {
data: {
id: 'new',
start_ts,
+ end_ts: 0.0,
text: croppedText,
type: 'sentence',
+ settings: {},
}
})
}
+export const showEditAnnotationForm = (annotation) => dispatch => {
+ dispatch({
+ type: types.align.set_temporary_annotation,
+ data: annotation,
+ })
+}
export const updateAnnotationForm = (key, value) => dispatch => {
dispatch({ type: types.align.update_temporary_annotation, key, value })
diff --git a/animism-align/frontend/views/align/align.css b/animism-align/frontend/views/align/align.css
index 323dc9a..e4629b7 100644
--- a/animism-align/frontend/views/align/align.css
+++ b/animism-align/frontend/views/align/align.css
@@ -112,4 +112,22 @@ canvas {
}
.annotationForm .row {
justify-content: space-between;
-} \ No newline at end of file
+}
+.annotationIndex {
+ width: 305px;
+}
+.annotation {
+ position: absolute;
+ left: 5px;
+ max-width: 300px;
+ padding: 0.25rem 0.375rem;
+ box-shadow: 2px 2px 4px rgba(0,0,0,0.5);
+ border-radius: 2px;
+ cursor: pointer;
+}
+.annotation.sentence {
+ background-color: #83b;
+}
+.annotation.header {
+ background-color: #838;
+}
diff --git a/animism-align/frontend/views/align/components/annotations/annotation.form.js b/animism-align/frontend/views/align/components/annotations/annotation.form.js
index 9432948..b62c36e 100644
--- a/animism-align/frontend/views/align/components/annotations/annotation.form.js
+++ b/animism-align/frontend/views/align/components/annotations/annotation.form.js
@@ -24,9 +24,13 @@ class AnnotationForm extends Component {
this.handleSubmit = this.handleSubmit.bind(this)
}
handleKeyDown(e) {
+ if (e.keyCode === 27) { // escape
+ actions.align.hideAnnotationForm()
+ return
+ }
+ // console.log(e.keyCode)
if (!e.metaKey && !e.ctrlKey) return
let { start_ts } = this.props.annotation
- console.log(e.keyCode)
switch (e.keyCode) {
case 38: // up
e.preventDefault()
diff --git a/animism-align/frontend/views/align/components/annotations/annotation.index.js b/animism-align/frontend/views/align/components/annotations/annotation.index.js
new file mode 100644
index 0000000..7b562c2
--- /dev/null
+++ b/animism-align/frontend/views/align/components/annotations/annotation.index.js
@@ -0,0 +1,83 @@
+import React, { Component } from 'react'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+
+import actions from '../../../../actions'
+
+import { ZOOM_STEPS } from '../../constants'
+import { clamp } from '../../../../util'
+import { positionToTime, timeToPosition } from '../../align.util'
+
+import { AnnotationElementLookup } from './annotation.types'
+
+class AnnotationIndex extends Component {
+ state = {
+ items: [],
+ }
+ constructor(props){
+ super(props)
+ this.handleClick = this.handleClick.bind(this)
+ }
+ componentDidUpdate(prevProps) {
+ if (this.props.index.loading) return
+ if (prevProps.timeline !== this.props.timeline || prevProps.index !== this.props.index) {
+ this.update()
+ }
+ }
+ update() {
+ let { timeline, index } = this.props
+ let { start_ts, zoom, duration } = this.props.timeline
+ const { order, lookup } = index
+
+ let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step
+ let widthTimeDuration = window.innerHeight * secondsPerPixel // secs per pixel
+
+ let timeMin = start_ts - 30.0
+ let timeMax = Math.min(start_ts + widthTimeDuration, duration)
+
+ const items = order.filter(id => {
+ const { start_ts: ts } = lookup[id]
+ return (timeMin < ts && ts < timeMax)
+ }).map(id => lookup[id])
+ this.setState({ items })
+ }
+ handleClick(annotation) {
+ actions.audio.seek(annotation.start_ts)
+ }
+ handleDoubleClick(annotation) {
+ actions.align.showEditAnnotationForm(annotation)
+ }
+ render() {
+ const { timeline } = this.props
+ const { start_ts } = timeline
+ const { items } = this.state
+ return (
+ <div className='annotationIndex'>
+ {items.map(annotation => {
+ const { id, type, start_ts } = annotation
+ const AnnotationElement = AnnotationElementLookup[type]
+ const y = timeToPosition(start_ts, timeline)
+ return (
+ <AnnotationElement
+ key={id}
+ y={y}
+ annotation={annotation}
+ onClick={this.handleClick}
+ onDoubleClick={this.handleDoubleClick}
+ />
+ )
+ })}
+ </div>
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ timeline: state.align.timeline,
+ index: state.annotation.index,
+})
+
+const mapDispatchToProps = dispatch => ({
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(AnnotationIndex)
diff --git a/animism-align/frontend/views/align/components/annotations/annotation.types.js b/animism-align/frontend/views/align/components/annotations/annotation.types.js
new file mode 100644
index 0000000..465844f
--- /dev/null
+++ b/animism-align/frontend/views/align/components/annotations/annotation.types.js
@@ -0,0 +1,40 @@
+import React, { Component } from 'react'
+
+import actions from '../../../../actions'
+
+import { ZOOM_STEPS } from '../../constants'
+import { clamp } from '../../../../util'
+import { positionToTime, timeToPosition } from '../../align.util'
+
+export const AnnotationSentence = ({ y, annotation, onClick, onDoubleClick }) => {
+ const { start_ts, text } = annotation
+ return (
+ <div
+ className='annotation sentence'
+ style={{ top: y }}
+ onClick={() => onClick(annotation)}
+ onDoubleClick={() => onDoubleClick(annotation)}
+ >
+ {text}
+ </div>
+ )
+}
+
+export const AnnotationHeader = ({ y, annotation, onClick, onDoubleClick }) => {
+ const { start_ts, text } = annotation
+ return (
+ <div
+ className='annotation header'
+ style={{ top: y }}
+ onClick={() => onClick(annotation)}
+ onDoubleClick={() => onDoubleClick(annotation)}
+ >
+ {text}
+ </div>
+ )
+}
+
+export const AnnotationElementLookup = {
+ sentence: AnnotationSentence,
+ header: AnnotationHeader,
+} \ No newline at end of file
diff --git a/animism-align/frontend/views/align/containers/annotations.container.js b/animism-align/frontend/views/align/containers/annotations.container.js
index e32757b..b6cdace 100644
--- a/animism-align/frontend/views/align/containers/annotations.container.js
+++ b/animism-align/frontend/views/align/containers/annotations.container.js
@@ -11,15 +11,16 @@ import { clamp } from '../../../util'
import { positionToTime } from '../align.util'
import AnnotationForm from '../components/annotations/annotation.form'
+import AnnotationIndex from '../components/annotations/annotation.index'
class Annotations extends Component {
constructor(props){
super(props)
- // this.handleKeydown = this.handleKeydown.bind(this)
}
render() {
return (
<div className='annotations'>
+ <AnnotationIndex />
{this.props.annotation.start_ts &&
<AnnotationForm />
}
@@ -28,11 +29,6 @@ class Annotations extends Component {
}
}
-/*
-- get the first sentence from the text
-- display the form at that point
-*/
-
const mapStateToProps = state => ({
timeline: state.align.timeline,
annotation: state.align.annotation,
diff --git a/animism-align/frontend/views/align/containers/timeline.container.js b/animism-align/frontend/views/align/containers/timeline.container.js
index 4167d2d..23b9435 100644
--- a/animism-align/frontend/views/align/containers/timeline.container.js
+++ b/animism-align/frontend/views/align/containers/timeline.container.js
@@ -53,6 +53,9 @@ class Timeline extends Component {
} else {
// console.log(e.keyCode)
switch (e.keyCode) {
+ case 27: // escape
+ actions.align.hideAnnotationForm()
+ break
case 32: // spacebar
actions.audio.toggle()
break