@@ -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.
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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,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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+115
-47
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user