import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import classNames from 'classnames';
-import { changeUploadCompose, uploadThumbnail } from 'flavours/glitch/actions/compose';
+import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from 'flavours/glitch/actions/compose';
import { getPointerPosition } from 'flavours/glitch/features/video';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import IconButton from 'flavours/glitch/components/icon_button';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
apply: { id: 'upload_modal.apply', defaultMessage: 'Apply' },
+ applying: { id: 'upload_modal.applying', defaultMessage: 'Applying…' },
placeholder: { id: 'upload_modal.description_placeholder', defaultMessage: 'A quick brown fox jumps over the lazy dog' },
chooseImage: { id: 'upload_modal.choose_image', defaultMessage: 'Choose image' },
+ discardMessage: { id: 'confirmations.discard_edit_media.message', defaultMessage: 'You have unsaved changes to the media description or preview, discard them anyway?' },
+ discardConfirm: { id: 'confirmations.discard_edit_media.confirm', defaultMessage: 'Discard' },
});
const mapStateToProps = (state, { id }) => ({
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
account: state.getIn(['accounts', me]),
isUploadingThumbnail: state.getIn(['compose', 'isUploadingThumbnail']),
+ description: state.getIn(['compose', 'media_modal', 'description']),
+ focusX: state.getIn(['compose', 'media_modal', 'focusX']),
+ focusY: state.getIn(['compose', 'media_modal', 'focusY']),
+ dirty: state.getIn(['compose', 'media_modal', 'dirty']),
+ is_changing_upload: state.getIn(['compose', 'is_changing_upload']),
});
const mapDispatchToProps = (dispatch, { id }) => ({
dispatch(changeUploadCompose(id, { description, focus: `${x.toFixed(2)},${y.toFixed(2)}` }));
},
+ onChangeDescription: (description) => {
+ dispatch(onChangeMediaDescription(description));
+ },
+
+ onChangeFocus: (focusX, focusY) => {
+ dispatch(onChangeMediaFocus(focusX, focusY));
+ },
+
onSelectThumbnail: files => {
dispatch(uploadThumbnail(id, files[0]));
},
}
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
+export default @connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })
+@(component => injectIntl(component, { withRef: true }))
class FocalPointModal extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
isUploadingThumbnail: PropTypes.bool,
onSave: PropTypes.func.isRequired,
+ onChangeDescription: PropTypes.func.isRequired,
+ onChangeFocus: PropTypes.func.isRequired,
onSelectThumbnail: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
state = {
- x: 0,
- y: 0,
- focusX: 0,
- focusY: 0,
dragging: false,
- description: '',
dirty: false,
progress: 0,
loading: true,
ocrStatus: '',
};
- componentWillMount () {
- this.updatePositionFromMedia(this.props.media);
- }
-
- componentWillReceiveProps (nextProps) {
- if (this.props.media.get('id') !== nextProps.media.get('id')) {
- this.updatePositionFromMedia(nextProps.media);
- }
- }
-
componentWillUnmount () {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
const focusX = (x - .5) * 2;
const focusY = (y - .5) * -2;
- 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 description = media.get('description') || '';
-
- if (focusX && focusY) {
- const x = (focusX / 2) + .5;
- const y = (focusY / -2) + .5;
-
- this.setState({
- x,
- y,
- focusX,
- focusY,
- description,
- dirty: false,
- });
- } else {
- this.setState({
- x: 0.5,
- y: 0.5,
- focusX: 0,
- focusY: 0,
- description,
- dirty: false,
- });
- }
+ this.props.onChangeFocus(focusX, focusY);
}
handleChange = e => {
- this.setState({ description: e.target.value, dirty: true });
+ this.props.onChangeDescription(e.target.value);
}
handleKeyDown = (e) => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
e.stopPropagation();
- this.setState({ description: e.target.value, dirty: true });
+ this.props.onChangeDescription(e.target.value);
this.handleSubmit();
}
}
handleSubmit = () => {
- this.props.onSave(this.state.description, this.state.focusX, this.state.focusY);
- this.props.onClose();
+ this.props.onSave(this.props.description, this.props.focusX, this.props.focusY);
+ }
+
+ getCloseConfirmationMessage = () => {
+ const { intl, dirty } = this.props;
+
+ if (dirty) {
+ return {
+ message: intl.formatMessage(messages.discardMessage),
+ confirm: intl.formatMessage(messages.discardConfirm),
+ };
+ } else {
+ return null;
+ }
}
setRef = c => {
await worker.loadLanguage('eng');
await worker.initialize('eng');
const { data: { text } } = await worker.recognize(media_url);
- this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false });
+ this.setState({ detecting: false });
+ this.props.onChangeDescription(removeExtraLineBreaks(text));
await worker.terminate();
})().catch((e) => {
if (refreshCache) {
handleThumbnailChange = e => {
if (e.target.files.length > 0) {
- this.setState({ dirty: true });
this.props.onSelectThumbnail(e.target.files);
}
}
}
render () {
- const { media, intl, account, onClose, isUploadingThumbnail } = this.props;
- const { x, y, dragging, description, dirty, detecting, progress, ocrStatus } = this.state;
+ const { media, intl, account, onClose, isUploadingThumbnail, description, focusX, focusY, dirty, is_changing_upload } = this.props;
+ const { dragging, detecting, progress, ocrStatus } = this.state;
+ const x = (focusX / 2) + .5;
+ const y = (focusY / -2) + .5;
const width = media.getIn(['meta', 'original', 'width']) || null;
const height = media.getIn(['meta', 'original', 'height']) || null;
accept='image/png,image/jpeg'
onChange={this.handleThumbnailChange}
style={{ display: 'none' }}
- disabled={isUploadingThumbnail}
+ disabled={isUploadingThumbnail || is_changing_upload}
/>
</label>
value={detecting ? '…' : description}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
- disabled={detecting}
+ disabled={detecting || is_changing_upload}
autoFocus
/>
</div>
<div className='setting-text__toolbar'>
- <button disabled={detecting || media.get('type') !== 'image'} className='link-button' onClick={this.handleTextDetection}><FormattedMessage id='upload_modal.detect_text' defaultMessage='Detect text from picture' /></button>
+ <button disabled={detecting || media.get('type') !== 'image' || is_changing_upload} className='link-button' onClick={this.handleTextDetection}><FormattedMessage id='upload_modal.detect_text' defaultMessage='Detect text from picture' /></button>
<CharacterCounter max={1500} text={detecting ? '' : description} />
</div>
- <Button disabled={!dirty || detecting || isUploadingThumbnail || length(description) > 1500} text={intl.formatMessage(messages.apply)} onClick={this.handleSubmit} />
+ <Button disabled={!dirty || detecting || isUploadingThumbnail || length(description) > 1500 || is_changing_upload} text={intl.formatMessage(is_changing_upload ? messages.applying : messages.apply)} onClick={this.handleSubmit} />
</div>
<div className='focal-point-modal__content'>
COMPOSE_POLL_OPTION_CHANGE,
COMPOSE_POLL_OPTION_REMOVE,
COMPOSE_POLL_SETTINGS_CHANGE,
+ INIT_MEDIA_EDIT_MODAL,
+ COMPOSE_CHANGE_MEDIA_DESCRIPTION,
+ COMPOSE_CHANGE_MEDIA_FOCUS,
} from 'flavours/glitch/actions/compose';
import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines';
import { STORE_HYDRATE } from 'flavours/glitch/actions/store';
resetFileKey: Math.floor((Math.random() * 0x10000)),
idempotencyKey: null,
tagHistory: ImmutableList(),
+ media_modal: ImmutableMap({
+ id: null,
+ description: '',
+ focusX: 0,
+ focusY: 0,
+ dirty: false,
+ }),
doodle: ImmutableMap({
fg: 'rgb( 0, 0, 0)',
bg: 'rgb(255, 255, 255)',
return item;
}));
+ case INIT_MEDIA_EDIT_MODAL:
+ const media = state.get('media_attachments').find(item => item.get('id') === action.id);
+ return state.set('media_modal', ImmutableMap({
+ id: action.id,
+ description: media.get('description') || '',
+ focusX: media.getIn(['meta', 'focus', 'x'], 0),
+ focusY: media.getIn(['meta', 'focus', 'y'], 0),
+ dirty: false,
+ }));
+ case COMPOSE_CHANGE_MEDIA_DESCRIPTION:
+ return state.setIn(['media_modal', 'description'], action.description).setIn(['media_modal', 'dirty'], true);
+ case COMPOSE_CHANGE_MEDIA_FOCUS:
+ return state.setIn(['media_modal', 'focusX'], action.focusX).setIn(['media_modal', 'focusY'], action.focusY).setIn(['media_modal', 'dirty'], true);
case COMPOSE_MENTION:
return state.withMutations(map => {
map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' '));
case COMPOSE_UPLOAD_CHANGE_SUCCESS:
return state
.set('is_changing_upload', false)
+ .setIn(['media_modal', 'dirty'], false)
.update('media_attachments', list => list.map(item => {
if (item.get('id') === action.media.id) {
return fromJS(action.media);