From 4ec1771165ab8dd40e52804fd087eacfab25290b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 28 Sep 2017 15:31:31 +0200 Subject: Add ability to specify alternative text for media attachments (#5123) * Fix #117 - Add ability to specify alternative text for media attachments - POST /api/v1/media accepts `description` straight away - PUT /api/v1/media/:id to update `description` (only for unattached ones) - Serialized as `name` of Document object in ActivityPub - Uploads form adjusted for better performance and description input * Add tests * Change undo button blend mode to difference --- app/javascript/mastodon/actions/compose.js | 38 ++++ .../mastodon/components/extended_video_player.js | 14 +- .../mastodon/components/media_gallery.js | 3 +- app/javascript/mastodon/components/video_player.js | 204 --------------------- .../mastodon/features/compose/components/upload.js | 96 ++++++++++ .../features/compose/components/upload_form.js | 44 ++--- .../compose/containers/upload_container.js | 21 +++ .../compose/containers/upload_form_container.js | 13 +- .../mastodon/features/ui/components/media_modal.js | 5 +- .../mastodon/features/ui/components/video_modal.js | 1 + .../mastodon/features/ui/util/async-components.js | 4 - app/javascript/mastodon/features/video/index.js | 4 +- app/javascript/mastodon/reducers/compose.js | 19 +- app/javascript/styles/components.scss | 47 ++++- 14 files changed, 243 insertions(+), 270 deletions(-) delete mode 100644 app/javascript/mastodon/components/video_player.js create mode 100644 app/javascript/mastodon/features/compose/components/upload.js create mode 100644 app/javascript/mastodon/features/compose/containers/upload_container.js (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 9f10a8c15..8be5b939f 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -37,6 +37,10 @@ export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE'; export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT'; +export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST'; +export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS'; +export const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL'; + export function changeCompose(text) { return { type: COMPOSE_CHANGE, @@ -165,6 +169,40 @@ export function uploadCompose(files) { }; }; +export function changeUploadCompose(id, description) { + return (dispatch, getState) => { + dispatch(changeUploadComposeRequest()); + + api(getState).put(`/api/v1/media/${id}`, { description }).then(response => { + dispatch(changeUploadComposeSuccess(response.data)); + }).catch(error => { + dispatch(changeUploadComposeFail(id, error)); + }); + }; +}; + +export function changeUploadComposeRequest() { + return { + type: COMPOSE_UPLOAD_CHANGE_REQUEST, + skipLoading: true, + }; +}; +export function changeUploadComposeSuccess(media) { + return { + type: COMPOSE_UPLOAD_CHANGE_SUCCESS, + media: media, + skipLoading: true, + }; +}; + +export function changeUploadComposeFail(error) { + return { + type: COMPOSE_UPLOAD_CHANGE_FAIL, + error: error, + skipLoading: true, + }; +}; + export function uploadComposeRequest() { return { type: COMPOSE_UPLOAD_REQUEST, diff --git a/app/javascript/mastodon/components/extended_video_player.js b/app/javascript/mastodon/components/extended_video_player.js index 5ab5e9e58..f8bd067e8 100644 --- a/app/javascript/mastodon/components/extended_video_player.js +++ b/app/javascript/mastodon/components/extended_video_player.js @@ -5,6 +5,7 @@ export default class ExtendedVideoPlayer extends React.PureComponent { static propTypes = { src: PropTypes.string.isRequired, + alt: PropTypes.string, width: PropTypes.number, height: PropTypes.number, time: PropTypes.number, @@ -31,15 +32,20 @@ export default class ExtendedVideoPlayer extends React.PureComponent { } render () { + const { src, muted, controls, alt } = this.props; + return (
); diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index a81409871..38b26b1fc 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -136,7 +136,7 @@ class Item extends React.PureComponent { onClick={this.handleClick} target='_blank' > - + {attachment.get('description')} ); } else if (attachment.get('type') === 'gifv') { @@ -146,6 +146,7 @@ class Item extends React.PureComponent {
diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index b8c5e885a..ad5493f8c 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -90,10 +90,6 @@ export function MediaGallery () { return import(/* webpackChunkName: "status/media_gallery" */'../../../components/media_gallery'); } -export function VideoPlayer () { - return import(/* webpackChunkName: "status/video_player" */'../../../components/video_player'); -} - export function Video () { return import(/* webpackChunkName: "features/video" */'../../video'); } diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js index f228e434b..069264ef5 100644 --- a/app/javascript/mastodon/features/video/index.js +++ b/app/javascript/mastodon/features/video/index.js @@ -104,6 +104,7 @@ export default class Video extends React.PureComponent { static propTypes = { preview: PropTypes.string, src: PropTypes.string.isRequired, + alt: PropTypes.string, width: PropTypes.number, height: PropTypes.number, sensitive: PropTypes.bool, @@ -247,7 +248,7 @@ export default class Video extends React.PureComponent { } render () { - const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl } = this.props; + const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt } = this.props; const { progress, dragging, paused, fullscreen, hovered, muted, revealed } = this.state; return ( @@ -260,6 +261,7 @@ export default class Video extends React.PureComponent { loop role='button' tabIndex='0' + aria-label={alt} width={width} height={height} onClick={this.togglePlay} diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 9d39584fc..082d4d370 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -22,6 +22,9 @@ import { COMPOSE_VISIBILITY_CHANGE, COMPOSE_COMPOSING_CHANGE, COMPOSE_EMOJI_INSERT, + COMPOSE_UPLOAD_CHANGE_REQUEST, + COMPOSE_UPLOAD_CHANGE_SUCCESS, + COMPOSE_UPLOAD_CHANGE_FAIL, } from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; import { STORE_HYDRATE } from '../actions/store'; @@ -220,15 +223,15 @@ export default function compose(state = initialState, action) { map.set('idempotencyKey', uuid()); }); case COMPOSE_SUBMIT_REQUEST: + case COMPOSE_UPLOAD_CHANGE_REQUEST: return state.set('is_submitting', true); case COMPOSE_SUBMIT_SUCCESS: return clearAll(state); case COMPOSE_SUBMIT_FAIL: + case COMPOSE_UPLOAD_CHANGE_FAIL: return state.set('is_submitting', false); case COMPOSE_UPLOAD_REQUEST: - return state.withMutations(map => { - map.set('is_uploading', true); - }); + return state.set('is_uploading', true); case COMPOSE_UPLOAD_SUCCESS: return appendMedia(state, fromJS(action.media)); case COMPOSE_UPLOAD_FAIL: @@ -256,6 +259,16 @@ export default function compose(state = initialState, action) { } case COMPOSE_EMOJI_INSERT: return insertEmoji(state, action.position, action.emoji); + case COMPOSE_UPLOAD_CHANGE_SUCCESS: + return state + .set('is_submitting', false) + .update('media_attachments', list => list.map(item => { + if (item.get('id') === action.media.id) { + return item.set('description', action.media.description); + } + + return item; + })); default: return state; } diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index da479347b..631cd7a13 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -335,12 +335,52 @@ .compose-form__uploads-wrapper { display: flex; + flex-direction: row; padding: 5px; + flex-wrap: wrap; } .compose-form__upload { flex: 1 1 0; + min-width: 40%; margin: 5px; + + &-description { + position: absolute; + z-index: 2; + bottom: 0; + left: 0; + right: 0; + box-sizing: border-box; + background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent); + padding: 10px; + opacity: 0; + transition: opacity .1s ease; + + input { + background: transparent; + color: $ui-secondary-color; + border: 0; + padding: 0; + margin: 0; + width: 100%; + font-family: inherit; + font-size: 14px; + font-weight: 500; + + &:focus { + color: $white; + } + } + + &.active { + opacity: 1; + } + } + + .icon-button { + mix-blend-mode: difference; + } } .compose-form__upload-thumbnail { @@ -352,13 +392,6 @@ width: 100%; } -.compose-form__upload-cancel { - background-size: cover; - border-radius: 4px; - height: 100px; - width: 100px; -} - .compose-form__label { display: block; line-height: 24px; -- cgit