Merge pull request #88 from tinode/features

Message forwarding
This commit is contained in:
aforge
2021-09-28 15:43:15 +03:00
committed by GitHub
13 changed files with 372 additions and 273 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ const messages = defineMessages({
id: 'drafty_image', id: 'drafty_image',
defaultMessage: 'Picture', defaultMessage: 'Picture',
description: 'Comment for embedded images in drafty preview' description: 'Comment for embedded images in drafty preview'
}, }
}); });
// Size the already scaled image. // Size the already scaled image.
+8 -6
View File
@@ -221,6 +221,7 @@ class MessagesView extends React.Component {
const preview = nextProps.forwardedMessage.preview; const preview = nextProps.forwardedMessage.preview;
reply = { reply = {
content: preview, content: preview,
forwarded: nextProps.forwardedMessage.head.forwarded,
seq: null seq: null
}; };
} }
@@ -842,18 +843,19 @@ class MessagesView extends React.Component {
uploader.cancel(); uploader.cancel();
} }
handlePickReply(seq, content, senderId, senderName) { handlePickReply(seq, content, forwarded, senderId, senderName) {
this.setState({reply: null}); this.setState({reply: null});
if (!seq || !content) { if (!seq || !content) {
return; return;
} }
if (typeof content == 'string') { content = forwarded ?
content = Drafty.init(content); Drafty.forwardedContent(content) :
} typeof content == 'string' ?
Drafty.init(content) : content;
if (Drafty.isValid(content)) { if (Drafty.isValid(content)) {
content = Drafty.preview(content, QUOTED_REPLY_LENGTH); content = Drafty.preview(content, QUOTED_REPLY_LENGTH, undefined, forwarded != null);
} else { } else {
// /!\ invalid content. // /!\ invalid content.
content = Drafty.append(Drafty.init('\u26A0 '), content = Drafty.append(Drafty.init('\u26A0 '),
@@ -971,7 +973,6 @@ class MessagesView extends React.Component {
userName = user.public.fn; userName = user.public.fn;
userAvatar = makeImageUrl(user.public.photo); userAvatar = makeImageUrl(user.public.photo);
} }
//userFrom = thisFrom;
chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box'; chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box';
// Ref for this chat message. // Ref for this chat message.
@@ -985,6 +986,7 @@ class MessagesView extends React.Component {
<ChatMessage <ChatMessage
tinode={this.props.tinode} tinode={this.props.tinode}
content={msg.content} content={msg.content}
forwarded={msg.head ? msg.head.forwarded : null}
deleted={msg.hi} deleted={msg.hi}
mimeType={msg.head ? msg.head.mime : null} mimeType={msg.head ? msg.head.mime : null}
timestamp={msg.ts} timestamp={msg.ts}
+10 -8
View File
@@ -16,7 +16,7 @@ import MessagesView from './messages-view.jsx';
import SidepanelView from './sidepanel-view.jsx'; import SidepanelView from './sidepanel-view.jsx';
import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, LOGGING_ENABLED, import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, LOGGING_ENABLED,
MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js'; MEDIA_BREAKPOINT, QUOTED_REPLY_LENGTH, RECEIVED_DELAY } from '../config.js';
import { PACKAGE_VERSION } from '../version.js'; import { PACKAGE_VERSION } from '../version.js';
import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js'; import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';
import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js'; import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';
@@ -1450,18 +1450,20 @@ class TinodeWeb extends React.Component {
// If the Find panel is active, close it. // If the Find panel is active, close it.
this.handleSidepanelCancel(); this.handleSidepanelCancel();
} }
const header = "Forwarded from " + params.userName; const header = '➦ ' + params.userName;
const content = typeof params.content == 'string' ? Tinode.Drafty.init(params.content) : params.content; const content = params.forwarded ?
const preview = Tinode.Drafty.preview(content, 30); Tinode.Drafty.forwardedContent(params.content) :
typeof params.content == 'string' ?
Tinode.Drafty.init(params.content) : params.content;
const preview = Tinode.Drafty.preview(content, QUOTED_REPLY_LENGTH,
undefined, params.forwarded != null);
const msg = Tinode.Drafty.append( const msg = Tinode.Drafty.append(
Tinode.Drafty.appendLineBreak( Tinode.Drafty.appendLineBreak(
Tinode.Drafty.mention(header, params.userFrom)), Tinode.Drafty.mention(header, params.userFrom)),
content); content);
const msgPreview = Tinode.Drafty.quote(header, params.userFrom, preview); const msgPreview = Tinode.Drafty.quote(header, params.userFrom, preview);
const head = { const head = {
// TODO: decide what parameters to store. forwarded: params.topicName + ':' + params.seq
fwdFrom: params.topicName,
authorUid: params.userFrom
}; };
this.setState({ this.setState({
forwardMenuVisible: true, forwardMenuVisible: true,
@@ -1760,7 +1762,7 @@ class TinodeWeb extends React.Component {
applicationVisible={this.state.applicationVisible} applicationVisible={this.state.applicationVisible}
// Show the forwareded message if the user has selected a topic to forward this message to. // 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} forwardedMessage={this.state.forwardMessage && !this.state.forwardMessage.head.forwarded.startsWith(this.stateTopicSelected) ? this.state.forwardMessage : null}
onCancelForwardMessage={this.handleHideForwardMenu} onCancelForwardMessage={this.handleHideForwardMenu}
errorText={this.state.errorText} errorText={this.state.errorText}
+1
View File
@@ -83,6 +83,7 @@ class BaseChatMessage extends React.PureComponent {
content: this.props.content, content: this.props.content,
userFrom: this.props.userFrom, userFrom: this.props.userFrom,
userName: this.props.userName, userName: this.props.userName,
forwarded: this.props.forwarded,
y: e.pageY, y: e.pageY,
x: e.pageX, x: e.pageX,
pickReply: this.props.pickReply pickReply: this.props.pickReply
+4 -1
View File
@@ -77,14 +77,16 @@ class ContactList extends React.Component {
const comment = Array.isArray(c.private) ? const comment = Array.isArray(c.private) ?
c.private.join(',') : (c.private ? c.private.comment : null); c.private.join(',') : (c.private ? c.private.comment : null);
let preview; let preview;
let forwarded;
let deliveryStatus; let deliveryStatus;
if (!this.props.showMode && c.latestMessage) { if (!this.props.showMode && c.latestMessage) {
const msg = c.latestMessage(true); const msg = c.latestMessage(true);
if (msg) { if (msg) {
forwarded = msg.head ? msg.head.forwarded : null;
deliveryStatus = msg._status || c.msgStatus(msg, true); deliveryStatus = msg._status || c.msgStatus(msg, true);
preview = typeof msg.content == 'string' ? preview = typeof msg.content == 'string' ?
msg.content.substr(0, MESSAGE_PREVIEW_LENGTH) : msg.content.substr(0, MESSAGE_PREVIEW_LENGTH) :
Drafty.preview(msg.content, MESSAGE_PREVIEW_LENGTH); Drafty.preview(msg.content, MESSAGE_PREVIEW_LENGTH, undefined, forwarded != null);
} }
} }
contactNodes.push( contactNodes.push(
@@ -94,6 +96,7 @@ class ContactList extends React.Component {
avatar={makeImageUrl(c.public ? c.public.photo : null)} avatar={makeImageUrl(c.public ? c.public.photo : null)}
comment={comment} comment={comment}
preview={preview} preview={preview}
forwarded={forwarded}
received={deliveryStatus} received={deliveryStatus}
unread={this.props.showUnread ? c.unread : 0} unread={this.props.showUnread ? c.unread : 0}
now={c.online && this.props.connected} now={c.online && this.props.connected}
+1 -1
View File
@@ -440,7 +440,7 @@ class ContextMenu extends React.Component {
} }
replyToMessage(params, errorHandler) { replyToMessage(params, errorHandler) {
params.pickReply(params.seq, params.content, params.userFrom, params.userName, errorHandler); params.pickReply(params.seq, params.content, params.forwarded, params.userFrom, params.userName, errorHandler);
} }
forwardMessage(params, errorHandler) { forwardMessage(params, errorHandler) {
+13 -4
View File
@@ -13,6 +13,10 @@ export default class ForwardMenu extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
query: null
};
this.selfRef = React.createRef(); this.selfRef = React.createRef();
this.handlePageClick = this.handlePageClick.bind(this); this.handlePageClick = this.handlePageClick.bind(this);
this.handleEscapeKey = this.handleEscapeKey.bind(this); this.handleEscapeKey = this.handleEscapeKey.bind(this);
@@ -51,6 +55,7 @@ export default class ForwardMenu extends React.Component {
} }
handleSearchContacts(query) { handleSearchContacts(query) {
this.setState({ query: Tinode.isNullValue(query) ? null : query });
this.props.onSearchContacts(query); this.props.onSearchContacts(query);
} }
@@ -60,20 +65,24 @@ export default class ForwardMenu extends React.Component {
} }
render() { render() {
let contacts = this.state.query != null ? this.props.searchResults : this.props.contacts;
// Filter out contacts without a 'W' permission.
contacts = contacts.filter((c) => { return c.acs.isJoiner() && c.acs.isWriter(); });
// Ensure that menu is inside the app-container. // Ensure that menu is inside the app-container.
const hSize = 20 * REM_SIZE; const hSize = 20 * REM_SIZE;
const vSize = REM_SIZE * (0.7 + this.props.contacts.length * 3); const vSize = REM_SIZE * (0.7 + contacts.length * 3);
const left = (this.props.bounds.right - this.props.clickAt.x < hSize) ? 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 - hSize) :
(this.props.clickAt.x - this.props.bounds.left); (this.props.clickAt.x - this.props.bounds.left);
const top = (this.props.bounds.bottom - this.props.clickAt.y < vSize) ? const top = (this.props.bounds.bottom - this.props.clickAt.y < vSize) ?
(this.props.clickAt.y - this.props.bounds.top - vSize) : Math.max(this.props.clickAt.y - this.props.bounds.top - vSize, this.props.bounds.top + 70) :
(this.props.clickAt.y - this.props.bounds.top); (this.props.clickAt.y - this.props.bounds.top);
const style = { const style = {
left: left + 'px', left: left + 'px',
top: top + 'px', top: top + 'px',
maxWidth: hSize + 'px' maxWidth: hSize + 'px',
height: '100%'
}; };
return ( return (
@@ -83,7 +92,7 @@ export default class ForwardMenu extends React.Component {
onSearchContacts={this.handleSearchContacts} /> onSearchContacts={this.handleSearchContacts} />
<ContactList <ContactList
tinode={this.props.tinode} tinode={this.props.tinode}
contacts={this.props.searchResults} contacts={contacts}
myUserId={this.props.myUserId} myUserId={this.props.myUserId}
emptyListMessage={null} emptyListMessage={null}
showOnline={false} showOnline={false}
+214 -200
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+115 -47
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long