diff options
Diffstat (limited to 'animism-align/frontend/views/media/components')
4 files changed, 300 insertions, 98 deletions
diff --git a/animism-align/frontend/views/media/components/media.form.js b/animism-align/frontend/views/media/components/media.form.js new file mode 100644 index 0000000..ed96e6e --- /dev/null +++ b/animism-align/frontend/views/media/components/media.form.js @@ -0,0 +1,190 @@ +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 newMedia = () => ({ + type: '', + tag: '', + url: '', + title: '', + author: '', + pre_title: '', + translated_title: '', + date: '', + source: '', + medium: '', + start_ts: 0, + settings: {}, +}) + +export default class MediaForm extends Component { + state = { + title: "", + submitTitle: "", + data: { ...newMedia() }, + errorFields: new Set([]), + } + + 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 + }, + }) + } + + 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".split(" ") + const validKeys = "title".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 + /* + type: '', + tag: '', + url: '', + */ + return ( + <div className='form'> + <h1>{title}</h1> + <form onSubmit={this.handleSubmit.bind(this)}> + <TextInput + title="Author" + name="author" + required + data={data} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <TextInput + title="Title" + name="title" + required + data={data} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <TextInput + title="Title Prefix" + name="pre_title" + required + data={data} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <TextInput + title="Translated Title" + name="translated_title" + required + data={data} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <TextInput + title="Date" + name="date" + required + data={data} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <TextInput + title="Medium" + name="medium" + required + data={data} + onChange={this.handleChange.bind(this)} + /> + <TextInput + title="Source" + name="source" + placeholder="Courtesy of / Copyright" + required + data={data} + 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/media/components/media.index.js b/animism-align/frontend/views/media/components/media.index.js deleted file mode 100644 index a4127f9..0000000 --- a/animism-align/frontend/views/media/components/media.index.js +++ /dev/null @@ -1,98 +0,0 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' - -import { formatDateTime } from '../../../util' -import { MenuButton, SmallMenuButton, Loader } from '../../../common' -import actions from '../../../actions' - -import MediaIndexOptions from './media.indexOptions' -import MediaMenu from './media.menu' - -// const { result, collectionLookup } = this.props - -export default class MediaIndex extends Component { - componentDidMount() { - this.fetch(false) - } - - componentDidUpdate(prevProps) { - if (this.props.media.options.sort !== prevProps.media.options.sort) { - this.fetch(false) - } - } - - fetch(load_more) { - const { options, index } = this.props.media - const { order: index_order } = index - const [ sort, order ] = options.sort.split('-') - actions.media.index({ - sort, order, limit: 5000, // offset: load_more ? index_order.length : 0, - }, load_more) - } - - render() { - const { mediaActions } = this.props - const { options } = this.props.media - const { loading, lookup, order } = this.props.media.index - if (loading) { - return ( - <section> - <MediaIndexOptions /> - <div className="row"> - {order && !!order.length && - <div className={'results ' + options.thumbnailSize}> - {order.map(id => <MediaItem key={id} data={lookup[id]} />)} - </div> - } - </div> - <Loader /> - </section> - ) - } - if (!lookup || !order.length) { - return ( - <section> - <MediaIndexOptions /> - <div className="row"> - <MediaMenu /> - <p className='gray'> - {"No media"} - </p> - </div> - </section> - ) - } - return ( - <section> - <MediaIndexOptions /> - <div className="row"> - <MediaMenu /> - <div className={'results ' + options.thumbnailSize}> - {order.map(id => <MediaItem key={id} data={lookup[id]} />)} - </div> - </div> - {order.length >= 50 && <button className='loadMore' onClick={() => this.fetch(true)}>Load More</button>} - </section> - ) - } -} - -const MediaItem = ({ data }) => { - // console.log(data) - return ( - <div className='cell'> - <div className='img'> - <Link to={"/media/" + data.id + "/show/"}> - <img src={data.thumb_url} alt={data.title} /> - </Link> - </div> - <div className='meta center'> - <div> - {data.title}<br /> - {data.author} - </div> - </div> - </div> - ) -} - diff --git a/animism-align/frontend/views/media/components/media.indexOptions.js b/animism-align/frontend/views/media/components/media.indexOptions.js new file mode 100644 index 0000000..774bf22 --- /dev/null +++ b/animism-align/frontend/views/media/components/media.indexOptions.js @@ -0,0 +1,61 @@ +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: '-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 new file mode 100644 index 0000000..3d7e86a --- /dev/null +++ b/animism-align/frontend/views/media/components/media.menu.js @@ -0,0 +1,49 @@ +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" />, + <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" />, +]) + +const MediaEditMenu = (props) => ([ + <MenuButton key='back' name="back" />, +]) |
