diff --git a/app/soapbox/components/media_gallery.js b/app/soapbox/components/media_gallery.js index 0fc7dcb67..3eda57795 100644 --- a/app/soapbox/components/media_gallery.js +++ b/app/soapbox/components/media_gallery.js @@ -6,6 +6,7 @@ import { is } from 'immutable'; import IconButton from './icon_button'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { isIOS } from '../is_mobile'; +import { truncateFilename } from 'soapbox/utils/media'; import classNames from 'classnames'; import { decode } from 'blurhash'; import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio'; @@ -14,6 +15,8 @@ import { getSettings } from 'soapbox/actions/settings'; import Icon from 'soapbox/components/icon'; import StillImage from 'soapbox/components/still_image'; +const MAX_FILENAME_LENGTH = 45; + const messages = defineMessages({ toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' }, }); @@ -142,19 +145,8 @@ class Item extends React.PureComponent { let thumbnail = ''; - const MAX_FILENAME_LENGTH = 45; - const getCroppedFilename = () => { - const remoteURL = attachment.get('remote_url'); - const filenameLastIndex = remoteURL.lastIndexOf('/'); - const filename = remoteURL.substr(filenameLastIndex + 1); - if (filename.length <= MAX_FILENAME_LENGTH) - return filename; - else - return filename.substr(0, MAX_FILENAME_LENGTH/2) + '...' + filename.substr(filename.length - MAX_FILENAME_LENGTH/2); - }; - if (attachment.get('type') === 'unknown') { - const filename = getCroppedFilename(); + const filename = truncateFilename(attachment.get('remote_url'), MAX_FILENAME_LENGTH); return (
@@ -228,7 +220,7 @@ class Item extends React.PureComponent { } return ( -
+
{visible && thumbnail}
diff --git a/app/soapbox/features/chats/components/chat_box.js b/app/soapbox/features/chats/components/chat_box.js index 12b28d513..ee2b7cd20 100644 --- a/app/soapbox/features/chats/components/chat_box.js +++ b/app/soapbox/features/chats/components/chat_box.js @@ -10,6 +10,11 @@ import { } from 'soapbox/actions/chats'; import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import ChatMessageList from './chat_message_list'; +import UploadButton from 'soapbox/features/compose/components/upload_button'; +import { uploadMedia } from 'soapbox/actions/media'; +import UploadProgress from 'soapbox/features/compose/components/upload_progress'; +import { truncateFilename } from 'soapbox/utils/media'; +import IconButton from 'soapbox/components/icon_button'; const messages = defineMessages({ placeholder: { id: 'chat_box.input.placeholder', defaultMessage: 'Send a message…' }, @@ -21,6 +26,8 @@ const mapStateToProps = (state, { chatId }) => ({ chatMessageIds: state.getIn(['chat_message_lists', chatId], ImmutableOrderedSet()), }); +const fileKeyGen = () => Math.floor((Math.random() * 0x10000)); + export default @connect(mapStateToProps) @injectIntl class ChatBox extends ImmutablePureComponent { @@ -35,15 +42,50 @@ class ChatBox extends ImmutablePureComponent { me: PropTypes.node, } - state = { + initialState = () => ({ content: '', + attachment: undefined, + isUploading: false, + uploadProgress: 0, + resetFileKey: fileKeyGen(), + }) + + state = this.initialState() + + clearState = () => { + this.setState(this.initialState()); + } + + getParams = () => { + const { content, attachment } = this.state; + + return { + content, + media_id: attachment && attachment.id, + }; + } + + canSubmit = () => { + const { content, attachment } = this.state; + + const conds = [ + content.length > 0, + attachment, + ]; + + return conds.some(c => c); } sendMessage = () => { - const { chatId } = this.props; - if (this.state.content.length < 1) return; - this.props.dispatch(sendChatMessage(chatId, this.state)); - this.setState({ content: '' }); + const { dispatch, chatId } = this.props; + const { isUploading } = this.state; + + if (this.canSubmit() && !isUploading) { + const params = this.getParams(); + + dispatch(sendChatMessage(chatId, params)); + this.clearState(); + } } insertLine = () => { @@ -91,20 +133,76 @@ class ChatBox extends ImmutablePureComponent { this.markRead(); } + handleRemoveFile = (e) => { + this.setState({ attachment: undefined, resetFileKey: fileKeyGen() }); + } + + onUploadProgress = (e) => { + const { loaded, total } = e; + this.setState({ uploadProgress: loaded/total }); + } + + handleFiles = (files) => { + const { dispatch } = this.props; + + this.setState({ isUploading: true }); + + const data = new FormData(); + data.append('file', files[0]); + + dispatch(uploadMedia(data, this.onUploadProgress)).then(response => { + this.setState({ attachment: response.data, isUploading: false }); + }).catch(() => { + this.setState({ isUploading: false }); + }); + } + + renderAttachment = () => { + const { attachment } = this.state; + if (!attachment) return null; + + return ( +
+
+ {truncateFilename(attachment.preview_url, 20)} +
+
+ +
+
+ ); + } + + renderActionButton = () => { + const { resetFileKey } = this.state; + + return this.canSubmit() ? ( +
+ +
+ ) : ( + + ); + } + render() { const { chatMessageIds, chatId, intl } = this.props; + const { content, isUploading, uploadProgress } = this.state; if (!chatMessageIds) return null; return (
+ {this.renderAttachment()} +
+ {this.renderActionButton()}