From 72d978ed550d67bc28f85e4fdb975b4f15ae678c Mon Sep 17 00:00:00 2001 From: or-else Date: Tue, 14 Jan 2020 18:07:56 +0300 Subject: [PATCH] previewing image before sending works now --- src/i18n/en.json | 1 + src/i18n/ru.json | 3 +- src/i18n/zh.json | 3 +- src/messages.json | 7 +- src/views/messages-view.jsx | 99 +++++++++++++---- src/widgets/image-preview.jsx | 19 +++- src/widgets/send-message.jsx | 55 ++++------ umd/index.dev.js | 193 +++++++++++++++++++++------------- umd/index.dev.js.map | 2 +- umd/index.prod.js | 2 +- umd/index.prod.js.map | 2 +- umd/tinode.dev.js | 66 +++++++++++- umd/tinode.prod.js | 2 +- 13 files changed, 308 insertions(+), 146 deletions(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index b4e584b9..b5f55528 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -146,6 +146,7 @@ "search_placeholder": "List like email:alice@example.com, tel:17025550003...", "messaging_disabled_prompt": "Messaging disabled", "new_message_prompt": "New message", + "image_caption_prompt": "Image caption", "file_attachment_too_large": "The file size {size} exceeds the {limit} limit.", "cannot_initiate_file_upload": "Cannot initiate file upload.", "tags_not_found": "No tags defined. Add some.", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 37088586..c22e813b 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -150,5 +150,6 @@ "topic_delete_warning": "Вы действительно хотите удалить этот чат?", "topic_block_warning": "Вы действительно хотите заблокировать этот чат?", "search_no_results": "Ничего не найдено", - "deleted_content": "удалено" + "deleted_content": "удалено", + "image_caption_prompt": "Подпись к фото" } diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 6e8412dd..edfcfe17 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -150,5 +150,6 @@ "topic_delete_warning": ":!![MISSING]!!:Are you sure you want to delete this conversation?", "topic_block_warning": ":!![MISSING]!!:Are you sure you want to block this conversation?", "search_no_results": ":!![MISSING]!!:Search returned no results", - "deleted_content": "内容已删除" + "deleted_content": "内容已删除", + "image_caption_prompt": "图片标题" } diff --git a/src/messages.json b/src/messages.json index 46658966..19c36312 100644 --- a/src/messages.json +++ b/src/messages.json @@ -147,6 +147,7 @@ "search_placeholder": "List like email:alice@example.com, tel:17025550003...", "messaging_disabled_prompt": "Messaging disabled", "new_message_prompt": "New message", + "image_caption_prompt": "Image caption", "file_attachment_too_large": "The file size {size} exceeds the {limit} limit.", "cannot_initiate_file_upload": "Cannot initiate file upload.", "tags_not_found": "No tags defined. Add some.", @@ -305,7 +306,8 @@ "topic_delete_warning": "Вы действительно хотите удалить этот чат?", "topic_block_warning": "Вы действительно хотите заблокировать этот чат?", "search_no_results": "Ничего не найдено", - "deleted_content": "удалено" + "deleted_content": "удалено", + "image_caption_prompt": "Подпись к фото" }, "zh": { "archived_contacts": "已归档联系人 ({count})", @@ -450,6 +452,7 @@ "action_delete_messages": "删除所有帖子", "action_block_contact": "屏蔽联系人", "action_report_chat": "检举垃圾邮件", - "deleted_content": "内容已删除" + "deleted_content": "内容已删除", + "image_caption_prompt": "图片标题" } } diff --git a/src/views/messages-view.jsx b/src/views/messages-view.jsx index 3f06002b..dc77ee68 100644 --- a/src/views/messages-view.jsx +++ b/src/views/messages-view.jsx @@ -14,9 +14,11 @@ import LoadSpinner from '../widgets/load-spinner.jsx'; import LogoView from './logo-view.jsx'; import SendMessage from '../widgets/send-message.jsx'; -import { DEFAULT_P2P_ACCESS_MODE, KEYPRESS_DELAY, MESSAGES_PAGE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE } from '../config.js'; -import { SUPPORTED_IMAGE_FORMATS, filePasted, fileToBase64, imageFileToBase64, imageFileScaledToBase64, makeImageUrl } from '../lib/blob-helpers.js'; -import { shortDateFormat } from '../lib/strformat.js'; +import { DEFAULT_P2P_ACCESS_MODE, KEYPRESS_DELAY, MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE, + MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE } from '../config.js'; +import { SUPPORTED_IMAGE_FORMATS, filePasted, fileToBase64, imageFileToBase64, + imageFileScaledToBase64, makeImageUrl } from '../lib/blob-helpers.js'; +import { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js'; const messages = defineMessages({ online_now: { @@ -61,6 +63,9 @@ class MessagesView extends React.Component { // this.propsChange = this.propsChange.bind(this); this.leave = this.leave.bind(this); + this.sendImageAttachment = this.sendImageAttachment.bind(this); + this.sendFileAttachment = this.sendFileAttachment.bind(this); + this.sendKeyPress = this.sendKeyPress.bind(this); this.handleScrollReference = this.handleScrollReference.bind(this); this.handleScrollEvent = this.handleScrollEvent.bind(this); this.handleDescChange = this.handleDescChange.bind(this); @@ -68,7 +73,8 @@ class MessagesView extends React.Component { this.handleNewMessage = this.handleNewMessage.bind(this); this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this); this.handleInfoReceipt = this.handleInfoReceipt.bind(this); - this.handleImagePreview = this.handleImagePreview.bind(this); + this.handlePreImagePreview = this.handlePreImagePreview.bind(this); + this.handlePostImagePreview = this.handlePostImagePreview.bind(this); this.handleCloseImagePreview = this.handleCloseImagePreview.bind(this); this.handleFormResponse = this.handleFormResponse.bind(this); this.handleContextClick = this.handleContextClick.bind(this); @@ -77,7 +83,6 @@ class MessagesView extends React.Component { this.handleEnablePeer = this.handleEnablePeer.bind(this); this.handleAttachFile = this.handleAttachFile.bind(this); this.handleAttachImage = this.handleAttachImage.bind(this); - this.handleNewImagePreview = this.handleNewImagePreview.bind(this); } componentDidMount() { @@ -172,7 +177,8 @@ class MessagesView extends React.Component { topic: null, title: '', avatar: null, - imagePreview: null, + imagePrePreview: null, + imagePostPreview: null, typingIndicator: false, scrollPosition: 0, fetchingMessages: false, @@ -182,7 +188,8 @@ class MessagesView extends React.Component { const topic = nextProps.tinode.getTopic(nextProps.topic); nextState = { topic: nextProps.topic, - imagePreview: null, + imagePrePreview: null, + imagePostPreview: null, typingIndicator: false, scrollPosition: 0, fetchingMessages: false @@ -435,12 +442,12 @@ class MessagesView extends React.Component { } } - handleImagePreview(content) { - this.setState({ imagePreview: content }); + handlePostImagePreview(content) { + this.setState({ imagePostPreview: content }); } handleCloseImagePreview() { - this.setState({ imagePreview: null }); + this.setState({ imagePostPreview: null, imagePrePreview: null }); } handleFormResponse(action, text, data) { @@ -495,7 +502,14 @@ class MessagesView extends React.Component { this.props.onChangePermissions(this.state.topic, DEFAULT_P2P_ACCESS_MODE, this.state.topic); } - handleAttachFile(mime, bits, fname, size, uploadCompletionPromise, uploader) { + sendKeyPress() { + const topic = this.props.tinode.getTopic(this.state.topic); + if (topic.isSubscribed()) { + topic.noteKeyPress(); + } + } + + sendFileAttachment(mime, bits, fname, size, uploadCompletionPromise, uploader) { if (uploadCompletionPromise) { // Format data and initiate upload. const msg = Drafty.attachFile(null, mime, null, fname, size, uploadCompletionPromise); @@ -506,9 +520,42 @@ class MessagesView extends React.Component { } } - handleNewImagePreview(content) { - this.setState({ imagePreview: content }); - // this.props.sendMessage(Drafty.insertImage(null, 0, mime, bits, width, height, fname)); + handleAttachFile(file) { + if (file.size > MAX_EXTERN_ATTACHMENT_SIZE) { + // Too large. + this.props.onError(formatMessage(messages.file_attachment_too_large, + {size: bytesToHumanSize(file.size), limit: bytesToHumanSize(MAX_EXTERN_ATTACHMENT_SIZE)}), 'err'); + } else if (file.size > MAX_INBAND_ATTACHMENT_SIZE) { + // Too large to send inband - uploading out of band and sending as a link. + const uploader = this.props.tinode.getLargeFileHelper(); + if (!uploader) { + this.props.onError(formatMessage(messages.cannot_initiate_upload)); + return; + } + // Pass data and the uploader to the TinodeWeb. + this.sendFileAttachment(file.type, null, file.name, file.size, uploadCompletionPromise, uploader); + } else { + // Small enough to send inband. + fileToBase64(file, + (mime, bits, fname) => { + this.sendFileAttachment(mime, bits, fname); + }, + this.props.onError + ); + } + } + + sendImageAttachment(caption, mime, bits, width, height, fname) { + let msg = Drafty.insertImage(null, 0, mime, bits, width, height, fname); + if (caption) { + msg = Drafty.appendLineBreak(msg); + msg = Drafty.append(msg, Drafty.init(caption)); + } + this.props.sendMessage(msg); + } + + handlePreImagePreview(content) { + this.setState({ imagePrePreview: content }); } handleAttachImage(file) { @@ -518,8 +565,9 @@ class MessagesView extends React.Component { imageFileScaledToBase64(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, false, // Success (bits, mime, width, height, fname) => { - this.handleNewImagePreview({ + this.handlePreImagePreview({ url: URL.createObjectURL(file), + bits: bits, filename: fname, width: width, height: height, @@ -536,8 +584,9 @@ class MessagesView extends React.Component { imageFileToBase64(file, // Success (bits, mime, width, height, fname) => { - this.handleNewImagePreview({ + this.handlePreImagePreview({ url: URL.createObjectURL(file), + bits: bits, filename: fname, width: width, height: height, @@ -567,13 +616,20 @@ class MessagesView extends React.Component { ); } else { let component2; - if (this.state.imagePreview) { + if (this.state.imagePrePreview) { + // Preview image before sending. component2 = ( + onSendMessage={this.sendImageAttachment} /> + ); + } else if (this.state.imagePostPreview) { + // Expand received image. + component2 = ( + ); } else { const topic = this.props.tinode.getTopic(this.state.topic); @@ -627,7 +683,7 @@ class MessagesView extends React.Component { sequence={sequence} received={deliveryStatus} uploader={msg._uploader} viewportWidth={this.props.viewportWidth} showContextMenu={this.handleShowContextMenuMessage} - onImagePreview={this.handleImagePreview} + onImagePreview={this.handlePostImagePreview} onFormResponse={this.handleFormResponse} onError={this.props.onError} key={msg.seq} /> @@ -723,6 +779,7 @@ class MessagesView extends React.Component { topic={this.props.topic} disabled={!this.state.isWriter} onSendMessage={this.props.sendMessage} + onKeyPress={this.sendKeyPress} onAttachFile={this.handleAttachFile} onAttachImage={this.handleAttachImage} onError={this.props.onError} />} diff --git a/src/widgets/image-preview.jsx b/src/widgets/image-preview.jsx index 3f8a4834..e69a509f 100644 --- a/src/widgets/image-preview.jsx +++ b/src/widgets/image-preview.jsx @@ -14,6 +14,8 @@ export default class ImagePreview extends React.PureComponent { width: 0, height: 0 }; + + this.handleSendImage = this.handleSendImage.bind(this); } assignWidth(node) { @@ -26,6 +28,12 @@ export default class ImagePreview extends React.PureComponent { } } + handleSendImage(caption) { + this.props.onClose(); + this.props.onSendMessage(caption, this.props.content.type, this.props.content.bits, + this.props.content.width, this.props.content.height, this.props.content.filename); + } + render() { if (!this.props.content) { return null; @@ -47,15 +55,16 @@ export default class ImagePreview extends React.PureComponent { const width = this.props.content.width || '-'; const height = this.props.content.height || '-'; return ( -
+
- {this.props.downloadable ? + {!this.props.onSendMessage ? file_download : - null} + {this.props.content.filename} + } {e.preventDefault(); this.props.onClose();}}>close
this.assignWidth(node)}> @@ -65,7 +74,9 @@ export default class ImagePreview extends React.PureComponent { :