merge with drag-n-drop
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "Drag file here",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,10 @@
|
||||
"defaultMessage": "Unsupported",
|
||||
"description": "Unsupported entity in drafty"
|
||||
},
|
||||
"drag_file": {
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner"
|
||||
},
|
||||
"editing_message": {
|
||||
"defaultMessage": "Editing",
|
||||
"description": "Title over message editing preview"
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,5 +1521,12 @@
|
||||
"description": "Icon tool tip for sending a message",
|
||||
"missing": false,
|
||||
"obsolete": false
|
||||
},
|
||||
"drag_file": {
|
||||
"translation": "",
|
||||
"defaultMessage": "Drag file here",
|
||||
"description": "Prompt on the file drag-n-drop overlay banner",
|
||||
"missing": true,
|
||||
"obsolete": false
|
||||
}
|
||||
}
|
||||
|
||||
+72
-44
@@ -9,6 +9,7 @@ import CallPanel from '../widgets/call-panel.jsx';
|
||||
import ChatMessage from '../widgets/chat-message.jsx';
|
||||
import ContactBadges from '../widgets/contact-badges.jsx';
|
||||
import DocPreview from '../widgets/doc-preview.jsx';
|
||||
import DragAndDrop from '../widgets/drag-and-drop.jsx'
|
||||
import ErrorPanel from '../widgets/error-panel.jsx';
|
||||
import GroupSubs from '../widgets/group-subs.jsx';
|
||||
import ImagePreview from '../widgets/image-preview.jsx';
|
||||
@@ -68,6 +69,11 @@ const messages = defineMessages({
|
||||
id: 'editing_message',
|
||||
defaultMessage: 'Editing',
|
||||
description: 'Title over message editing preview'
|
||||
},
|
||||
drag_file: {
|
||||
id: 'drag_file',
|
||||
defaultMessage: 'Drag file here',
|
||||
description: 'Prompt on the file drag-n-drop overlay banner'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -130,6 +136,7 @@ class MessagesView extends React.Component {
|
||||
this.postReadNotification = this.postReadNotification.bind(this);
|
||||
this.clearNotificationQueue = this.clearNotificationQueue.bind(this);
|
||||
this.goToLatestMessage = this.goToLatestMessage.bind(this);
|
||||
this.handleFileDrop = this.handleFileDrop.bind(this);
|
||||
|
||||
this.handlePickReply = this.handlePickReply.bind(this);
|
||||
this.handleEditMessage = this.handleEditMessage.bind(this);
|
||||
@@ -1044,6 +1051,19 @@ class MessagesView extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
// handleFileDrop is called when the user drags & drops a file upon the message view.
|
||||
handleFileDrop(files) {
|
||||
if (!files || files.length == 0) {
|
||||
return;
|
||||
}
|
||||
const file = files[0];
|
||||
if (file.type && file.type.startsWith('image/')) {
|
||||
this.handleAttachImageOrVideo(file);
|
||||
} else {
|
||||
this.handleAttachFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
// sendAudioAttachment sends audio bits inband as Drafty message (no preview).
|
||||
sendAudioAttachment(url, preview, duration) {
|
||||
fetch(url)
|
||||
@@ -1368,51 +1388,8 @@ class MessagesView extends React.Component {
|
||||
|
||||
const titleClass = 'panel-title' + (this.state.deleted ? ' deleted' : '');
|
||||
|
||||
component2 = (
|
||||
let messagesComponent = (
|
||||
<>
|
||||
<div id="topic-caption-panel" className="caption-panel">
|
||||
{this.props.displayMobile ?
|
||||
<a href="#" id="hide-message-view" onClick={(e) => {e.preventDefault(); this.props.onHideMessagesView();}}>
|
||||
<i className="material-icons">arrow_back</i>
|
||||
</a>
|
||||
:
|
||||
null}
|
||||
<div className="avatar-box">
|
||||
<LetterTile
|
||||
tinode={this.props.tinode}
|
||||
avatar={avatar}
|
||||
topic={this.state.topic}
|
||||
title={this.state.title}
|
||||
deleted={this.state.deleted} />
|
||||
{!isChannel ? <span className={online} /> : null}
|
||||
</div>
|
||||
<div id="topic-title-group">
|
||||
<div id="topic-title" className={titleClass}>{
|
||||
this.state.title ||
|
||||
<i><FormattedMessage id="unnamed_topic" defaultMessage="Unnamed"
|
||||
description="Title shown when the topic has no name" /></i>
|
||||
}<ContactBadges badges={icon_badges} /></div>
|
||||
<div id="topic-last-seen">{lastSeen}</div>
|
||||
</div>
|
||||
{groupTopic ?
|
||||
<GroupSubs
|
||||
tinode={this.props.tinode}
|
||||
subscribers={this.state.onlineSubs} /> :
|
||||
<div id="topic-users" />
|
||||
}
|
||||
<div>
|
||||
<a href="#" onClick={this.handleContextClick}>
|
||||
<i className="material-icons">more_vert</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{this.props.displayMobile ?
|
||||
<ErrorPanel
|
||||
level={this.props.errorLevel}
|
||||
text={this.props.errorText}
|
||||
onClearError={this.props.onError} />
|
||||
: null}
|
||||
<LoadSpinner show={this.state.fetchingMessages} />
|
||||
<div id="messages-container">
|
||||
<button className={'action-button' + (this.state.showGoToLastButton ? '' : ' hidden')}
|
||||
onClick={this.goToLatestMessage}>
|
||||
@@ -1463,6 +1440,57 @@ class MessagesView extends React.Component {
|
||||
onCancelReply={this.handleCancelReply} />}
|
||||
</>
|
||||
);
|
||||
|
||||
component2 = (
|
||||
<>
|
||||
<div id="topic-caption-panel" className="caption-panel">
|
||||
{this.props.displayMobile ?
|
||||
<a href="#" id="hide-message-view" onClick={(e) => {e.preventDefault(); this.props.onHideMessagesView();}}>
|
||||
<i className="material-icons">arrow_back</i>
|
||||
</a>
|
||||
:
|
||||
null}
|
||||
<div className="avatar-box">
|
||||
<LetterTile
|
||||
tinode={this.props.tinode}
|
||||
avatar={avatar}
|
||||
topic={this.state.topic}
|
||||
title={this.state.title}
|
||||
deleted={this.state.deleted} />
|
||||
{!isChannel ? <span className={online} /> : null}
|
||||
</div>
|
||||
<div id="topic-title-group">
|
||||
<div id="topic-title" className={titleClass}>{
|
||||
this.state.title ||
|
||||
<i><FormattedMessage id="unnamed_topic" defaultMessage="Unnamed"
|
||||
description="Title shown when the topic has no name" /></i>
|
||||
}<ContactBadges badges={icon_badges} /></div>
|
||||
<div id="topic-last-seen">{lastSeen}</div>
|
||||
</div>
|
||||
{groupTopic ?
|
||||
<GroupSubs
|
||||
tinode={this.props.tinode}
|
||||
subscribers={this.state.onlineSubs} /> :
|
||||
<div id="topic-users" />
|
||||
}
|
||||
<div>
|
||||
<a href="#" onClick={this.handleContextClick}>
|
||||
<i className="material-icons">more_vert</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{this.props.displayMobile ?
|
||||
<ErrorPanel
|
||||
level={this.props.errorLevel}
|
||||
text={this.props.errorText}
|
||||
onClearError={this.props.onError} />
|
||||
: null}
|
||||
<LoadSpinner show={this.state.fetchingMessages} />
|
||||
{!this.state.unconfirmed && !this.props.forwardMessage ?
|
||||
<DragAndDrop actionPrompt={this.props.intl.formatMessage(messages.drag_file)} onDrop={this.handleFileDrop} >{messagesComponent}</DragAndDrop>
|
||||
: messagesComponent}
|
||||
</>
|
||||
);
|
||||
}
|
||||
component = <div id="topic-view">{component2}</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Drag & drop container: displays a banner on drag and calls a handler on drop.
|
||||
import React from 'react';
|
||||
|
||||
export default class DragAndDrop extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
// A file is being dragged over the container.
|
||||
dragging: false
|
||||
};
|
||||
this.dropRef = React.createRef();
|
||||
// Keeps track of the drag event.
|
||||
// Need a counter b/c the browser's 'drag' events may fire multiple times
|
||||
// when the user takes the mouse pointer over the container:
|
||||
// for the component itself and for all nested/child elements.
|
||||
this.dragCounter = 0;
|
||||
|
||||
this.handleDrag = this.handleDrag.bind(this);
|
||||
this.handleDragIn = this.handleDragIn.bind(this);
|
||||
this.handleDragOut = this.handleDragOut.bind(this);
|
||||
this.handleDrop = this.handleDrop.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let div = this.dropRef.current;
|
||||
div.addEventListener('dragenter', this.handleDragIn);
|
||||
div.addEventListener('dragleave', this.handleDragOut);
|
||||
div.addEventListener('dragover', this.handleDrag);
|
||||
div.addEventListener('drop', this.handleDrop);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
let div = this.dropRef.current;
|
||||
div.removeEventListener('dragenter', this.handleDragIn);
|
||||
div.removeEventListener('dragleave', this.handleDragOut);
|
||||
div.removeEventListener('dragover', this.handleDrag);
|
||||
div.removeEventListener('drop', this.handleDrop);
|
||||
}
|
||||
|
||||
handleDrag(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
handleDragIn(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.dragCounter++;
|
||||
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
|
||||
this.setState({dragging: true});
|
||||
}
|
||||
}
|
||||
|
||||
handleDragOut(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.dragCounter--;
|
||||
if (this.dragCounter == 0) {
|
||||
this.setState({dragging: false});
|
||||
}
|
||||
}
|
||||
|
||||
handleDrop(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.setState({dragging: false});
|
||||
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||||
this.props.onDrop(e.dataTransfer.files);
|
||||
e.dataTransfer.clearData();
|
||||
this.dragCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='drag-and-drop' ref={this.dropRef}>
|
||||
{this.state.dragging ?
|
||||
<div className='banner'>{this.props.actionPrompt}</div>
|
||||
: null}
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -80,6 +80,7 @@ class SendMessage extends React.PureComponent {
|
||||
this.handleSend = this.handleSend.bind(this);
|
||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||
this.handleMessageTyping = this.handleMessageTyping.bind(this);
|
||||
this.handleDropAttach = this.handleDropAttach.bind(this);
|
||||
|
||||
this.handleQuoteClick = this.handleQuoteClick.bind(this);
|
||||
|
||||
@@ -159,6 +160,13 @@ class SendMessage extends React.PureComponent {
|
||||
e.target.value = '';
|
||||
}
|
||||
|
||||
handleDropAttach(files) {
|
||||
if (files && files.length > 0) {
|
||||
console.log('Dropping ', files);
|
||||
this.props.onAttachFile(files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
handleAttachAudio(url, preview, duration) {
|
||||
this.setState({audioRec: false});
|
||||
this.props.onAttachAudio(url, preview, duration);
|
||||
@@ -260,10 +268,9 @@ class SendMessage extends React.PureComponent {
|
||||
onFinished={this.handleAttachAudio}/>
|
||||
</Suspense>) :
|
||||
<textarea id="sendMessage" placeholder={prompt}
|
||||
value={this.state.message}
|
||||
onChange={this.handleMessageTyping}
|
||||
value={this.state.message} onChange={this.handleMessageTyping}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
ref={ref => {this.messageEditArea = ref;}}
|
||||
ref={(ref) => {this.messageEditArea = ref;}}
|
||||
autoFocus />)}
|
||||
{this.state.message || !audioEnabled ?
|
||||
<a href="#" onClick={this.handleSend} title={formatMessage(messages.icon_title_send)}>
|
||||
|
||||
Reference in New Issue
Block a user