Make handleQuoteClick work.

This commit is contained in:
aforge
2021-08-30 23:35:13 +03:00
parent 331c17a7cc
commit 2edfaeecde
7 changed files with 42 additions and 23 deletions
+6 -1
View File
@@ -49,11 +49,16 @@ export function stringToColorHash(value) {
return hash; return hash;
} }
// User avatar letter color id.
export function letterTileColorId(userId) {
return Math.abs(stringToColorHash(userId)) % 16;
}
// Converts user or topic ID to a CSS color class. Ideally it should produce the same color value as Java version. // Converts user or topic ID to a CSS color class. Ideally it should produce the same color value as Java version.
// params: // params:
// - {string} id: user or topic ID // - {string} id: user or topic ID
// - {boolean} light: light or dark version of the color. // - {boolean} light: light or dark version of the color.
// - {boolean} fg: foreground or background color. // - {boolean} fg: foreground or background color.
export function idToColorClass(id, light, fg) { export function idToColorClass(id, light, fg) {
return (light ? 'lt-' : 'dk-') + (fg ? 'fg-' : 'bg-') + (Math.abs(stringToColorHash(id)) % 16); return (light ? 'lt-' : 'dk-') + (fg ? 'fg-' : 'bg-') + letterTileColorId(id);
} }
-7
View File
@@ -2,13 +2,6 @@
import Tinode from 'tinode-sdk'; import Tinode from 'tinode-sdk';
import { stringToColorHash } from './strformat.js';
// User avatar letter color id.
export function letterTileColor(userId) {
return Math.abs(stringToColorHash(userId)) % 16;
}
// Make shortcut icon appear with a green dot + show unread count in title. // Make shortcut icon appear with a green dot + show unread count in title.
export function updateFavicon(count) { export function updateFavicon(count) {
const oldIcon = document.getElementById('shortcut-icon'); const oldIcon = document.getElementById('shortcut-icon');
+27 -8
View File
@@ -22,8 +22,8 @@ import { DEFAULT_P2P_ACCESS_MODE, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY, MESSAGES_PA
import { SUPPORTED_IMAGE_FORMATS, blobToBase64, base64ToBlob, filePasted, fileToBase64, import { SUPPORTED_IMAGE_FORMATS, blobToBase64, base64ToBlob, filePasted, fileToBase64,
imageScaled, makeImageUrl, fitImageSize } from '../lib/blob-helpers.js'; imageScaled, makeImageUrl, fitImageSize } from '../lib/blob-helpers.js';
import HashNavigation from '../lib/navigation.js'; import HashNavigation from '../lib/navigation.js';
import { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js'; import { bytesToHumanSize, shortDateFormat, letterTileColorId } from '../lib/strformat.js';
import { sanitizeImageUrl, letterTileColor } from '../lib/utils.js'; import { sanitizeImageUrl } from '../lib/utils.js';
// Run timer with this frequency (ms) for checking notification queue. // Run timer with this frequency (ms) for checking notification queue.
const NOTIFICATION_EXEC_INTERVAL = 300; const NOTIFICATION_EXEC_INTERVAL = 300;
@@ -121,10 +121,22 @@ class MessagesView extends React.Component {
this.handleQuoteClick = this.handleQuoteClick.bind(this); this.handleQuoteClick = this.handleQuoteClick.bind(this);
this.convertIntoThumbnails = this.convertIntoThumbnails.bind(this); this.convertIntoThumbnails = this.convertIntoThumbnails.bind(this);
this.chatMessageRefs = {};
this.getOrCreateMessageRef = this.getOrCreateMessageRef.bind(this);
this.readNotificationQueue = []; this.readNotificationQueue = [];
this.readNotificationTimer = null; this.readNotificationTimer = null;
} }
getOrCreateMessageRef(seqId) {
if (this.chatMessageRefs.hasOwnProperty(seqId)) {
return this.chatMessageRefs[seqId];
}
const ref = React.createRef();
this.chatMessageRefs[seqId] = ref;
return ref;
}
componentDidMount() { componentDidMount() {
// this.propsChange(this.props, this.state); // this.propsChange(this.props, this.state);
if (this.messagesScroller) { if (this.messagesScroller) {
@@ -868,7 +880,7 @@ class MessagesView extends React.Component {
} }
this.convertIntoThumbnails(ents, 0, (success) => { this.convertIntoThumbnails(ents, 0, (success) => {
if (success) { if (success) {
const msg = Drafty.createQuote(header, cont, letterTileColor(thisFrom)); const msg = Drafty.createQuote(header, cont, letterTileColorId(thisFrom));
this.setState({reply: {content: msg, seq: m.seq}}); this.setState({reply: {content: msg, seq: m.seq}});
} }
}); });
@@ -916,12 +928,11 @@ class MessagesView extends React.Component {
scale.call(this, b); scale.call(this, b);
} }
} else { } else {
//let saveThis = this;
const from = this.props.tinode.authorizeURL(sanitizeImageUrl(ex.data.ref)); const from = this.props.tinode.authorizeURL(sanitizeImageUrl(ex.data.ref));
fetch(from) fetch(from)
.then(e => e.blob()) .then(e => e.blob())
.then((b) => { .then((b) => {
scale.call(saveThis, b); scale.call(this, b);
}); });
return; return;
} }
@@ -932,11 +943,14 @@ class MessagesView extends React.Component {
} }
handleQuoteClick(replyToSeq) { handleQuoteClick(replyToSeq) {
const element = document.getElementById("msg-" + replyToSeq); const ref = this.getOrCreateMessageRef(replyToSeq);
const element = ref.current;
if (element) { if (element) {
element.scrollIntoView({block: "center", behavior: "smooth"}); element.scrollIntoView({block: "center", behavior: "smooth"});
element.style.backgroundColor = 'rgb(0, 0, 0, 0.4)'; element.style.backgroundColor = 'rgb(0, 0, 0, 0.4)';
setTimeout(() => { element.style.backgroundColor = ''; } , 1000); setTimeout(() => { element.style.backgroundColor = ''; } , 1000);
} else {
console.error("Unresolved ref: seqId", replyToSeq);
} }
} }
@@ -1036,6 +1050,9 @@ class MessagesView extends React.Component {
chatBoxClass='chat-box'; chatBoxClass='chat-box';
} }
// Ref for this chat message.
const ref = this.getOrCreateMessageRef(msg.seq);
messageNodes.push( messageNodes.push(
<ChatMessage <ChatMessage
tinode={this.props.tinode} tinode={this.props.tinode}
@@ -1061,7 +1078,8 @@ class MessagesView extends React.Component {
pickReply={this.handlePickReply} pickReply={this.handlePickReply}
replyToSeq={msg.head ? msg.head.replyToSeq : null} replyToSeq={msg.head ? msg.head.replyToSeq : null}
onQuoteClick={this.handleQuoteClick} onQuoteClick={this.handleQuoteClick}
onFormatQuote={draftyFormatter} formatter={draftyFormatter}
ref={ref}
key={msg.seq} /> key={msg.seq} />
); );
@@ -1167,7 +1185,7 @@ class MessagesView extends React.Component {
onError={this.props.onError} onError={this.props.onError}
replyTo={this.state.reply} replyTo={this.state.reply}
onQuoteClick={this.handleQuoteClick} onQuoteClick={this.handleQuoteClick}
onFormatQuote={draftyFormatter} formatter={draftyFormatter}
onCancelReply={this.handleCancelReply} />} onCancelReply={this.handleCancelReply} />}
</> </>
); );
@@ -1179,6 +1197,7 @@ class MessagesView extends React.Component {
} }
}; };
// Transforms styles (and corresponding entities) for formatting reply quotes.
function quotePreviewFmt(fmt, ent) { function quotePreviewFmt(fmt, ent) {
let tp = fmt.tp; let tp = fmt.tp;
if (!tp) { if (!tp) {
+6 -4
View File
@@ -10,7 +10,7 @@ import UploadingImage from './uploading-image.jsx'
import { sanitizeImageUrl, sanitizeUrl } from '../lib/utils.js'; import { sanitizeImageUrl, sanitizeUrl } from '../lib/utils.js';
class ChatMessage extends React.PureComponent { class BaseChatMessage extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -94,7 +94,6 @@ class ChatMessage extends React.PureComponent {
const fullDisplay = (this.props.userFrom && this.props.response && !this.props.deleted && const fullDisplay = (this.props.userFrom && this.props.response && !this.props.deleted &&
(this.props.sequence == 'single' || this.props.sequence == 'last')); (this.props.sequence == 'single' || this.props.sequence == 'last'));
const msgId = 'msg-' + this.props.seq;
let content = this.props.content; let content = this.props.content;
const attachments = []; const attachments = [];
if (this.props.mimeType == Drafty.getContentType() && Drafty.isValid(content)) { if (this.props.mimeType == Drafty.getContentType() && Drafty.isValid(content)) {
@@ -133,7 +132,7 @@ class ChatMessage extends React.PureComponent {
} }
return ( return (
<li id={msgId} className={sideClass}> <li ref={this.props.innerRef} className={sideClass}>
{this.props.userFrom && this.props.response ? {this.props.userFrom && this.props.response ?
<div className="avatar-box"> <div className="avatar-box">
{fullDisplay ? {fullDisplay ?
@@ -182,4 +181,7 @@ class ChatMessage extends React.PureComponent {
} }
}; };
export default injectIntl(ChatMessage); const IntlChatMessage = injectIntl(BaseChatMessage);
const ChatMessage = React.forwardRef((props, ref) => <IntlChatMessage innerRef = {ref} {...props} />);
export default ChatMessage;
+1 -1
View File
@@ -47,7 +47,7 @@ export default class DocPreview extends React.PureComponent {
noInput={true} noInput={true}
tinode={this.props.tinode} tinode={this.props.tinode}
replyTo={this.props.replyTo} replyTo={this.props.replyTo}
formatter={this.props.onFormatQuote} formatter={this.props.formatter}
onCancelReply={this.props.onCancelReply} onCancelReply={this.props.onCancelReply}
onSendMessage={this.handleSendDoc} onSendMessage={this.handleSendDoc}
onError={this.props.onError} /> onError={this.props.onError} />
+1 -1
View File
@@ -75,7 +75,7 @@ export default class ImagePreview extends React.PureComponent {
acceptBlank={true} acceptBlank={true}
tinode={this.props.tinode} tinode={this.props.tinode}
replyTo={this.props.replyTo} replyTo={this.props.replyTo}
formatter={this.props.onFormatQuote} formatter={this.props.formatter}
onCancelReply={this.props.onCancelReply} onCancelReply={this.props.onCancelReply}
onSendMessage={this.handleSendImage} onSendMessage={this.handleSendImage}
onError={this.props.onError} /> onError={this.props.onError} />
+1 -1
View File
@@ -162,7 +162,7 @@ class SendMessage extends React.PureComponent {
let quote = null; let quote = null;
if (this.props.replyTo) { if (this.props.replyTo) {
quote = Drafty.format(this.props.replyTo.content, this.props.onFormatQuote, this); quote = Drafty.format(this.props.replyTo.content, this.props.formatter, this);
} }
return ( return (