Basic message forwarding.

This commit is contained in:
aforge
2021-09-21 17:39:24 +03:00
parent 03f076a1bc
commit 3a7a806420
6 changed files with 246 additions and 40 deletions
+34 -35
View File
@@ -214,6 +214,17 @@ class MessagesView extends React.Component {
};
} else if (nextProps.topic != prevState.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 = {
topic: nextProps.topic,
docPreview: null,
@@ -222,7 +233,7 @@ class MessagesView extends React.Component {
typingIndicator: false,
scrollPosition: 0,
fetchingMessages: false,
reply: null
reply: reply
};
if (topic) {
@@ -665,14 +676,20 @@ class MessagesView extends React.Component {
// sendMessage sends the message with an optional subscription to topic first.
sendMessage(msg, uploadCompletionPromise, uploader) {
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};
// Turn it into Drafty so we can make a quoted Drafty object later.
if (typeof msg == 'string') {
msg = Drafty.parse(msg);
}
msg = Drafty.append(this.state.reply.content, msg);
this.setState({reply: null});
this.handleCancelReply();
}
this.props.sendMessage(msg, uploadCompletionPromise, uploader, head);
}
@@ -825,7 +842,7 @@ class MessagesView extends React.Component {
uploader.cancel();
}
handlePickReply(seq, content) {
handlePickReply(seq, content, senderId, senderName) {
this.setState({reply: null});
if (!seq || !content) {
@@ -843,23 +860,6 @@ class MessagesView extends React.Component {
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({
reply: {
content: Drafty.quote(senderName, senderId, content),
@@ -869,7 +869,8 @@ class MessagesView extends React.Component {
}
handleCancelReply() {
this.setState({reply: null})
this.setState({reply: null});
this.props.onCancelForwardMessage();
}
handleQuoteClick(replyToSeq) {
@@ -964,18 +965,14 @@ class MessagesView extends React.Component {
const isReply = !(thisFrom == this.props.myUserId);
const deliveryStatus = topic.msgStatus(msg, true);
let userName, userAvatar, userFrom;
if (groupTopic) {
const user = topic.userDesc(thisFrom);
if (user && user.public) {
userName = user.public.fn;
userAvatar = makeImageUrl(user.public.photo);
}
userFrom = thisFrom;
chatBoxClass='chat-box group';
} else {
chatBoxClass='chat-box';
let userFrom = thisFrom, userName, userAvatar;
const user = topic.userDesc(thisFrom);
if (user && user.public) {
userName = user.public.fn;
userAvatar = makeImageUrl(user.public.photo);
}
//userFrom = thisFrom;
chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box';
// Ref for this chat message.
const ref = this.getOrCreateMessageRef(msg.seq);
@@ -993,6 +990,7 @@ class MessagesView extends React.Component {
timestamp={msg.ts}
response={isReply}
seq={msg.seq}
isGroup={groupTopic}
userFrom={userFrom}
userName={userName}
userAvatar={userAvatar}
@@ -1106,11 +1104,12 @@ class MessagesView extends React.Component {
:
<SendMessage
tinode={this.props.tinode}
noInput={this.props.forwardedMessage != null}
disabled={!this.state.isWriter}
onKeyPress={this.sendKeyPress}
onSendMessage={this.sendMessage}
onAttachFile={this.handleAttachFile}
onAttachImage={this.handleAttachImage}
onAttachFile={this.props.forwardedMessage == null ? this.handleAttachFile : null}
onAttachImage={this.props.forwardedMessage == null ? this.handleAttachImage : null}
onError={this.props.onError}
replyTo={this.state.reply}
onQuoteClick={this.handleQuoteClick}
+79 -2
View File
@@ -9,6 +9,7 @@ import Tinode from 'tinode-sdk';
import Alert from '../widgets/alert.jsx';
import ContextMenu from '../widgets/context-menu.jsx';
import ForwardMenu from '../widgets/forward-menu.jsx';
import InfoView from './info-view.jsx';
import MessagesView from './messages-view.jsx';
@@ -140,6 +141,11 @@ class TinodeWeb extends React.Component {
this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);
this.handleResetPassword = this.handleResetPassword.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() {
@@ -197,6 +203,11 @@ class TinodeWeb extends React.Component {
contextMenuParams: null,
contextMenuItems: [],
forwardMenuVisible: false,
forwardMenuBounds: null,
forwardMenuClickAt: null,
forwardMessage: null,
// Modal alert dialog.
alertVisible: false,
alertParams: {},
@@ -552,6 +563,7 @@ class TinodeWeb extends React.Component {
errorLevel: err && err.message ? 'err' : 'warn',
loginDisabled: false,
contextMenuVisible: false,
forwardMenuVisible: false,
serverVersion: "no connection"
});
}
@@ -872,7 +884,8 @@ class TinodeWeb extends React.Component {
if (this.state.topicSelected != topicName) {
this.setState({
topicSelectedOnline: this.tinode.isTopicOnline(topicName),
topicSelectedAcs: this.tinode.getTopicAccessMode(topicName)
topicSelectedAcs: this.tinode.getTopicAccessMode(topicName),
forwardMessage: null
});
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', topicName));
}
@@ -884,7 +897,8 @@ class TinodeWeb extends React.Component {
mobilePanel: 'sidepanel',
topicSelectedOnline: false,
topicSelectedAcs: null,
infoPanel: undefined
infoPanel: undefined,
forwardMessage: null
});
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));
@@ -907,7 +921,10 @@ class TinodeWeb extends React.Component {
// - head - head dictionary to be attached to the message
handleSendMessage(msg, promise, uploader, head) {
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);
// The uploader is used to show progress.
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) {
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) {
if (action == 'topic_archive') {
if (promise && params.topicName && params.topicName == this.state.topicSelected) {
@@ -1475,6 +1527,8 @@ class TinodeWeb extends React.Component {
this.handleTopicSelected(null);
});
}
} else if (action == 'menu_item_forward') {
this.handleShowForwardMenu(params);
}
}
@@ -1590,6 +1644,25 @@ class TinodeWeb extends React.Component {
:
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
visible={this.state.alertVisible}
title={this.state.alertParams.title}
@@ -1686,6 +1759,10 @@ class TinodeWeb extends React.Component {
serverAddress={this.state.serverAddress}
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}
errorLevel={this.state.errorLevel}
errorAction={this.state.errorAction}