diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-07-22 14:05:15 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-07-22 14:05:15 +0200 |
| commit | ef78bc6a084f92b4794e987b5832240d85b6479e (patch) | |
| tree | b314b630800db6aa60f28ef0b115625e6ca176db /animism-align/frontend/views/media/components | |
| parent | 85d4cb9addf9ca887d3440b2786665d67d9917c4 (diff) | |
refactor app using babel module-resolver
Diffstat (limited to 'animism-align/frontend/views/media/components')
6 files changed, 0 insertions, 891 deletions
diff --git a/animism-align/frontend/views/media/components/media.form.js b/animism-align/frontend/views/media/components/media.form.js deleted file mode 100644 index 1463041..0000000 --- a/animism-align/frontend/views/media/components/media.form.js +++ /dev/null @@ -1,272 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' - -import { session } from '../../../session' -import { capitalize } from '../../../util' - -import { TextInput, LabelDescription, Select, TextArea, Checkbox, SubmitButton, Loader } from '../../../common' - -import MediaImageForm from './media.formImage' -import MediaVideoForm from './media.formVideo' - -const newMedia = () => ({ - type: 'image', - tag: 'media', - url: '', - title: '', - author: '', - pre_title: '', - post_title: '', - translated_title: '', - date: '', - source: '', - medium: '', - start_ts: 0, - settings: {}, -}) - -const MEDIA_TYPES = [ - 'image', 'video' -].map(name => ({ name, label: capitalize(name) })) - -export default class MediaForm extends Component { - state = { - title: "", - submitTitle: "", - data: { ...newMedia() }, - errorFields: new Set([]), - } - - constructor(props) { - super(props) - this.handleKeyDown = this.handleKeyDown.bind(this) - this.handleSelect = this.handleSelect.bind(this) - this.handleChange = this.handleChange.bind(this) - this.handleSettingsChange = this.handleSettingsChange.bind(this) - this.handleSettingsChangeEvent = this.handleSettingsChangeEvent.bind(this) - this.handleSubmit = this.handleSubmit.bind(this) - } - - componentDidMount() { - const { data, isNew } = this.props - const title = isNew ? 'New media' : 'Editing ' + data.title - const submitTitle = isNew ? "Add Media" : "Save Changes" - this.setState({ - title, - submitTitle, - errorFields: new Set([]), - data: { - ...newMedia(), - ...data - }, - }) - window.addEventListener('keydown', this.handleKeyDown) - } - - componentWillUnmount() { - window.removeEventListener('keydown', this.handleKeyDown) - } - - handleKeyDown(e) { - // console.log(e, e.keyCode) - if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) { - if (e) { - e.preventDefault() - } - this.handleSubmit() - } - } - - handleChange(e) { - const { name, value } = e.target - this.handleSelect(name, value) - } - - handleSelect(name, value) { - const { errorFields } = this.state - if (errorFields.has(name)) { - errorFields.delete(name) - } - this.setState({ - errorFields, - data: { - ...this.state.data, - [name]: value, - } - }) - } - - handleSettingsChangeEvent(e) { - const { name, value } = e.target - this.handleSettingsChange(name, value) - } - - handleSettingsChange(name, value) { - console.log(name, value) - if (name !== 'multiple') { - value = { [name]: value } - } - this.setState({ - data: { - ...this.state.data, - settings: { - ...this.state.data.settings, - ...value, - } - } - }) - } - - handleSubmit(e) { - if (e) { - e.preventDefault() - } - const { isNew, onSubmit } = this.props - const { data } = this.state - const requiredKeys = "author title date".split(" ") - const validKeys = "type tag url title author pre_title post_title translated_title date source medium start_ts settings".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 media - // 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 - // console.log(data) - return ( - <div className='form'> - <h1>{title}</h1> - <form onSubmit={this.handleSubmit}> - <Select - title='Media Type' - name='type' - selected={data.type} - options={MEDIA_TYPES} - onChange={this.handleSelect} - /> - - {data.type === 'image' && - <MediaImageForm - data={data} - onChange={this.handleSelect} - onSettingsChange={this.handleSettingsChange} - /> - } - - {data.type === 'video' && - <MediaVideoForm - data={data} - onChange={this.handleSelect} - onSettingsChange={this.handleSettingsChange} - /> - } - - <TextInput - title="Author" - name="author" - required - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - <TextInput - title="Title" - name="title" - required - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - <TextInput - title="Title Prefix" - name="pre_title" - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - <TextInput - title="Title Suffix" - name="post_title" - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - <TextInput - title="Translated Title" - name="translated_title" - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - <TextInput - title="Date" - name="date" - required - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - <TextInput - title="Medium" - name="medium" - required - data={data} - onChange={this.handleChange} - /> - <TextInput - title="Source" - name="source" - placeholder="Courtesy of / Copyright" - required - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - <TextArea - title="Citation" - name="bibliography" - placeholder="Use if special HTML formatting needed" - data={data.settings} - onChange={this.handleSettingsChangeEvent} - /> - <Checkbox - label="Hide in list of works" - name="hide_in_bibliography" - checked={data.settings.hide_in_bibliography} - onChange={this.handleSettingsChange} - autoComplete="off" - /> - <TextArea - title="Description" - name="description" - data={data} - onChange={this.handleChange} - /> - <SubmitButton - title={submitTitle} - onClick={this.handleSubmit} - /> - {!!errorFields.size && - <label> - <span></span> - <span>Please complete the required fields</span> - </label> - } - </form> - </div> - ) - } -} diff --git a/animism-align/frontend/views/media/components/media.formImage.js b/animism-align/frontend/views/media/components/media.formImage.js deleted file mode 100644 index 4a97112..0000000 --- a/animism-align/frontend/views/media/components/media.formImage.js +++ /dev/null @@ -1,172 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' - -import { session } from '../../../session' -import actions from '../../../actions' -import { capitalize, preloadImage, cropImage } from '../../../util' - -import { TextInput, LabelDescription, UploadImage, Select, TextArea, Checkbox, SubmitButton, Loader } from '../../../common' -import { renderThumbnail } from '../../../common/upload.helpers' - -import ImageSelection from './media.formImageSelection' - -const DISPLAY_SIZE = 1024 -const DISPLAY_QUALITY= 80 -const THUMBNAIL_SIZE = 320 -const THUMBNAIL_QUALITY = 80 - -export default class MediaImageForm extends Component { - state = { - img: null, - } - - constructor(props) { - super(props) - this.handleSelect = this.handleSelect.bind(this) - this.handleChange = this.handleChange.bind(this) - this.handleSettingsChange = this.handleSettingsChange.bind(this) - this.handleUpload = this.handleUpload.bind(this) - this.handleCrop = this.handleCrop.bind(this) - this.replaceTaggedSize = this.replaceTaggedSize.bind(this) - this.uploadTaggedSize = this.uploadTaggedSize.bind(this) - } - - componentDidMount() { - // this.setState({ }) - if (this.props.data.settings.fullsize) { - preloadImage(this.props.data.settings.fullsize.url) - .then(img => this.setState({ img })) - } - } - - handleChange(e) { - const { name, value } = e.target - this.handleSelect(name, value) - } - - handleSelect(name, value) { - this.props.onSelect(name, value) - } - - handleSettingsChange(name, value) { - this.props.onSettingsChange(name, value) - } - - handleUpload({ file, img, canvas, blob }) { - // sizes: fullsize, display, thumbnail - this.replaceTaggedSize(file, 'fullsize') - .then(data => { - this.setState({ img }) - this.props.onSettingsChange('multiple', { - fullsize: data, - crop: {}, - }) - return this.replaceTaggedSize(blob, 'display', file.name) - }).then(data => { - this.props.onSettingsChange('multiple', { - display: data, - }) - this.uploadThumbnail(img) - }) - } - - uploadThumbnail(img) { - const { fn } = this.props.data.settings.fullsize - const thumbnailCanvas = renderThumbnail(img, { maxSide: THUMBNAIL_SIZE }) - thumbnailCanvas.toBlob(thumbnail => { - this.replaceTaggedSize(thumbnail, 'thumbnail', fn).then(data => { - this.props.onSettingsChange('multiple', { - thumbnail: data, - }) - }) - }, 'image/jpeg', THUMBNAIL_QUALITY) - } - - replaceTaggedSize(image, tag, fn) { - // when we upload an image, if the image already exists in this "position" - // on the record, we should also delete it - if (this.props.data.settings[tag] && this.props.data.settings[tag].id) { - return new Promise((resolve, reject) => { - actions.upload.destroy(this.props.data.settings[tag]) - .then(() => { - console.log('destroy successful') - this.uploadTaggedSize(image, tag, fn).then(data => resolve(data)) - }) - .catch(() => { - console.log('error deleting the image') - this.uploadTaggedSize(image, tag, fn).then(data => resolve(data)) - }) - }) - } - return this.uploadTaggedSize(image, tag, fn) - } - - uploadTaggedSize(image, tag, fn) { - console.log('uploading size', tag) - const uploadData = { - image, - tag, - username: 'animism', - } - if (fn) { - uploadData['__image_filename'] = fn - } - // console.log(uploadData) - return actions.upload.upload(uploadData).then(data => { - // console.log(data) - return data.res - }) - } - - handleCrop(crop) { - // when cropping an image, re-upload the display image and thumbnail - // console.log(crop) - cropImage(this.state.img, crop, DISPLAY_SIZE) - .then(canvas => { - canvas.toBlob(blob => { - // console.log(canvas, canvas.width, canvas.height, blob) - this.replaceTaggedSize(blob, 'display', this.props.data.settings.fullsize.fn) - .then(data => { - this.props.onSettingsChange('multiple', { - crop, - display: data, - }) - this.uploadThumbnail(canvas) - }) - }, 'image/jpeg', DISPLAY_QUALITY) - }) - } - - render() { - const { data } = this.props - // console.log(data) - return ( - <div className='imageForm'> - {!data.url && - <label className={'text fileInput'}> - <span>{"Upload image"}</span> - <div className="row"> - <button> - {"Choose image"} - </button> - <UploadImage - onUpload={this.handleUpload} - maxSide={DISPLAY_SIZE} - quality={DISPLAY_QUALITY} - /> - </div> - </label> - } - {data.settings.fullsize && - <div> - <ImageSelection - url={data.settings.fullsize.url} - crop={data.settings.crop} - onCrop={this.handleCrop} - /> - </div> - } - </div> - ) - } -} diff --git a/animism-align/frontend/views/media/components/media.formImageSelection.js b/animism-align/frontend/views/media/components/media.formImageSelection.js deleted file mode 100644 index 5572793..0000000 --- a/animism-align/frontend/views/media/components/media.formImageSelection.js +++ /dev/null @@ -1,213 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' -import toBlob from 'data-uri-to-blob' - -import { clamp } from '../../../util' -import { Loader } from '../../../common' - -const defaultState = { - dragging: false, - draggingBox: false, - bounds: null, - mouseX: 0, - mouseY: 0, - box: { - x: 0, - y: 0, - w: 0, - h: 0, - } -} - -export default class ImageSelection extends Component { - state = { - ...defaultState - } - - constructor() { - super() - // bind these events in the constructor, so we can remove event listeners later - this.handleMouseDown = this.handleMouseDown.bind(this) - this.handleMouseDownOnBox = this.handleMouseDownOnBox.bind(this) - this.handleMouseMove = this.handleMouseMove.bind(this) - this.handleMouseUp = this.handleMouseUp.bind(this) - this.handleWindowResize = this.handleWindowResize.bind(this) - } - - componentDidMount() { - document.body.addEventListener('mousemove', this.handleMouseMove) - document.body.addEventListener('mouseup', this.handleMouseUp) - window.addEventListener('resize', this.handleWindowResize) - } - - componentDidUpdate(prevProps) { - if (this.state.bounds && this.props.url !== prevProps.url) { - this.setState({ - ...defaultState, - bounds: this.getBoundingClientRect(), - box: this.props.crop || defaultState.box, - }) - } - } - - componentWillUnmount() { - document.body.removeEventListener('mousemove', this.handleMouseMove) - document.body.removeEventListener('mouseup', this.handleMouseUp) - window.removeEventListener('resize', this.handleWindowResize) - } - - getBoundingClientRect() { - if (!this.imgRef) return null - const rect = this.imgRef.getBoundingClientRect() - const scrollTop = document.body.scrollTop || document.body.parentNode.scrollTop - const scrollLeft = document.body.scrollLeft || document.body.parentNode.scrollLeft - const bounds = { - top: rect.top + scrollTop, - left: rect.left + scrollLeft, - width: rect.width, - height: rect.height, - } - return bounds - } - - handleLoad() { - const bounds = this.getBoundingClientRect() - const box = this.props.crop || defaultState.box - this.setState({ bounds, box }) - } - - handleWindowResize() { - if (!this.imgRef) return - const bounds = this.getBoundingClientRect() - this.setState({ bounds }) - } - - handleMouseDown(e) { - e.preventDefault() - const bounds = this.getBoundingClientRect() - const mouseX = e.pageX - const mouseY = e.pageY - const x = (mouseX - bounds.left) / bounds.width - const y = (mouseY - bounds.top) / bounds.height - const w = 1 / bounds.width - const h = 1 / bounds.height - this.setState({ - dragging: true, - bounds, - mouseX, - mouseY, - box: { - x, y, w, h, - } - }) - } - - handleMouseDownOnBox(e) { - const bounds = this.getBoundingClientRect() - const mouseX = e.pageX - const mouseY = e.pageY - this.setState({ - draggingBox: true, - bounds, - mouseX, - mouseY, - initialBox: { - ...this.state.box - }, - box: { - ...this.state.box - } - }) - } - - handleMouseMove(e) { - const { - dragging, draggingBox, - bounds, mouseX, mouseY, initialBox, box - } = this.state - if (dragging) { - e.preventDefault() - let { x, y } = box - let dx = (e.pageX - mouseX) / bounds.width - let dy = (e.pageY - mouseY) / bounds.height - let w = clamp(dx, 0.0, 1.0 - x) - let h = clamp(dy, 0.0, 1.0 - y) - this.setState({ - box: { - x, y, w, h, - } - }) - } else if (draggingBox) { - e.preventDefault() - let { x, y, w, h } = initialBox - let dx = (e.pageX - mouseX) / bounds.width - let dy = (e.pageY - mouseY) / bounds.height - this.setState({ - box: { - x: clamp(x + dx, 0, 1.0 - w), - y: clamp(y + dy, 0, 1.0 - h), - w, - h, - } - }) - } - } - - handleMouseUp(e) { - const { onCrop } = this.props - const { dragging, draggingBox, bounds, box } = this.state - if (!dragging && !draggingBox) return - e.preventDefault() - const { x, y, w, h } = box - let url = window.location.pathname - this.setState({ - dragging: false, - draggingBox: false, - }) - if (w < 10 / bounds.width || h < 10 / bounds.height) { - this.setState({ box: { ...defaultState.box }}) - onCrop({}) - } else { - // pass the box dimensions up - do the search again - onCrop(box) - } - } - - render() { - const { url } = this.props - const { bounds, box } = this.state - const { x, y, w, h } = box - return ( - <div className="imageSelection"> - <img - src={url} - ref={ref => this.imgRef = ref} - onMouseDown={this.handleMouseDown} - onLoad={this.handleLoad.bind(this)} - crossOrigin='anonymous' - /> - {!!w && - <div - className="box" - style={{ - left: x * bounds.width, - top: y * bounds.height, - width: w * bounds.width, - height: h * bounds.height, - }} - onMouseDown={this.handleMouseDownOnBox} - /> - } - </div> - ) - } -} - -const boxToFixed = ({ x, y, w, h }) => ({ - x: x.toFixed(3), - y: y.toFixed(3), - w: w.toFixed(3), - h: h.toFixed(3), -}) diff --git a/animism-align/frontend/views/media/components/media.formVideo.js b/animism-align/frontend/views/media/components/media.formVideo.js deleted file mode 100644 index 89954b9..0000000 --- a/animism-align/frontend/views/media/components/media.formVideo.js +++ /dev/null @@ -1,111 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' -import VimeoPlayer from '@u-wave/react-vimeo' - -import { capitalize } from '../../../util' -import { TextInput, LabelDescription, Select, TextArea, Checkbox, SubmitButton, Loader } from '../../../common' - -import { getVimeoMetadata } from '../media.actions' - -export default class MediaVideoForm extends Component { - state = { - } - - constructor(props) { - super(props) - this.handleSelect = this.handleSelect.bind(this) - this.handleChange = this.handleChange.bind(this) - this.handleSettingsChange = this.handleSettingsChange.bind(this) - } - - handleChange(e) { - let { name, value } = e.target - return this.handleSelect(name, value) - } - - handleSelect(name, value) { - value = value.trim() - if (name === 'url') { - getVimeoMetadata(value) - .then(data => { - console.log('video metadata', data) - this.props.onChange(name, value) - setTimeout(() => { - this.props.onSettingsChange('video', { - thumbnail_url: data.thumbnail_url, - duration: data.duration, - video_id: data.video_id, - }) - }, 20) - }) - } else { - this.props.onChange(name, value) - } - } - - handleSettingsChange(e) { - let { name, value } = e.target - this.props.onSettingsChange(name, value) - } - - handleSettingsSelect(name, value) { - this.props.onSettingsChange(name, value) - } - - render() { - const { data } = this.props - return ( - <div className='videoForm'> - <TextInput - title="Video URL" - name="url" - required - data={data} - onChange={this.handleChange} - autoComplete="off" - /> - - {data.url && - <div> - <LabelDescription className='video'> - <VimeoPlayer video={data.url} /> - </LabelDescription> - - {data.settings.video && data.settings.video.thumbnail && - <LabelDescription className='thumbnail'> - <img src={data.settings.video.thumbnail} /> - </LabelDescription> - } - - <TextInput - title="Start time" - name="video_start_time" - data={data.settings} - placeholder="0:00" - onChange={this.handleSettingsChange} - autoComplete="off" - /> - - <TextInput - title="End time" - name="video_end_time" - data={data.settings} - placeholder="0:00" - onChange={this.handleSettingsChange} - autoComplete="off" - /> - - <TextInput - title="Original duration" - name="original_duration" - data={data.settings} - placeholder="0:00" - onChange={this.handleSettingsChange} - autoComplete="off" - /> - </div> - } - </div> - ) - } -} diff --git a/animism-align/frontend/views/media/components/media.indexOptions.js b/animism-align/frontend/views/media/components/media.indexOptions.js deleted file mode 100644 index 5dbc415..0000000 --- a/animism-align/frontend/views/media/components/media.indexOptions.js +++ /dev/null @@ -1,65 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' - -import actions from '../../../actions' - -import { Select, Checkbox } from '../../../common' - -const thumbnailOptions = [ - { name: 'th', label: 'Thumbnails', }, - { name: 'sm', label: 'Small', }, - { name: 'md', label: 'Medium', }, - { name: 'lg', label: 'Large', }, - { name: 'orig', label: 'Original', }, -] - -const sortOptions = [ - { name: 'id-asc', label: 'Most recent' }, - { 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: '' }, - // { name: '-desc', label: '' }, - // { name: '-asc', label: '' }, - // { name: '-desc', label: '' }, -] - -class IndexOptions extends Component { - render() { - const { options } = this.props - return ( - <div className='row menubar'> - <div /> - <Select - name={'sort'} - options={sortOptions} - selected={options.sort} - onChange={actions.upload.updateOption} - /> - <Select - name={'thumbnailSize'} - options={thumbnailOptions} - selected={options.thumbnailSize} - onChange={actions.upload.updateOption} - /> - </div> - ) - } -} - -const mapStateToProps = state => ({ - options: state.upload.options, -}) - -const mapDispatchToProps = dispatch => ({ -}) - -export default connect(mapStateToProps, mapDispatchToProps)(IndexOptions) diff --git a/animism-align/frontend/views/media/components/media.menu.js b/animism-align/frontend/views/media/components/media.menu.js deleted file mode 100644 index 153a5c1..0000000 --- a/animism-align/frontend/views/media/components/media.menu.js +++ /dev/null @@ -1,58 +0,0 @@ -import React, { Component } from 'react' -import { Route, Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import { history } from '../../../store' -import actions from '../../../actions' -import { MenuButton, FileInput } from '../../../common' - -const mapStateToProps = state => ({ - media: state.media, -}) - -export default class MediaMenu extends Component { - render() { - return ( - <div className='menuButtons'> - <Route exact path='/media/:id/show/' component={MediaShowMenu} /> - <Route exact path='/media/:id/edit/' component={MediaEditMenu} /> - <Route exact path='/media/new/' component={MediaNewMenu} /> - <Route exact path='/media/' component={MediaIndexMenu} /> - </div> - ) - } -} - -const MediaIndexMenu = () => ([ - <MenuButton key='new' name="new" href="/media/new/" />, -]) - -const MediaShowMenu = connect(mapStateToProps)((props) => ([ - <MenuButton key='back' name="back" href="/media/" />, - <MenuButton key='edit' name="edit" href={"/media/" + props.match.params.id + "/edit/"} />, - <MenuButton key='delete' name="delete" onClick={() => { - const { res: media } = props.media.show - if (confirm("Really delete this media?")) { - actions.media.destroy(media).then(() => { - history.push('/media/') - }) - } - }} />, -])) - -const MediaNewMenu = (props) => ([ - <MenuButton key='back' name="back" href="/media/" />, -]) - -const MediaEditMenu = connect(mapStateToProps)((props) => ([ - <MenuButton key='back' name="back" href="/media/" />, - <MenuButton key='copy' name="copy" href={"/media/" + props.match.params.id + '/copy/'} label="Make a copy" />, - <MenuButton key='delete' name="delete" onClick={() => { - const { res: media } = props.media.show - if (confirm("Really delete this media?")) { - actions.media.destroy(media).then(() => { - history.push('/media/') - }) - } - }} />, -])) |
