@ -1,11 +1,21 @@
import React from 'react' ;
import ImmutablePropTypes from 'react-immutable-proptypes' ;
import PropTypes from 'prop-types' ;
import ImmutablePureComponent from 'react-immutable-pure-component' ;
import { connect } from 'react-redux' ;
import ImageLoader from './image_loader' ;
import classNames from 'classnames' ;
import { changeUploadCompose } from '../../../actions/compose' ;
import { getPointerPosition } from '../../video' ;
import { FormattedMessage , defineMessages , injectIntl } from 'react-intl' ;
import IconButton from 'mastodon/components/icon_button' ;
import Button from 'mastodon/components/button' ;
import Video from 'mastodon/features/video' ;
const messages = defineMessages ( {
close : { id : 'lightbox.close' , defaultMessage : 'Close' } ,
apply : { id : 'upload_modal.apply' , defaultMessage : 'Apply' } ,
placeholder : { id : 'upload_modal.description_placeholder' , defaultMessage : 'A quick brown fox jumps over the lazy dog' } ,
} ) ;
const mapStateToProps = ( state , { id } ) => ( {
media : state . getIn ( [ 'compose' , 'media_attachments' ] ) . find ( item => item . get ( 'id' ) === id ) ,
@ -13,17 +23,20 @@ const mapStateToProps = (state, { id }) => ({
const mapDispatchToProps = ( dispatch , { id } ) => ( {
onSave : ( x, y ) => {
dispatch ( changeUploadCompose ( id , { focus: ` ${ x . toFixed ( 2 ) } , ${ y . toFixed ( 2 ) } ` } ) ) ;
onSave : ( description, x, y ) => {
dispatch ( changeUploadCompose ( id , { description, focus: ` ${ x . toFixed ( 2 ) } , ${ y . toFixed ( 2 ) } ` } ) ) ;
} ,
} ) ;
export default @ connect ( mapStateToProps , mapDispatchToProps )
@ injectIntl
class FocalPointModal extends ImmutablePureComponent {
static propTypes = {
media : ImmutablePropTypes . map . isRequired ,
onClose : PropTypes . func . isRequired ,
intl : PropTypes . object . isRequired ,
} ;
state = {
@ -32,6 +45,8 @@ class FocalPointModal extends ImmutablePureComponent {
focusX : 0 ,
focusY : 0 ,
dragging : false ,
description : '' ,
dirty : false ,
} ;
componentWillMount ( ) {
@ -66,7 +81,6 @@ class FocalPointModal extends ImmutablePureComponent {
document . removeEventListener ( 'mouseup' , this . handleMouseUp ) ;
this . setState ( { dragging : false } ) ;
this . props . onSave ( this . state . focusX , this . state . focusY ) ;
}
updatePosition = e => {
@ -74,46 +88,113 @@ class FocalPointModal extends ImmutablePureComponent {
const focusX = ( x - . 5 ) * 2 ;
const focusY = ( y - . 5 ) * - 2 ;
this . setState ( { x , y , focusX , focusY } ) ;
this . setState ( { x , y , focusX , focusY , dirty : true } ) ;
}
updatePositionFromMedia = media => {
const focusX = media . getIn ( [ 'meta' , 'focus' , 'x' ] ) ;
const focusY = media . getIn ( [ 'meta' , 'focus' , 'y' ] ) ;
const focusX = media . getIn ( [ 'meta' , 'focus' , 'x' ] ) ;
const focusY = media . getIn ( [ 'meta' , 'focus' , 'y' ] ) ;
const description = media . get ( 'description' ) || '' ;
if ( focusX && focusY ) {
const x = ( focusX / 2 ) + . 5 ;
const y = ( focusY / - 2 ) + . 5 ;
this . setState ( { x , y , focusX , focusY } ) ;
this . setState ( {
x ,
y ,
focusX ,
focusY ,
description ,
dirty : false ,
} ) ;
} else {
this . setState ( { x : 0.5 , y : 0.5 , focusX : 0 , focusY : 0 } ) ;
this . setState ( {
x : 0.5 ,
y : 0.5 ,
focusX : 0 ,
focusY : 0 ,
description ,
dirty : false ,
} ) ;
}
}
handleChange = e => {
this . setState ( { description : e . target . value , dirty : true } ) ;
}
handleSubmit = ( ) => {
this . props . onSave ( this . state . description , this . state . focusX , this . state . focusY ) ;
this . props . onClose ( ) ;
}
setRef = c => {
this . node = c ;
}
render ( ) {
const { media } = this . props ;
const { x , y , dragging } = this . state ;
const { media , intl , onClose } = this . props ;
const { x , y , dragging , description , dirty } = this . state ;
const width = media . getIn ( [ 'meta' , 'original' , 'width' ] ) || null ;
const height = media . getIn ( [ 'meta' , 'original' , 'height' ] ) || null ;
const focals = [ 'image' , 'gifv' ] . includes ( media . get ( 'type' ) ) ;
const previewRatio = 16 / 9 ;
const previewWidth = 200 ;
const previewHeight = previewWidth / previewRatio ;
return (
< div className = 'modal-root__modal video-modal focal-point-modal' >
< div className = { classNames ( 'focal-point' , { dragging } ) } ref = { this . setRef } >
< ImageLoader
previewSrc = { media . get ( 'preview_url' ) }
src = { media . get ( 'url' ) }
width = { width }
height = { height }
/ >
< div className = 'focal-point__reticle' style = { { top : ` ${ y * 100 } % ` , left : ` ${ x * 100 } % ` } } / >
< div className = 'focal-point__overlay' onMouseDown = { this . handleMouseDown } / >
< div className = 'modal-root__modal report-modal' style = { { maxWidth : 960 } } >
< div className = 'report-modal__target' >
< IconButton className = 'media-modal__close' title = { intl . formatMessage ( messages . close ) } icon = 'times' onClick = { onClose } size = { 16 } / >
< FormattedMessage id = 'upload_modal.edit_media' defaultMessage = 'Edit media' / >
< / d i v >
< div className = 'report-modal__container' >
< div className = 'report-modal__comment' >
{ focals && < p > < FormattedMessage id = 'upload_modal.hint' defaultMessage = 'Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.' / > < / p > }
< label className = 'setting-text-label' htmlFor = 'upload-modal__description' > < FormattedMessage id = 'upload_form.description' defaultMessage = 'Describe for the visually impaired' / > < / l a b e l >
< textarea
id = 'upload-modal__description'
className = 'setting-text light'
value = { description }
onChange = { this . handleChange }
autoFocus
/ >
< Button disabled = { ! dirty } text = { intl . formatMessage ( messages . apply ) } onClick = { this . handleSubmit } / >
< / d i v >
< div className = 'report-modal__statuses' >
{ focals && (
< div className = { classNames ( 'focal-point' , { dragging } ) } ref = { this . setRef } >
{ media . get ( 'type' ) === 'image' && < img src = { media . get ( 'url' ) } width = { width } height = { height } alt = '' / > }
{ media . get ( 'type' ) === 'gifv' && < video src = { media . get ( 'url' ) } width = { width } height = { height } loop muted autoPlay / > }
< div className = 'focal-point__preview' >
< strong > < FormattedMessage id = 'upload_modal.preview_label' defaultMessage = 'Preview ({ratio})' values = { { ratio : '16:9' } } / > < / s t r o n g >
< div style = { { width : previewWidth , height : previewHeight , backgroundImage : ` url( ${ media . get ( 'preview_url' ) } ) ` , backgroundSize : 'cover' , backgroundPosition : ` ${ x * 100 } % ${ y * 100 } % ` } } / >
< / d i v >
< div className = 'focal-point__reticle' style = { { top : ` ${ y * 100 } % ` , left : ` ${ x * 100 } % ` } } / >
< div className = 'focal-point__overlay' onMouseDown = { this . handleMouseDown } / >
< / d i v >
) }
{ [ 'audio' , 'video' ] . includes ( media . get ( 'type' ) ) && (
< Video
preview = { media . get ( 'preview_url' ) }
blurhash = { media . get ( 'blurhash' ) }
src = { media . get ( 'url' ) }
detailed
editable
/ >
) }
< / d i v >
< / d i v >
< / d i v >
) ;