diff options
Diffstat (limited to 'animism-align/frontend')
15 files changed, 123 insertions, 25 deletions
diff --git a/animism-align/frontend/app.js b/animism-align/frontend/app.js index 137d933..6239fa6 100644 --- a/animism-align/frontend/app.js +++ b/animism-align/frontend/app.js @@ -22,6 +22,7 @@ export default class App extends Component { actions.site.loadPeaks() actions.annotation.index() actions.paragraph.index() + actions.media.index() } render() { return ( diff --git a/animism-align/frontend/common/form.component.js b/animism-align/frontend/common/form.component.js index 2f9162e..9a94136 100644 --- a/animism-align/frontend/common/form.component.js +++ b/animism-align/frontend/common/form.component.js @@ -114,7 +114,7 @@ export class Select extends Component { <label> {title && <span>{title}</span>} <div className={(focused ? 'select focus' : 'select') + " " + (className || "")}> - <div>{(options.find(opt => opt.name === selected) || {label: defaultOption}).label}</div> + <div>{(options.find(opt => String(opt.name) === String(selected)) || {label: defaultOption}).label}</div> <select onFocus={() => this.setState({ focused: true })} onBlur={() => this.setState({ focused: false })} diff --git a/animism-align/frontend/util/index.js b/animism-align/frontend/util/index.js index a50bccb..afebe13 100644 --- a/animism-align/frontend/util/index.js +++ b/animism-align/frontend/util/index.js @@ -313,6 +313,12 @@ export const orderByFn = (s='name asc') => { case 'priority': mapFn = a => [parseInt(a.priority) || parseInt(a.id) || 1000, a] sortFn = numericSort[direction] + case 'title': + mapFn = a => [a.title || "", a] + sortFn = stringSort[direction] + case 'author': + mapFn = a => [a.author || "", a] + sortFn = stringSort[direction] case 'name': default: mapFn = a => [a.name || "", a] diff --git a/animism-align/frontend/views/align/align.css b/animism-align/frontend/views/align/align.css index ec39cd8..38a2bc7 100644 --- a/animism-align/frontend/views/align/align.css +++ b/animism-align/frontend/views/align/align.css @@ -130,9 +130,16 @@ canvas { display: flex; align-items: center; } +.annotationForm .buttons { + margin-bottom: 0.5rem; +} .annotationForm .ts { color: #fff; } +.annotationForm .select.media_id { + width: 100%; + margin-right: 0; +} /* Annotation index */ diff --git a/animism-align/frontend/views/align/align.util.js b/animism-align/frontend/views/align/align.util.js index 91af64c..c99ff3b 100644 --- a/animism-align/frontend/views/align/align.util.js +++ b/animism-align/frontend/views/align/align.util.js @@ -58,3 +58,7 @@ export const cutFirstSentence = text => { actions.site.updateText(updatedText) return croppedText } + +export const thumbnailURL = data => { + if (data.type === 'video') return data.settings.video.thumbnail_url +} 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 226bb45..0a71233 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.form.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.form.js @@ -12,7 +12,7 @@ import { timeToPosition } from '../../align.util' import { Select } from '../../../../common' const ANNOTATION_TYPES = [ - 'sentence', 'header', 'paragraph_end' + 'sentence', 'header', 'paragraph_end', 'video', ].map(name => ({ name, label: name })) class AnnotationForm extends Component { @@ -20,6 +20,7 @@ class AnnotationForm extends Component { super(props) this.handleChange = this.handleChange.bind(this) this.handleSelect = this.handleSelect.bind(this) + this.handleSettingsSelect = this.handleSettingsSelect.bind(this) this.handleKeyDown = this.handleKeyDown.bind(this) this.handleSubmit = this.handleSubmit.bind(this) this.textareaRef = React.createRef() @@ -66,6 +67,10 @@ class AnnotationForm extends Component { handleSelect(name, value) { actions.align.updateAnnotationForm(name, value) } + handleSettingsSelect(name, value) { + if (name.indexOf('_id') !== -1) value = parseInt(value) || 0 + actions.align.updateAnnotationSettings(name, value) + } handleSubmit() { const { annotation } = this.props if (annotation.type === 'paragraph_end') { @@ -96,21 +101,28 @@ class AnnotationForm extends Component { top: timeToPosition(annotation.start_ts, timeline), }} > + {this.renderButtons()} {annotation.type === 'sentence' && this.renderTextarea()} {annotation.type === 'header' && this.renderTextarea()} - <div className='row buttons'> - <div> - <Select - name='type' - selected={annotation.type} - options={ANNOTATION_TYPES} - defaultOption='text' - onChange={this.handleSelect} - /> - <div className='ts'>{timestamp(annotation.start_ts, 1, true)}</div> - </div> - <button onClick={this.handleSubmit}>Save</button> + {annotation.type === 'video' && this.renderVideo()} + </div> + ) + } + renderButtons() { + const { annotation } = this.props + return ( + <div className='row buttons'> + <div> + <Select + name='type' + selected={annotation.type} + options={ANNOTATION_TYPES} + defaultOption='text' + onChange={this.handleSelect} + /> + <div className='ts'>{timestamp(annotation.start_ts, 1, true)}</div> </div> + <button onClick={this.handleSubmit}>Save</button> </div> ) } @@ -128,11 +140,36 @@ class AnnotationForm extends Component { </div> ) } + renderVideo() { + const { annotation, media } = this.props + if (!media.lookup) return <div /> + const { lookup, order } = media + const video_list_items = order.filter(id => lookup[id].type === 'video').map(id => { + const video = lookup[id] + return { + name: video.id, + label: video.author + ' - ' + video.title + } + }) + return ( + <div> + <Select + name='media_id' + className="media_id" + selected={annotation.settings.media_id} + options={video_list_items} + defaultOption='Choose a video' + onChange={this.handleSettingsSelect} + /> + </div> + ) + } } const mapStateToProps = state => ({ annotation: state.align.annotation, timeline: state.align.timeline, + media: state.media.index, }) const mapDispatchToProps = dispatch => ({ diff --git a/animism-align/frontend/views/align/components/annotations/annotation.index.js b/animism-align/frontend/views/align/components/annotations/annotation.index.js index eccd8b7..3a78dd4 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.index.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.index.js @@ -75,7 +75,7 @@ class AnnotationIndex extends Component { actions.align.showEditAnnotationForm(annotation) } render() { - const { timeline, annotationInForm } = this.props + const { timeline, media, annotationInForm } = this.props const { start_ts, zoom, selected_annotation_id } = timeline const { items } = this.state const className = (zoom < 2) @@ -98,6 +98,7 @@ class AnnotationIndex extends Component { y={y} selected={annotation.id === selected_annotation_id} annotation={annotation} + media={media} onClick={this.handleClick} onDoubleClick={this.handleDoubleClick} /> @@ -112,6 +113,7 @@ const mapStateToProps = state => ({ timeline: state.align.timeline, annotationInForm: state.align.annotation, index: state.annotation.index, + media: state.media.index, }) const mapDispatchToProps = dispatch => ({ diff --git a/animism-align/frontend/views/align/components/annotations/annotation.types.js b/animism-align/frontend/views/align/components/annotations/annotation.types.js index 06be4a8..b927a12 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.types.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.types.js @@ -4,7 +4,7 @@ import actions from '../../../../actions' import { ZOOM_STEPS } from '../../constants' import { clamp } from '../../../../util' -import { positionToTime, timeToPosition } from '../../align.util' +import { positionToTime, timeToPosition, thumbnailURL } from '../../align.util' export const AnnotationSentence = ({ y, annotation, selected, onClick, onDoubleClick }) => { const { start_ts, text, paragraph_id } = annotation @@ -55,8 +55,47 @@ export const AnnotationParagraphEnd = ({ y, annotation, selected, onClick, onDou ) } +export const AnnotationVideo = ({ y, annotation, media, selected, onClick, onDoubleClick }) => { + const { start_ts, text } = annotation + const className = selected ? 'annotation media video selected' : 'annotation media video' + if (!(annotation.settings.media_id in media)) { + return ( + <div + className={className} + style={{ top: y }} + onClick={e => onClick(e, annotation)} + onDoubleClick={e => onDoubleClick(e, annotation)} + >MEDIA NOT FOUND</div> + ) + } + const data = media[annotation.settings.media_id] + return ( + <div + className={className} + style={{ top: y }} + onClick={e => onClick(e, annotation)} + onDoubleClick={e => onDoubleClick(e, annotation)} + > + <div className='img'> + <Link to={"/media/" + data.id + "/edit/"}> + <img src={thumbnailURL(data)} alt={data.title} /> + </Link> + </div> + <div className='meta center'> + <div> + <i>{data.title}</i><br /> + {data.author}<br /> + {data.date} + </div> + </div> + </div> + ) +} + + export const AnnotationElementLookup = { sentence: React.memo(AnnotationSentence), header: React.memo(AnnotationHeader), paragraph_end: React.memo(AnnotationParagraphEnd), + video: React.memo(AnnotationVideo), } diff --git a/animism-align/frontend/views/audio/audio.actions.js b/animism-align/frontend/views/audio/audio.actions.js index 4b1bcdc..64c8215 100644 --- a/animism-align/frontend/views/audio/audio.actions.js +++ b/animism-align/frontend/views/audio/audio.actions.js @@ -4,7 +4,7 @@ import actions from '../../actions' import { session } from '../../session' const audioPlayer = document.createElement('audio') -audioPlayer.src = '/static/data_store/peaks/animismA200620.mp3' +audioPlayer.src = '/static/data_store/peaks/animismA080720.mp3' audioPlayer.addEventListener('loadedmetadata', () => { console.log('audio duration:', audioPlayer.duration) dispatch({ type: types.align.set_display_setting, key: 'duration', value: audioPlayer.duration }) diff --git a/animism-align/frontend/views/media/components/media.formImage.js b/animism-align/frontend/views/media/components/media.formImage.js index c757d03..d86a6d8 100644 --- a/animism-align/frontend/views/media/components/media.formImage.js +++ b/animism-align/frontend/views/media/components/media.formImage.js @@ -6,7 +6,7 @@ import { capitalize } from '../../../util' import { TextInput, LabelDescription, FileInputField, Select, TextArea, Checkbox, SubmitButton, Loader } from '../../../common' -import { ImageSelection } from './media.formImageSelection' +import ImageSelection from './media.formImageSelection' export default class MediaImageForm extends Component { state = { diff --git a/animism-align/frontend/views/media/components/media.formImageSelection.js b/animism-align/frontend/views/media/components/media.formImageSelection.js index 142525b..5572793 100644 --- a/animism-align/frontend/views/media/components/media.formImageSelection.js +++ b/animism-align/frontend/views/media/components/media.formImageSelection.js @@ -21,7 +21,7 @@ const defaultState = { } } -class ImageSelection extends Component { +export default class ImageSelection extends Component { state = { ...defaultState } diff --git a/animism-align/frontend/views/media/components/media.indexOptions.js b/animism-align/frontend/views/media/components/media.indexOptions.js index 774bf22..5dbc415 100644 --- a/animism-align/frontend/views/media/components/media.indexOptions.js +++ b/animism-align/frontend/views/media/components/media.indexOptions.js @@ -20,6 +20,10 @@ const sortOptions = [ { name: 'id-desc', label: 'Oldest first' }, { name: 'username-asc', label: 'Username (A-Z)' }, { name: 'username-desc', label: 'Username (Z-A)' }, + { name: 'author-asc', label: 'Author (A-Z)' }, + { name: 'author-desc', label: 'Author (Z-A)' }, + { name: 'title-asc', label: 'Title (A-Z)' }, + { name: 'title-desc', label: 'Title (Z-A)' }, // { name: '-asc', label: '' }, // { name: '-desc', label: '' }, // { name: '-asc', label: '' }, diff --git a/animism-align/frontend/views/media/containers/media.index.js b/animism-align/frontend/views/media/containers/media.index.js index 7797fd7..19ef4c5 100644 --- a/animism-align/frontend/views/media/containers/media.index.js +++ b/animism-align/frontend/views/media/containers/media.index.js @@ -7,6 +7,8 @@ import { formatDateTime } from '../../../util' import { MenuButton, SmallMenuButton, Loader } from '../../../common' import actions from '../../../actions' +import { thumbnailURL } from '../../align/align.util' + import MediaIndexOptions from '../components/media.indexOptions' import MediaMenu from '../components/media.menu' @@ -14,7 +16,7 @@ import MediaMenu from '../components/media.menu' class MediaIndex extends Component { componentDidMount() { - this.fetch(false) + // this.fetch(false) } componentDidUpdate(prevProps) { @@ -79,9 +81,6 @@ class MediaIndex extends Component { } } -const thumbnailURL = data => { - if (data.type === 'video') return data.settings.video.thumbnail_url -} const MediaItem = ({ data }) => { // console.log(data) return ( diff --git a/animism-align/frontend/views/media/media.actions.js b/animism-align/frontend/views/media/media.actions.js index e33746e..1f1ab01 100644 --- a/animism-align/frontend/views/media/media.actions.js +++ b/animism-align/frontend/views/media/media.actions.js @@ -6,5 +6,4 @@ export const getVimeoMetadata = url => { .then(data => { return data }) - // const id = url.match(/\d+/i)[0]; } diff --git a/animism-align/frontend/views/media/media.reducer.js b/animism-align/frontend/views/media/media.reducer.js index bc885e8..6a04b9a 100644 --- a/animism-align/frontend/views/media/media.reducer.js +++ b/animism-align/frontend/views/media/media.reducer.js @@ -5,7 +5,7 @@ import { crudState, crudReducer } from '../../api/crud.reducer' const initialState = crudState('media', { options: { - sort: getDefault('media.sort', 'id-desc'), + sort: 'author-asc', thumbnailSize: getDefault('upload.thumbnailSize', 'small'), } }) |
