Basic message forwarding.
This commit is contained in:
@@ -2528,3 +2528,16 @@ ul.tabbar li.active > a {
|
|||||||
.mention {
|
.mention {
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Message forwarding. */
|
||||||
|
|
||||||
|
.forward-menu {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 4;
|
||||||
|
max-height: 80%;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
padding-top: 0.35rem;
|
||||||
|
padding-bottom: 0.35rem;
|
||||||
|
box-shadow: 0 1px 6px rgba(0, 0, 0, .45);
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|||||||
+34
-35
@@ -214,6 +214,17 @@ class MessagesView extends React.Component {
|
|||||||
};
|
};
|
||||||
} else if (nextProps.topic != prevState.topic) {
|
} else if (nextProps.topic != prevState.topic) {
|
||||||
const topic = nextProps.tinode.getTopic(nextProps.topic);
|
const topic = nextProps.tinode.getTopic(nextProps.topic);
|
||||||
|
|
||||||
|
let reply = null;
|
||||||
|
if (nextProps.forwardedMessage) {
|
||||||
|
// We are forwarding a message. Show preview.
|
||||||
|
const preview = nextProps.forwardedMessage.preview;
|
||||||
|
reply = {
|
||||||
|
content: preview,
|
||||||
|
seq: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
nextState = {
|
nextState = {
|
||||||
topic: nextProps.topic,
|
topic: nextProps.topic,
|
||||||
docPreview: null,
|
docPreview: null,
|
||||||
@@ -222,7 +233,7 @@ class MessagesView extends React.Component {
|
|||||||
typingIndicator: false,
|
typingIndicator: false,
|
||||||
scrollPosition: 0,
|
scrollPosition: 0,
|
||||||
fetchingMessages: false,
|
fetchingMessages: false,
|
||||||
reply: null
|
reply: reply
|
||||||
};
|
};
|
||||||
|
|
||||||
if (topic) {
|
if (topic) {
|
||||||
@@ -665,14 +676,20 @@ class MessagesView extends React.Component {
|
|||||||
// sendMessage sends the message with an optional subscription to topic first.
|
// sendMessage sends the message with an optional subscription to topic first.
|
||||||
sendMessage(msg, uploadCompletionPromise, uploader) {
|
sendMessage(msg, uploadCompletionPromise, uploader) {
|
||||||
let head;
|
let head;
|
||||||
if (this.state.reply && this.state.reply.content) {
|
if (this.props.forwardedMessage) {
|
||||||
|
// We are forwarding a message.
|
||||||
|
msg = this.props.forwardedMessage.msg;
|
||||||
|
head = this.props.forwardedMessage.head;
|
||||||
|
this.handleCancelReply();
|
||||||
|
} else if (this.state.reply && this.state.reply.content) {
|
||||||
|
// We are replying to a message in this topic.
|
||||||
head = {reply: '' + this.state.reply.seq};
|
head = {reply: '' + this.state.reply.seq};
|
||||||
// Turn it into Drafty so we can make a quoted Drafty object later.
|
// Turn it into Drafty so we can make a quoted Drafty object later.
|
||||||
if (typeof msg == 'string') {
|
if (typeof msg == 'string') {
|
||||||
msg = Drafty.parse(msg);
|
msg = Drafty.parse(msg);
|
||||||
}
|
}
|
||||||
msg = Drafty.append(this.state.reply.content, msg);
|
msg = Drafty.append(this.state.reply.content, msg);
|
||||||
this.setState({reply: null});
|
this.handleCancelReply();
|
||||||
}
|
}
|
||||||
this.props.sendMessage(msg, uploadCompletionPromise, uploader, head);
|
this.props.sendMessage(msg, uploadCompletionPromise, uploader, head);
|
||||||
}
|
}
|
||||||
@@ -825,7 +842,7 @@ class MessagesView extends React.Component {
|
|||||||
uploader.cancel();
|
uploader.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePickReply(seq, content) {
|
handlePickReply(seq, content, senderId, senderName) {
|
||||||
this.setState({reply: null});
|
this.setState({reply: null});
|
||||||
|
|
||||||
if (!seq || !content) {
|
if (!seq || !content) {
|
||||||
@@ -843,23 +860,6 @@ class MessagesView extends React.Component {
|
|||||||
Drafty.wrapInto(this.props.intl.formatMessage(messages.invalid_content), 'EM'));
|
Drafty.wrapInto(this.props.intl.formatMessage(messages.invalid_content), 'EM'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the author.
|
|
||||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
|
||||||
const msg = topic.findMessage(seq);
|
|
||||||
let senderName, senderId;
|
|
||||||
if (msg) {
|
|
||||||
senderId = msg.from || 'chan';
|
|
||||||
if (senderId != this.props.myUserId) {
|
|
||||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
|
||||||
const user = topic.userDesc(senderId);
|
|
||||||
if (user && user.public) {
|
|
||||||
senderName = user.public.fn;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
senderName = this.props.myUserName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
reply: {
|
reply: {
|
||||||
content: Drafty.quote(senderName, senderId, content),
|
content: Drafty.quote(senderName, senderId, content),
|
||||||
@@ -869,7 +869,8 @@ class MessagesView extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleCancelReply() {
|
handleCancelReply() {
|
||||||
this.setState({reply: null})
|
this.setState({reply: null});
|
||||||
|
this.props.onCancelForwardMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleQuoteClick(replyToSeq) {
|
handleQuoteClick(replyToSeq) {
|
||||||
@@ -964,18 +965,14 @@ class MessagesView extends React.Component {
|
|||||||
const isReply = !(thisFrom == this.props.myUserId);
|
const isReply = !(thisFrom == this.props.myUserId);
|
||||||
const deliveryStatus = topic.msgStatus(msg, true);
|
const deliveryStatus = topic.msgStatus(msg, true);
|
||||||
|
|
||||||
let userName, userAvatar, userFrom;
|
let userFrom = thisFrom, userName, userAvatar;
|
||||||
if (groupTopic) {
|
const user = topic.userDesc(thisFrom);
|
||||||
const user = topic.userDesc(thisFrom);
|
if (user && user.public) {
|
||||||
if (user && user.public) {
|
userName = user.public.fn;
|
||||||
userName = user.public.fn;
|
userAvatar = makeImageUrl(user.public.photo);
|
||||||
userAvatar = makeImageUrl(user.public.photo);
|
|
||||||
}
|
|
||||||
userFrom = thisFrom;
|
|
||||||
chatBoxClass='chat-box group';
|
|
||||||
} else {
|
|
||||||
chatBoxClass='chat-box';
|
|
||||||
}
|
}
|
||||||
|
//userFrom = thisFrom;
|
||||||
|
chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box';
|
||||||
|
|
||||||
// Ref for this chat message.
|
// Ref for this chat message.
|
||||||
const ref = this.getOrCreateMessageRef(msg.seq);
|
const ref = this.getOrCreateMessageRef(msg.seq);
|
||||||
@@ -993,6 +990,7 @@ class MessagesView extends React.Component {
|
|||||||
timestamp={msg.ts}
|
timestamp={msg.ts}
|
||||||
response={isReply}
|
response={isReply}
|
||||||
seq={msg.seq}
|
seq={msg.seq}
|
||||||
|
isGroup={groupTopic}
|
||||||
userFrom={userFrom}
|
userFrom={userFrom}
|
||||||
userName={userName}
|
userName={userName}
|
||||||
userAvatar={userAvatar}
|
userAvatar={userAvatar}
|
||||||
@@ -1106,11 +1104,12 @@ class MessagesView extends React.Component {
|
|||||||
:
|
:
|
||||||
<SendMessage
|
<SendMessage
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
|
noInput={this.props.forwardedMessage != null}
|
||||||
disabled={!this.state.isWriter}
|
disabled={!this.state.isWriter}
|
||||||
onKeyPress={this.sendKeyPress}
|
onKeyPress={this.sendKeyPress}
|
||||||
onSendMessage={this.sendMessage}
|
onSendMessage={this.sendMessage}
|
||||||
onAttachFile={this.handleAttachFile}
|
onAttachFile={this.props.forwardedMessage == null ? this.handleAttachFile : null}
|
||||||
onAttachImage={this.handleAttachImage}
|
onAttachImage={this.props.forwardedMessage == null ? this.handleAttachImage : null}
|
||||||
onError={this.props.onError}
|
onError={this.props.onError}
|
||||||
replyTo={this.state.reply}
|
replyTo={this.state.reply}
|
||||||
onQuoteClick={this.handleQuoteClick}
|
onQuoteClick={this.handleQuoteClick}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Tinode from 'tinode-sdk';
|
|||||||
|
|
||||||
import Alert from '../widgets/alert.jsx';
|
import Alert from '../widgets/alert.jsx';
|
||||||
import ContextMenu from '../widgets/context-menu.jsx';
|
import ContextMenu from '../widgets/context-menu.jsx';
|
||||||
|
import ForwardMenu from '../widgets/forward-menu.jsx';
|
||||||
|
|
||||||
import InfoView from './info-view.jsx';
|
import InfoView from './info-view.jsx';
|
||||||
import MessagesView from './messages-view.jsx';
|
import MessagesView from './messages-view.jsx';
|
||||||
@@ -140,6 +141,11 @@ class TinodeWeb extends React.Component {
|
|||||||
this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);
|
this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);
|
||||||
this.handleResetPassword = this.handleResetPassword.bind(this);
|
this.handleResetPassword = this.handleResetPassword.bind(this);
|
||||||
this.handleContextMenuAction = this.handleContextMenuAction.bind(this);
|
this.handleContextMenuAction = this.handleContextMenuAction.bind(this);
|
||||||
|
|
||||||
|
this.handleShowForwardMenu = this.handleShowForwardMenu.bind(this);
|
||||||
|
this.handleHideForwardMenu = this.handleHideForwardMenu.bind(this);
|
||||||
|
|
||||||
|
this.sendMessageToTopic = this.sendMessageToTopic.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlankState() {
|
getBlankState() {
|
||||||
@@ -197,6 +203,11 @@ class TinodeWeb extends React.Component {
|
|||||||
contextMenuParams: null,
|
contextMenuParams: null,
|
||||||
contextMenuItems: [],
|
contextMenuItems: [],
|
||||||
|
|
||||||
|
forwardMenuVisible: false,
|
||||||
|
forwardMenuBounds: null,
|
||||||
|
forwardMenuClickAt: null,
|
||||||
|
forwardMessage: null,
|
||||||
|
|
||||||
// Modal alert dialog.
|
// Modal alert dialog.
|
||||||
alertVisible: false,
|
alertVisible: false,
|
||||||
alertParams: {},
|
alertParams: {},
|
||||||
@@ -552,6 +563,7 @@ class TinodeWeb extends React.Component {
|
|||||||
errorLevel: err && err.message ? 'err' : 'warn',
|
errorLevel: err && err.message ? 'err' : 'warn',
|
||||||
loginDisabled: false,
|
loginDisabled: false,
|
||||||
contextMenuVisible: false,
|
contextMenuVisible: false,
|
||||||
|
forwardMenuVisible: false,
|
||||||
serverVersion: "no connection"
|
serverVersion: "no connection"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -872,7 +884,8 @@ class TinodeWeb extends React.Component {
|
|||||||
if (this.state.topicSelected != topicName) {
|
if (this.state.topicSelected != topicName) {
|
||||||
this.setState({
|
this.setState({
|
||||||
topicSelectedOnline: this.tinode.isTopicOnline(topicName),
|
topicSelectedOnline: this.tinode.isTopicOnline(topicName),
|
||||||
topicSelectedAcs: this.tinode.getTopicAccessMode(topicName)
|
topicSelectedAcs: this.tinode.getTopicAccessMode(topicName),
|
||||||
|
forwardMessage: null
|
||||||
});
|
});
|
||||||
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', topicName));
|
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', topicName));
|
||||||
}
|
}
|
||||||
@@ -884,7 +897,8 @@ class TinodeWeb extends React.Component {
|
|||||||
mobilePanel: 'sidepanel',
|
mobilePanel: 'sidepanel',
|
||||||
topicSelectedOnline: false,
|
topicSelectedOnline: false,
|
||||||
topicSelectedAcs: null,
|
topicSelectedAcs: null,
|
||||||
infoPanel: undefined
|
infoPanel: undefined,
|
||||||
|
forwardMessage: null
|
||||||
});
|
});
|
||||||
|
|
||||||
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));
|
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));
|
||||||
@@ -907,7 +921,10 @@ class TinodeWeb extends React.Component {
|
|||||||
// - head - head dictionary to be attached to the message
|
// - head - head dictionary to be attached to the message
|
||||||
handleSendMessage(msg, promise, uploader, head) {
|
handleSendMessage(msg, promise, uploader, head) {
|
||||||
const topic = this.tinode.getTopic(this.state.topicSelected);
|
const topic = this.tinode.getTopic(this.state.topicSelected);
|
||||||
|
this.sendMessageToTopic(topic, msg, promise, uploader, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessageToTopic(topic, msg, promise, uploader, head) {
|
||||||
msg = topic.createMessage(msg, false);
|
msg = topic.createMessage(msg, false);
|
||||||
// The uploader is used to show progress.
|
// The uploader is used to show progress.
|
||||||
msg._uploader = uploader;
|
msg._uploader = uploader;
|
||||||
@@ -1428,6 +1445,32 @@ class TinodeWeb extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleShowForwardMenu(params) {
|
||||||
|
if (this.state.sidePanelSelected == 'newtpk') {
|
||||||
|
// If the Find panel is active, close it.
|
||||||
|
this.handleSidepanelCancel();
|
||||||
|
}
|
||||||
|
const header = "Forwarded from " + params.userName;
|
||||||
|
const content = typeof params.content == 'string' ? Tinode.Drafty.init(params.content) : params.content;
|
||||||
|
const preview = Tinode.Drafty.preview(content, 30);
|
||||||
|
const msg = Tinode.Drafty.append(
|
||||||
|
Tinode.Drafty.appendLineBreak(
|
||||||
|
Tinode.Drafty.mention(header, params.userFrom)),
|
||||||
|
content);
|
||||||
|
const msgPreview = Tinode.Drafty.quote(header, params.userFrom, preview);
|
||||||
|
const head = {
|
||||||
|
// TODO: decide what parameters to store.
|
||||||
|
fwdFrom: params.topicName,
|
||||||
|
authorUid: params.userFrom
|
||||||
|
};
|
||||||
|
this.setState({
|
||||||
|
forwardMenuVisible: true,
|
||||||
|
forwardMenuBounds: this.selfRef.current.getBoundingClientRect(),
|
||||||
|
forwardMenuClickAt: {x: params.x, y: params.y},
|
||||||
|
forwardMessage: { head: head, msg: msg, preview: msgPreview }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
defaultTopicContextMenu(topicName) {
|
defaultTopicContextMenu(topicName) {
|
||||||
const topic = this.tinode.getTopic(topicName);
|
const topic = this.tinode.getTopic(topicName);
|
||||||
|
|
||||||
@@ -1468,6 +1511,15 @@ class TinodeWeb extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHideForwardMenu(keepForwardedMessage) {
|
||||||
|
this.setState({
|
||||||
|
forwardMenuVisible: false,
|
||||||
|
forwardMenuBounds: null,
|
||||||
|
forwardMenuClickAt: null,
|
||||||
|
forwardMessage: keepForwardedMessage ? this.state.forwardMessage : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleContextMenuAction(action, promise, params) {
|
handleContextMenuAction(action, promise, params) {
|
||||||
if (action == 'topic_archive') {
|
if (action == 'topic_archive') {
|
||||||
if (promise && params.topicName && params.topicName == this.state.topicSelected) {
|
if (promise && params.topicName && params.topicName == this.state.topicSelected) {
|
||||||
@@ -1475,6 +1527,8 @@ class TinodeWeb extends React.Component {
|
|||||||
this.handleTopicSelected(null);
|
this.handleTopicSelected(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (action == 'menu_item_forward') {
|
||||||
|
this.handleShowForwardMenu(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1590,6 +1644,25 @@ class TinodeWeb extends React.Component {
|
|||||||
:
|
:
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
{this.state.forwardMenuVisible ?
|
||||||
|
<ForwardMenu
|
||||||
|
tinode={this.tinode}
|
||||||
|
bounds={this.state.forwardMenuBounds}
|
||||||
|
clickAt={this.state.forwardMenuClickAt}
|
||||||
|
|
||||||
|
contacts={this.state.chatList}
|
||||||
|
topicSelected={this.props.topicSelected}
|
||||||
|
myUserId={this.state.myUserId}
|
||||||
|
|
||||||
|
hide={this.handleHideForwardMenu}
|
||||||
|
onInitFind={this.tnInitFind}
|
||||||
|
searchResults={this.state.searchResults}
|
||||||
|
onSearchContacts={this.handleSearchContacts}
|
||||||
|
onCreateTopic={this.handleStartTopicRequest}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
<Alert
|
<Alert
|
||||||
visible={this.state.alertVisible}
|
visible={this.state.alertVisible}
|
||||||
title={this.state.alertParams.title}
|
title={this.state.alertParams.title}
|
||||||
@@ -1686,6 +1759,10 @@ class TinodeWeb extends React.Component {
|
|||||||
serverAddress={this.state.serverAddress}
|
serverAddress={this.state.serverAddress}
|
||||||
applicationVisible={this.state.applicationVisible}
|
applicationVisible={this.state.applicationVisible}
|
||||||
|
|
||||||
|
// Show the forwareded message if the user has selected a topic to forward this message to.
|
||||||
|
forwardedMessage={this.state.forwardMessage && this.stateTopicSelected != this.state.forwardMessage.head.fwdFrom ? this.state.forwardMessage : null}
|
||||||
|
onCancelForwardMessage={this.handleHideForwardMenu}
|
||||||
|
|
||||||
errorText={this.state.errorText}
|
errorText={this.state.errorText}
|
||||||
errorLevel={this.state.errorLevel}
|
errorLevel={this.state.errorLevel}
|
||||||
errorAction={this.state.errorAction}
|
errorAction={this.state.errorAction}
|
||||||
|
|||||||
@@ -76,8 +76,10 @@ class BaseChatMessage extends React.PureComponent {
|
|||||||
this.props.received > Tinode.MESSAGE_STATUS_FAILED &&
|
this.props.received > Tinode.MESSAGE_STATUS_FAILED &&
|
||||||
this.props.received < Tinode.MESSAGE_STATUS_DEL_RANGE) {
|
this.props.received < Tinode.MESSAGE_STATUS_DEL_RANGE) {
|
||||||
menuItems.push('menu_item_reply');
|
menuItems.push('menu_item_reply');
|
||||||
|
menuItems.push('menu_item_forward');
|
||||||
}
|
}
|
||||||
this.props.showContextMenu({ seq: this.props.seq, content: this.props.content,
|
this.props.showContextMenu({ seq: this.props.seq, content: this.props.content,
|
||||||
|
userFrom: this.props.userFrom, userName: this.props.userName,
|
||||||
y: e.pageY, x: e.pageX, pickReply: this.props.pickReply }, menuItems);
|
y: e.pageY, x: e.pageX, pickReply: this.props.pickReply }, menuItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ class BaseChatMessage extends React.PureComponent {
|
|||||||
(this.props.sequence + ' ' + (this.props.response ? 'left' : 'right'));
|
(this.props.sequence + ' ' + (this.props.response ? 'left' : 'right'));
|
||||||
const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';
|
const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';
|
||||||
const avatar = this.props.deleted ? null : (this.props.userAvatar || true);
|
const avatar = this.props.deleted ? null : (this.props.userAvatar || true);
|
||||||
const fullDisplay = (this.props.userFrom && this.props.response && !this.props.deleted &&
|
const fullDisplay = (this.props.isGroup && this.props.response && !this.props.deleted &&
|
||||||
(this.props.sequence == 'single' || this.props.sequence == 'last'));
|
(this.props.sequence == 'single' || this.props.sequence == 'last'));
|
||||||
|
|
||||||
let content = this.props.content;
|
let content = this.props.content;
|
||||||
@@ -143,7 +145,7 @@ class BaseChatMessage extends React.PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li ref={this.props.innerRef} className={sideClass}>
|
<li ref={this.props.innerRef} className={sideClass}>
|
||||||
{this.props.userFrom && this.props.response ?
|
{this.props.isGroup && this.props.response ?
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
{fullDisplay ?
|
{fullDisplay ?
|
||||||
<LetterTile
|
<LetterTile
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Reply',
|
defaultMessage: 'Reply',
|
||||||
description: 'Reply to message'
|
description: 'Reply to message'
|
||||||
},
|
},
|
||||||
|
forward: {
|
||||||
|
id: 'menu_item_forward',
|
||||||
|
defaultMessage: 'Forward',
|
||||||
|
description: 'Forward message'
|
||||||
|
},
|
||||||
topic_delete: {
|
topic_delete: {
|
||||||
id: 'menu_item_delete_topic',
|
id: 'menu_item_delete_topic',
|
||||||
defaultMessage: 'Delete',
|
defaultMessage: 'Delete',
|
||||||
@@ -183,6 +188,13 @@ class ContextMenu extends React.Component {
|
|||||||
return this.replyToMessage(params, errorHandler);
|
return this.replyToMessage(params, errorHandler);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'menu_item_forward': {
|
||||||
|
id: 'menu_item_forward',
|
||||||
|
title: formatMessage(messages.forward),
|
||||||
|
handler: (params, errorHandler) => {
|
||||||
|
return this.forwardMessage(params, errorHandler);
|
||||||
|
}
|
||||||
|
},
|
||||||
'topic_unmute': {
|
'topic_unmute': {
|
||||||
id: 'topic_unmute',
|
id: 'topic_unmute',
|
||||||
title: formatMessage(messages.unmute),
|
title: formatMessage(messages.unmute),
|
||||||
@@ -428,7 +440,13 @@ class ContextMenu extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
replyToMessage(params, errorHandler) {
|
replyToMessage(params, errorHandler) {
|
||||||
params.pickReply(params.seq, params.content, errorHandler);
|
params.pickReply(params.seq, params.content, params.userFrom, params.userName, errorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
forwardMessage(params, errorHandler) {
|
||||||
|
//params.pickReply({ seq: params.seq, content: params.content });
|
||||||
|
//this.props.
|
||||||
|
console.log('forwarding msg ', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// Forward Menu: message forwarding popup/dropdown menu.
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Tinode from 'tinode-sdk';
|
||||||
|
|
||||||
|
import ContactList from './contact-list.jsx';
|
||||||
|
import SearchContacts from './search-contacts.jsx';
|
||||||
|
|
||||||
|
import HashNavigation from '../lib/navigation.js';
|
||||||
|
import { REM_SIZE } from '../config.js';
|
||||||
|
|
||||||
|
export default class ForwardMenu extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.selfRef = React.createRef();
|
||||||
|
this.handlePageClick = this.handlePageClick.bind(this);
|
||||||
|
this.handleEscapeKey = this.handleEscapeKey.bind(this);
|
||||||
|
|
||||||
|
this.handleSearchContacts = this.handleSearchContacts.bind(this);
|
||||||
|
this.handleContactSelected = this.handleContactSelected.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.addEventListener('mousedown', this.handlePageClick, false);
|
||||||
|
document.addEventListener('keyup', this.handleEscapeKey, false);
|
||||||
|
|
||||||
|
this.props.onInitFind();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.removeEventListener('mousedown', this.handlePageClick, false);
|
||||||
|
document.removeEventListener('keyup', this.handleEscapeKey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePageClick(e) {
|
||||||
|
if (this.selfRef.current.contains(e.target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
// Not forwarding the message.
|
||||||
|
this.props.hide(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEscapeKey(e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
// Not forwarding the message.
|
||||||
|
this.props.hide(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSearchContacts(query) {
|
||||||
|
this.props.onSearchContacts(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleContactSelected(uid) {
|
||||||
|
this.props.onCreateTopic(uid);
|
||||||
|
this.props.hide(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// Ensure that menu is inside the app-container.
|
||||||
|
const hSize = 20 * REM_SIZE;
|
||||||
|
const vSize = REM_SIZE * (0.7 + this.props.contacts.length * 3);
|
||||||
|
const left = (this.props.bounds.right - this.props.clickAt.x < hSize) ?
|
||||||
|
(this.props.clickAt.x - this.props.bounds.left - hSize) :
|
||||||
|
(this.props.clickAt.x - this.props.bounds.left);
|
||||||
|
const top = (this.props.bounds.bottom - this.props.clickAt.y < vSize) ?
|
||||||
|
(this.props.clickAt.y - this.props.bounds.top - vSize) :
|
||||||
|
(this.props.clickAt.y - this.props.bounds.top);
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
left: left + 'px',
|
||||||
|
top: top + 'px',
|
||||||
|
maxWidth: hSize + 'px'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="forward-menu" style={style} ref={this.selfRef}>
|
||||||
|
<div className="flex-column">
|
||||||
|
<SearchContacts
|
||||||
|
onSearchContacts={this.handleSearchContacts} />
|
||||||
|
<ContactList
|
||||||
|
tinode={this.props.tinode}
|
||||||
|
contacts={this.props.searchResults}
|
||||||
|
myUserId={this.props.myUserId}
|
||||||
|
emptyListMessage={null}
|
||||||
|
showOnline={false}
|
||||||
|
showUnread={false}
|
||||||
|
showContextMenu={false}
|
||||||
|
onTopicSelected={this.handleContactSelected} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user