diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-07-04 16:37:19 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-07-04 16:37:19 +0200 |
| commit | e973412b5ea29685f4fa260d8eb44baae095fb81 (patch) | |
| tree | 0e859ab0541187ed769e654663730834b998f3d1 /animism-align/frontend | |
| parent | 3e1c6f81bbdad758b8756955fce82da49a564611 (diff) | |
rename timestamp to annotation
Diffstat (limited to 'animism-align/frontend')
| -rw-r--r-- | animism-align/frontend/actions.js | 9 | ||||
| -rw-r--r-- | animism-align/frontend/api/index.js | 6 | ||||
| -rw-r--r-- | animism-align/frontend/store.js | 5 | ||||
| -rw-r--r-- | animism-align/frontend/types.js | 4 | ||||
| -rw-r--r-- | animism-align/frontend/views/align/align.actions.js | 11 | ||||
| -rw-r--r-- | animism-align/frontend/views/align/align.reducer.js | 21 | ||||
| -rw-r--r-- | animism-align/frontend/views/align/components/annotations/annotation.form.js | 95 | ||||
| -rw-r--r-- | animism-align/frontend/views/align/containers/timeline.container.js | 2 | ||||
| -rw-r--r-- | animism-align/frontend/views/annotation/annotation.reducer.js (renamed from animism-align/frontend/views/timestamp/timestamp.reducer.js) | 6 | ||||
| -rw-r--r-- | animism-align/frontend/views/timestamp/components/timestamp.form.js | 153 | ||||
| -rw-r--r-- | animism-align/frontend/views/timestamp/containers/timestamp.edit.js | 53 | ||||
| -rw-r--r-- | animism-align/frontend/views/timestamp/containers/timestamp.index.js | 53 | ||||
| -rw-r--r-- | animism-align/frontend/views/timestamp/containers/timestamp.new.js | 44 |
13 files changed, 92 insertions, 370 deletions
diff --git a/animism-align/frontend/actions.js b/animism-align/frontend/actions.js index 1b5f15e..4153296 100644 --- a/animism-align/frontend/actions.js +++ b/animism-align/frontend/actions.js @@ -1,5 +1,6 @@ import { bindActionCreators } from 'redux' -import { actions as crudActions } from './api' +// import { actions as crudActions } from './api' +import { crud_actions } from './api/crud.actions' import * as audioActions from './views/audio/audio.actions' import * as alignActions from './views/align/align.actions' @@ -7,6 +8,12 @@ import * as siteActions from './views/site/site.actions' import { store } from './store' +const crudActions = [ + 'paragraph', + 'annotation', + 'upload', +].reduce((a,b) => (a[b] = crud_actions(b)) && a, {}) + export default Object.keys(crudActions) .map(a => [a, crudActions[a]]) diff --git a/animism-align/frontend/api/index.js b/animism-align/frontend/api/index.js index 96c4e5c..05f6b8b 100644 --- a/animism-align/frontend/api/index.js +++ b/animism-align/frontend/api/index.js @@ -15,9 +15,3 @@ so you can do ... */ export { util } - -export const actions = [ - 'paragraph', - 'timestamp', - 'upload', -].reduce((a,b) => (a[b] = crud_actions(b)) && a, {}) diff --git a/animism-align/frontend/store.js b/animism-align/frontend/store.js index accf1b1..24542ff 100644 --- a/animism-align/frontend/store.js +++ b/animism-align/frontend/store.js @@ -9,7 +9,7 @@ import uploadReducer from './views/upload/upload.reducer' import alignReducer from './views/align/align.reducer' import audioReducer from './views/audio/audio.reducer' import paragraphReducer from './views/paragraph/paragraph.reducer' -import timestampReducer from './views/timestamp/timestamp.reducer' +import annotationReducer from './views/annotation/annotation.reducer' import siteReducer from './views/site/site.reducer' // import collectionReducer from './views/collection/collection.reducer' @@ -22,8 +22,7 @@ const createRootReducer = history => ( align: alignReducer, audio: audioReducer, paragraph: paragraphReducer, - timestamp: timestampReducer, - // collection: collectionReducer, + annotation: annotationReducer, }) ) diff --git a/animism-align/frontend/types.js b/animism-align/frontend/types.js index d888a0a..45c9caf 100644 --- a/animism-align/frontend/types.js +++ b/animism-align/frontend/types.js @@ -5,11 +5,13 @@ export const api = crud_type('api', []) export const upload = crud_type('upload', []) export const peaks = crud_type('peaks', []) export const text = crud_type('text', []) -export const timestamp = crud_type('timestamp', []) +export const annotation = crud_type('annotation', []) export const paragraph = crud_type('paragraph', []) export const align = crud_type('align', [ 'set_display_setting', 'set_temporary_annotation', + 'update_temporary_annotation', + 'update_temporary_annotation_settings', ]) export const audio = with_type('audio', [ diff --git a/animism-align/frontend/views/align/align.actions.js b/animism-align/frontend/views/align/align.actions.js index 82e4799..b3883ae 100644 --- a/animism-align/frontend/views/align/align.actions.js +++ b/animism-align/frontend/views/align/align.actions.js @@ -25,7 +25,7 @@ export const setCursor = cursor_ts => dispatch => ( dispatch({ type: types.align.set_display_setting, key: 'cursor_ts', value: cursor_ts }) ) -export const showNewTimestampForm = (start_ts, text) => dispatch => { +export const showNewAnnotationForm = (start_ts, text) => dispatch => { let croppedText; if (store.getState().align.annotation.start_ts) { croppedText = store.getState().align.annotation.text @@ -44,6 +44,13 @@ export const showNewTimestampForm = (start_ts, text) => dispatch => { }) } +export const updateAnnotationForm = (key, value) => dispatch => { + dispatch({ type: types.align.update_temporary_annotation, key, value }) +} +export const updateAnnotationSettings = (key, value) => dispatch => { + dispatch({ type: types.align.update_temporary_annotation_settings, key, value }) +} + const cutFirstSentence = text => { const textToCrop = text.trim().replace("\n", " ").split("\n")[0] let cropIndex = textToCrop.indexOf('. ') + 1 @@ -54,7 +61,7 @@ const cutFirstSentence = text => { return croppedText } -export const hideTimestampForm = () => dispatch => { +export const hideAnnotationForm = () => dispatch => { dispatch({ type: types.align.set_temporary_annotation, data: {} diff --git a/animism-align/frontend/views/align/align.reducer.js b/animism-align/frontend/views/align/align.reducer.js index 9064b56..f080c24 100644 --- a/animism-align/frontend/views/align/align.reducer.js +++ b/animism-align/frontend/views/align/align.reducer.js @@ -41,6 +41,27 @@ export default function alignReducer(state = initialState, action) { annotation: action.data, } + case types.align.update_temporary_annotation: + return { + ...state, + annotation: { + ...state.annotation, + [action.key]: action.value, + } + } + + case types.align.update_temporary_annotation_settings: + return { + ...state, + annotation: { + ...state.annotation, + settings: { + ...state.annotation.settings, + [action.key]: action.value, + } + } + } + default: return state } 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 03e44e1..9432948 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.form.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.form.js @@ -11,36 +11,42 @@ import { clamp } from '../../../../util' import { timeToPosition } from '../../align.util' import { Select } from '../../../../common' -const TIMESTAMP_TYPES = ['sentence', 'header'].map(name => ({ name, label: name })) +const ANNOTATION_TYPES = [ + 'sentence', 'header' +].map(name => ({ name, label: name })) class AnnotationForm extends Component { - state = { - data: {}, - } constructor(props){ super(props) this.handleChange = this.handleChange.bind(this) this.handleSelect = this.handleSelect.bind(this) this.handleKeyDown = this.handleKeyDown.bind(this) - } - componentDidMount(){ - this.setState({ - data: { ...this.props.annotation }, - }) - } - componentDidUpdate(prevProps){ - if (this.props.annotation !== prevProps.annotation) { - this.setState({ - data: { - ...this.props.annotation, - text: this.state.data.text, - type: this.state.data.text, - }, - }) - } + this.handleSubmit = this.handleSubmit.bind(this) } handleKeyDown(e) { + if (!e.metaKey && !e.ctrlKey) return + let { start_ts } = this.props.annotation + console.log(e.keyCode) switch (e.keyCode) { + case 38: // up + e.preventDefault() + start_ts -= 0.1 + actions.align.updateAnnotationForm('start_ts', start_ts) + actions.audio.seek(start_ts) + actions.align.setCursor(start_ts) + break + case 40: // down + e.preventDefault() + start_ts += 0.1 + actions.align.updateAnnotationForm('start_ts', start_ts) + actions.audio.seek(start_ts) + actions.align.setCursor(start_ts) + break + case 83: // ctrl-S + e.preventDefault() + this.handleSubmit() + default: + break } } handleChange(e) { @@ -48,62 +54,57 @@ class AnnotationForm extends Component { this.handleSelect(name, value) } handleSelect(name, value) { - this.setState({ - data: { - ...this.state.data, - [name]: value, - } - }) + actions.align.updateAnnotationForm(name, value) } handleSubmit() { - const { data } = this.state - if (data.id === 'new') { - delete data.id - actions.graph.create(data) + const { annotation } = this.props + if (annotation.id === 'new') { + delete annotation.id + actions.annotation.create(annotation) .then(response => { console.log(response) - actions.align.hideTimestampForm() + actions.align.hideAnnotationForm() }) } else { - actions.graph.update(data) + actions.annotation.update(annotation) .then(response => { console.log(response) - actions.align.hideTimestampForm() + actions.align.hideAnnotationForm() }) } } render() { - const { timeline } = this.props - const { data } = this.state - if (!data.start_ts) return <div></div> + const { timeline, annotation } = this.props + if (!annotation.start_ts) return <div></div> return ( <div className='annotationForm' style={{ - top: timeToPosition(data.start_ts, timeline), + top: timeToPosition(annotation.start_ts, timeline), }} > - {data.type === 'sentence' && this.renderTextarea()} - {data.type === 'header' && this.renderTextarea()} + {annotation.type === 'sentence' && this.renderTextarea()} + {annotation.type === 'header' && this.renderTextarea()} <div className='row'> <Select name='type' - selected={data.type} - options={TIMESTAMP_TYPES} + selected={annotation.type} + options={ANNOTATION_TYPES} defaultOption='text' onChange={this.handleSelect} /> - <button>Save</button> + <button onClick={this.handleSubmit}>Save</button> </div> </div> ) } renderTextarea() { - const { data } = this.state + const { annotation } = this.props return ( <div> <textarea - value={data.text} + name='text' + value={annotation.text} onKeyDown={this.handleKeyDown} onChange={this.handleChange} /> @@ -112,12 +113,6 @@ class AnnotationForm extends Component { } } - -/* -- get the first sentence from the text -- display the form at that point -*/ - const mapStateToProps = state => ({ annotation: state.align.annotation, timeline: state.align.timeline, diff --git a/animism-align/frontend/views/align/containers/timeline.container.js b/animism-align/frontend/views/align/containers/timeline.container.js index 3795751..4167d2d 100644 --- a/animism-align/frontend/views/align/containers/timeline.container.js +++ b/animism-align/frontend/views/align/containers/timeline.container.js @@ -91,7 +91,7 @@ class Timeline extends Component { handleClick(e) { const play_ts = positionToTime(e.pageY, this.props.timeline) if (e.pageX > WAVEFORM_SIZE * 0.67) { - actions.align.showNewTimestampForm(play_ts, this.props.text) + actions.align.showNewAnnotationForm(play_ts, this.props.text) } else { actions.audio.seek(play_ts) } diff --git a/animism-align/frontend/views/timestamp/timestamp.reducer.js b/animism-align/frontend/views/annotation/annotation.reducer.js index e57110e..80be5b2 100644 --- a/animism-align/frontend/views/timestamp/timestamp.reducer.js +++ b/animism-align/frontend/views/annotation/annotation.reducer.js @@ -3,15 +3,15 @@ import { session, getDefault, getDefaultInt } from '../../session' import { crudState, crudReducer } from '../../api/crud.reducer' -const initialState = crudState('timestamp', { +const initialState = crudState('annotation', { options: { sort: 'start_ts desc', } }) -const reducer = crudReducer('timestamp') +const reducer = crudReducer('annotation') -export default function timestampReducer(state = initialState, action) { +export default function annotationReducer(state = initialState, action) { // console.log(action.type, action) state = reducer(state, action) switch (action.type) { diff --git a/animism-align/frontend/views/timestamp/components/timestamp.form.js b/animism-align/frontend/views/timestamp/components/timestamp.form.js deleted file mode 100644 index d90b663..0000000 --- a/animism-align/frontend/views/timestamp/components/timestamp.form.js +++ /dev/null @@ -1,153 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' - -import { session } from '../../../session' - -import { TextInput, LabelDescription, TextArea, Checkbox, SubmitButton, Loader } from '../../../common' - -const newGraph = () => ({ - path: '', - title: '', - username: session('username'), - description: '', -}) - -export default class GraphForm extends Component { - state = { - title: "", - submitTitle: "", - data: { ...newGraph() }, - errorFields: new Set([]), - } - - componentDidMount() { - const { data, isNew } = this.props - const title = isNew ? 'new project' : 'editing ' + data.title - const submitTitle = isNew ? "Create Graph" : "Save Changes" - this.setState({ - title, - submitTitle, - errorFields: new Set([]), - data: { - ...newGraph(), - ...data - }, - }) - } - - handleChange(e) { - const { errorFields } = this.state - const { name, value } = e.target - if (errorFields.has(name)) { - errorFields.delete(name) - } - let sanitizedValue = value - if (name === 'path') { - sanitizedValue = sanitizedValue.toLowerCase().replace(/ /, '-').replace(/[!@#$%^&*()[\]{}]/, '-').replace(/-+/, '-') - } - this.setState({ - errorFields, - data: { - ...this.state.data, - [name]: sanitizedValue, - } - }) - } - - handleSelect(name, value) { - const { errorFields } = this.state - if (errorFields.has(name)) { - errorFields.delete(name) - } - this.setState({ - errorFields, - data: { - ...this.state.data, - [name]: value, - } - }) - } - - handleSubmit(e) { - e.preventDefault() - const { isNew, onSubmit } = this.props - const { data } = this.state - const requiredKeys = "title username path description".split(" ") - const validKeys = "title username path description".split(" ") - const validData = validKeys.reduce((a,b) => { a[b] = data[b]; return a }, {}) - const errorFields = requiredKeys.filter(key => !validData[key]) - if (errorFields.length) { - console.log('error', errorFields, validData) - this.setState({ errorFields: new Set(errorFields) }) - } else { - if (isNew) { - // side effect: set username if we're creating a new graph - session.set('username', data.username) - } else { - validData.id = data.id - } - console.log('submit', validData) - onSubmit(validData) - } - } - - render() { - const { isNew } = this.props - const { title, submitTitle, errorFields, data } = this.state - return ( - <div className='form'> - <h1>{title}</h1> - <form onSubmit={this.handleSubmit.bind(this)}> - <TextInput - title="Path" - name="path" - required - data={data} - error={errorFields.has('path')} - onChange={this.handleChange.bind(this)} - autoComplete="off" - /> - <LabelDescription> - {data.path - ? 'Project URLs will be: /' + data.path + '/example' - : 'Enter the base path for this project.'} - </LabelDescription> - <TextInput - title="Title" - name="title" - required - data={data} - error={errorFields.has('title')} - onChange={this.handleChange.bind(this)} - autoComplete="off" - /> - <TextInput - title="Author" - name="username" - required - data={data} - error={errorFields.has('username')} - onChange={this.handleChange.bind(this)} - autoComplete="off" - /> - <TextArea - title="Description" - name="description" - data={data} - onChange={this.handleChange.bind(this)} - /> - <SubmitButton - title={submitTitle} - onClick={this.handleSubmit.bind(this)} - /> - {!!errorFields.size && - <label> - <span></span> - <span>Please complete the required fields =)</span> - </label> - } - </form> - </div> - ) - } -} diff --git a/animism-align/frontend/views/timestamp/containers/timestamp.edit.js b/animism-align/frontend/views/timestamp/containers/timestamp.edit.js deleted file mode 100644 index ce1b404..0000000 --- a/animism-align/frontend/views/timestamp/containers/timestamp.edit.js +++ /dev/null @@ -1,53 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import { history } from '../../../store' -import actions from '../../../actions' - -import { Loader } from '../../../common' - -import GraphForm from '../components/graph.form' - -class GraphEdit extends Component { - componentDidMount() { - console.log(this.props.match.params.id) - actions.graph.show(this.props.match.params.id) - } - - handleSubmit(data) { - actions.graph.update(data) - .then(response => { - // response - console.log(response) - history.push('/' + data.path) - }) - } - - render() { - const { show } = this.props.graph - if (show.loading || !show.res) { - return ( - <div className='form'> - <Loader /> - </div> - ) - } - return ( - <GraphForm - data={show.res} - onSubmit={this.handleSubmit.bind(this)} - /> - ) - } -} - -const mapStateToProps = state => ({ - graph: state.graph, -}) - -const mapDispatchToProps = dispatch => ({ - // searchActions: bindActionCreators({ ...searchActions }, dispatch), -}) - -export default connect(mapStateToProps, mapDispatchToProps)(GraphEdit) diff --git a/animism-align/frontend/views/timestamp/containers/timestamp.index.js b/animism-align/frontend/views/timestamp/containers/timestamp.index.js deleted file mode 100644 index 6d0cdd5..0000000 --- a/animism-align/frontend/views/timestamp/containers/timestamp.index.js +++ /dev/null @@ -1,53 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' - -import { Loader } from '../../../common' -import actions from '../../../actions' -// import * as uploadActions from './upload.actions' - -class GraphIndex extends Component { - componentDidMount() { - actions.graph.index() - } - render() { - const { index } = this.props - // console.log(this.props) - if (!index.order) { - return ( - <div className='graphIndex'> - <Loader /> - </div> - ) - } - // console.log(state) - return ( - <div className='graphIndex'> - <div> - <b>welcome, swimmer</b> - <Link to='/index/new'>+ new project</Link> - </div> - {index.order.map(id => { - const graph = index.lookup[id] - return ( - <div key={id}> - <Link to={'/' + graph.path}>{graph.title}</Link> - <Link to={'/index/' + id + '/edit'}>{'edit project'}</Link> - </div> - ) - })} - </div> - ) - } -} - -const mapStateToProps = state => ({ - index: state.graph.index, -}) - -const mapDispatchToProps = dispatch => ({ - // uploadActions: bindActionCreators({ ...uploadActions }, dispatch), -}) - -export default connect(mapStateToProps, mapDispatchToProps)(GraphIndex) diff --git a/animism-align/frontend/views/timestamp/containers/timestamp.new.js b/animism-align/frontend/views/timestamp/containers/timestamp.new.js deleted file mode 100644 index be96bf5..0000000 --- a/animism-align/frontend/views/timestamp/containers/timestamp.new.js +++ /dev/null @@ -1,44 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import { history } from '../../../store' -import actions from '../../../actions' - -import GraphForm from '../components/graph.form' - -class GraphNew extends Component { - handleSubmit(data) { - console.log(data) - actions.graph.create(data) - .then(res => { - console.log(res) - if (res.res && res.res.id) { - history.push('/' + res.res.path) - } - }) - .catch(err => { - console.error('error') - }) - } - - render() { - return ( - <GraphForm - isNew - data={{}} - onSubmit={this.handleSubmit.bind(this)} - /> - ) - } -} - -const mapStateToProps = state => ({ - graph: state.graph, -}) - -const mapDispatchToProps = dispatch => ({ - // searchActions: bindActionCreators({ ...searchActions }, dispatch), -}) - -export default connect(mapStateToProps, mapDispatchToProps)(GraphNew) |
