-
+
- {media.get('type') === 'image' && }
-
-
-
-
+
)}
diff --git a/app/javascript/mastodon/features/compose/containers/upload_container.js b/app/javascript/mastodon/features/compose/containers/upload_container.js
index b6d81f03ac..342b0c2a9c 100644
--- a/app/javascript/mastodon/features/compose/containers/upload_container.js
+++ b/app/javascript/mastodon/features/compose/containers/upload_container.js
@@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import Upload from '../components/upload';
-import { undoUploadCompose, changeUploadCompose } from '../../../actions/compose';
+import { undoUploadCompose } from '../../../actions/compose';
import { openModal } from '../../../actions/modal';
import { submitCompose } from '../../../actions/compose';
@@ -14,10 +14,6 @@ const mapDispatchToProps = dispatch => ({
dispatch(undoUploadCompose(id));
},
- onDescriptionChange: (id, description) => {
- dispatch(changeUploadCompose(id, { description }));
- },
-
onOpenFocalPoint: id => {
dispatch(openModal('FOCAL_POINT', { id }));
},
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
index 7488a3598b..e15bf69d60 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
@@ -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 (
-
-
-
+
+
+
+
+
-
-
+
+
+ {focals &&
}
+
+
+
+
+
+
+
+
+
+ {focals && (
+
+ {media.get('type') === 'image' &&
}
+ {media.get('type') === 'gifv' &&
}
+
+
+
+
+
+
+ )}
+
+ {['audio', 'video'].includes(media.get('type')) && (
+
+ )}
+
);
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 0acdd198d5..da48c165e0 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -101,6 +101,7 @@ class Video extends React.PureComponent {
onCloseVideo: PropTypes.func,
detailed: PropTypes.bool,
inline: PropTypes.bool,
+ editable: PropTypes.bool,
cacheWidth: PropTypes.func,
visible: PropTypes.bool,
onToggleVisibility: PropTypes.func,
@@ -375,7 +376,7 @@ class Video extends React.PureComponent {
}
render () {
- const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link } = this.props;
+ const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link, editable } = this.props;
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
const progress = (currentTime / duration) * 100;
@@ -413,7 +414,7 @@ class Video extends React.PureComponent {
return (
- {revealed &&
}
-
+
@@ -489,7 +490,7 @@ class Video extends React.PureComponent {
- {!onCloseVideo && }
+ {(!onCloseVideo && !editable) && }
{(!fullscreen && onOpenVideo) && }
{onCloseVideo && }
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 8de72d72ee..f2967a3981 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -4567,6 +4567,14 @@ a.status-card.compact:hover {
}
}
+ .setting-text-label {
+ display: block;
+ color: $inverted-text-color;
+ font-size: 14px;
+ font-weight: 500;
+ margin-bottom: 10px;
+ }
+
.setting-toggle {
margin-top: 20px;
margin-bottom: 24px;
@@ -4960,6 +4968,10 @@ a.status-card.compact:hover {
max-width: 100%;
border-radius: 4px;
+ &.editable {
+ border-radius: 0;
+ }
+
&:focus {
outline: 0;
}
@@ -5688,27 +5700,20 @@ noscript {
}
}
-.focal-point-modal {
- max-width: 80vw;
- max-height: 80vh;
- position: relative;
-}
-
.focal-point {
position: relative;
- cursor: pointer;
+ cursor: move;
overflow: hidden;
- &.dragging {
- cursor: move;
- }
-
- img {
- max-width: 80vw;
+ img,
+ video {
+ display: block;
max-height: 80vh;
- width: auto;
+ width: 100%;
height: auto;
- margin: auto;
+ margin: 0;
+ object-fit: contain;
+ background: $base-shadow-color;
}
&__reticle {
@@ -5728,6 +5733,27 @@ noscript {
top: 0;
left: 0;
}
+
+ &__preview {
+ position: absolute;
+ bottom: 10px;
+ right: 10px;
+ z-index: 2;
+ cursor: default;
+
+ strong {
+ color: $primary-text-color;
+ font-size: 14px;
+ font-weight: 500;
+ display: block;
+ margin-bottom: 5px;
+ }
+
+ div {
+ border-radius: 4px;
+ box-shadow: 0 0 14px rgba($base-shadow-color, 0.2);
+ }
+ }
}
.account__header__content {