Code review suggestions.
This commit is contained in:
@@ -39,7 +39,7 @@ export function bytesToHumanSize(bytes) {
|
|||||||
|
|
||||||
// Get 32 bit integer hash value for a string. Ideally it should produce the same value
|
// Get 32 bit integer hash value for a string. Ideally it should produce the same value
|
||||||
// as Java's String#hash().
|
// as Java's String#hash().
|
||||||
export function stringToColorHash(value) {
|
function stringToColorHash(value) {
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
value = '' + value;
|
value = '' + value;
|
||||||
for (let i = 0; i < value.length; i++) {
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
|||||||
+72
-44
@@ -674,7 +674,7 @@ class MessagesView extends React.Component {
|
|||||||
sendMessage(msg, uploadCompletionPromise, uploader) {
|
sendMessage(msg, uploadCompletionPromise, uploader) {
|
||||||
let head;
|
let head;
|
||||||
if (this.state.reply && this.state.reply.content) {
|
if (this.state.reply && this.state.reply.content) {
|
||||||
head = {replyToSeq: 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);
|
||||||
@@ -879,31 +879,15 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
this.convertIntoThumbnails(ents, 0, (success) => {
|
|
||||||
if (success) {
|
|
||||||
const msg = Drafty.createQuote(header, cont, letterTileColorId(thisFrom));
|
|
||||||
this.setState({reply: {content: msg, seq: m.seq}});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn images in the provided Drafty.ent entries into thumbnails.
|
|
||||||
convertIntoThumbnails(ents, idx, done) {
|
|
||||||
if (idx >= ents.length) {
|
|
||||||
done(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Turn all images into thumbnails.
|
||||||
|
const promises = [];
|
||||||
|
ents.map((ex) => {
|
||||||
|
let p = new Promise((resolve, reject) => {
|
||||||
const scale = (origBlob) => {
|
const scale = (origBlob) => {
|
||||||
imageScaled(origBlob, IMAGE_THUMBNAIL_DIM, IMAGE_THUMBNAIL_DIM, -1, false,
|
imageScaled(origBlob, IMAGE_THUMBNAIL_DIM, IMAGE_THUMBNAIL_DIM, -1, false,
|
||||||
// Success
|
// Success
|
||||||
(mime, blob, width, height, fname) => {
|
(mime, blob, width, height, fname) => {
|
||||||
const ex = ents[idx];
|
|
||||||
|
|
||||||
ex.data.mime = mime;
|
ex.data.mime = mime;
|
||||||
ex.data.size = blob.size;
|
ex.data.size = blob.size;
|
||||||
ex.data.width = width;
|
ex.data.width = width;
|
||||||
@@ -913,30 +897,51 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
blobToBase64(blob, (blobMime, tinyBits64) => {
|
blobToBase64(blob, (blobMime, tinyBits64) => {
|
||||||
ex.data.val = tinyBits64;
|
ex.data.val = tinyBits64;
|
||||||
this.convertIntoThumbnails(ents, idx + 1, done);
|
resolve(true);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Failure
|
// Failure
|
||||||
(err) => {
|
(err) => {
|
||||||
done(false);
|
reject(`Could not scale image: ${err}`);
|
||||||
this.props.onError(err, 'err');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const ex = ents[idx];
|
|
||||||
if (ex.data.val) {
|
if (ex.data.val) {
|
||||||
const b = base64ToBlob(ex.data.val, ex.data.mime);
|
const b = base64ToBlob(ex.data.val, ex.data.mime);
|
||||||
if (b) {
|
if (b) {
|
||||||
scale.call(this, b);
|
scale(b);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
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 => {
|
||||||
.then((b) => {
|
if (e.ok) {
|
||||||
scale.call(this, b);
|
return e.blob();
|
||||||
});
|
} else {
|
||||||
|
reject(`Image fetch unsuccessful: ${e.status} - ${e.statusText}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((b) => scale(b))
|
||||||
|
.catch((err) => reject(`Error fetching image data: ${err}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
promises.push(p);
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(promises)
|
||||||
|
.then((_) => {
|
||||||
|
// All done. Create a reply quote.
|
||||||
|
const msg = Drafty.createQuote(header, cont, letterTileColorId(thisFrom));
|
||||||
|
this.setState({reply: {content: msg, seq: m.seq}});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.props.onError(err, 'err');
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelReply() {
|
handleCancelReply() {
|
||||||
@@ -945,13 +950,16 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
handleQuoteClick(replyToSeq) {
|
handleQuoteClick(replyToSeq) {
|
||||||
const ref = this.getOrCreateMessageRef(replyToSeq);
|
const ref = this.getOrCreateMessageRef(replyToSeq);
|
||||||
|
if (!ref) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const element = ref.current;
|
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 {
|
} else {
|
||||||
console.error("Unresolved ref: seqId", replyToSeq);
|
console.error("Unresolved message ref: seqId", replyToSeq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,7 +983,7 @@ class MessagesView extends React.Component {
|
|||||||
<ImagePreview
|
<ImagePreview
|
||||||
content={this.state.imagePreview}
|
content={this.state.imagePreview}
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
viewportWidth={this.props.viewportWidth}
|
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
|
||||||
replyTo={this.state.reply}
|
replyTo={this.state.reply}
|
||||||
formatter={draftyFormatter}
|
formatter={draftyFormatter}
|
||||||
onCancelReply={this.handleCancelReply}
|
onCancelReply={this.handleCancelReply}
|
||||||
@@ -995,7 +1003,7 @@ class MessagesView extends React.Component {
|
|||||||
<DocPreview
|
<DocPreview
|
||||||
content={this.state.docPreview}
|
content={this.state.docPreview}
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
viewportWidth={this.props.viewportWidth}
|
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
|
||||||
replyTo={this.state.reply}
|
replyTo={this.state.reply}
|
||||||
formatter={draftyFormatter}
|
formatter={draftyFormatter}
|
||||||
onCancelReply={this.handleCancelReply}
|
onCancelReply={this.handleCancelReply}
|
||||||
@@ -1055,6 +1063,10 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
// Ref for this chat message.
|
// Ref for this chat message.
|
||||||
const ref = this.getOrCreateMessageRef(msg.seq);
|
const ref = this.getOrCreateMessageRef(msg.seq);
|
||||||
|
let replyToSeq = msg.head ? parseInt(msg.head.reply) : null;
|
||||||
|
if (!replyToSeq || isNaN(replyToSeq)) {
|
||||||
|
replyToSeq = null;
|
||||||
|
}
|
||||||
|
|
||||||
messageNodes.push(
|
messageNodes.push(
|
||||||
<ChatMessage
|
<ChatMessage
|
||||||
@@ -1071,19 +1083,18 @@ class MessagesView extends React.Component {
|
|||||||
sequence={sequence}
|
sequence={sequence}
|
||||||
received={deliveryStatus}
|
received={deliveryStatus}
|
||||||
uploader={msg._uploader}
|
uploader={msg._uploader}
|
||||||
viewportWidth={this.props.viewportWidth}
|
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
|
||||||
showContextMenu={this.state.channel? false : this.handleShowContextMenuMessage}
|
showContextMenu={this.state.channel? false : this.handleShowContextMenuMessage}
|
||||||
onImagePreview={this.handleImagePostview}
|
onImagePreview={this.handleImagePostview}
|
||||||
onFormResponse={this.handleFormResponse}
|
onFormResponse={this.handleFormResponse}
|
||||||
onError={this.props.onError}
|
onError={this.props.onError}
|
||||||
onCancelUpload={this.handleCancelUpload}
|
onCancelUpload={this.handleCancelUpload}
|
||||||
|
|
||||||
pickReply={this.handlePickReply}
|
pickReply={this.handlePickReply}
|
||||||
replyToSeq={msg.head ? msg.head.replyToSeq : null}
|
replyToSeq={replyToSeq}
|
||||||
onQuoteClick={this.handleQuoteClick}
|
onQuoteClick={this.handleQuoteClick}
|
||||||
formatter={draftyFormatter}
|
formatter={draftyFormatter}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
userIsWriter={this.state.isWriter}
|
||||||
key={msg.seq} />
|
key={msg.seq} />
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1225,17 +1236,17 @@ function quotePreviewFmt(fmt, ent) {
|
|||||||
return [new_fmt, ent];
|
return [new_fmt, ent];
|
||||||
case 'BN':
|
case 'BN':
|
||||||
new_fmt.tp = null;
|
new_fmt.tp = null;
|
||||||
return [new_fmt, { tp: 'IC', data: { orig: 'BN', iconName: 'dashboard', iconTitle: 'drafty_form'} }];
|
return [new_fmt, { tp: 'IC', data: { orig: 'BN', iconName: 'button'}}];
|
||||||
case 'FM':
|
case 'FM':
|
||||||
new_fmt.tp = null;
|
new_fmt.tp = null;
|
||||||
new_fmt.len = 0;
|
new_fmt.len = 0;
|
||||||
return [new_fmt, {tp: 'IC', data: { orig: 'FM', iconName: 'dashboard', iconTitle: 'drafty_form'} }];
|
return [new_fmt, {tp: 'IC', data: { orig: 'FM', iconName: 'form'}}];
|
||||||
case 'RW':
|
case 'RW':
|
||||||
return [null, null];
|
return [null, null];
|
||||||
case 'EX':
|
case 'EX':
|
||||||
// Make it an icon.
|
// Make it an icon.
|
||||||
new_fmt.tp = null;
|
new_fmt.tp = null;
|
||||||
return [new_fmt, {tp: 'IC', data: { orig: 'EX', iconName: 'attachment', iconTitle: 'drafty_attachment'} }];
|
return [new_fmt, {tp: 'IC', data: { orig: 'EX', iconName: 'attachment'}}];
|
||||||
case 'QQ':
|
case 'QQ':
|
||||||
// Quote/citation.
|
// Quote/citation.
|
||||||
return [null,null];
|
return [null,null];
|
||||||
@@ -1327,14 +1338,31 @@ function draftyFormatter(style, data, values, key) {
|
|||||||
break;
|
break;
|
||||||
case 'IC':
|
case 'IC':
|
||||||
// Icon.
|
// Icon.
|
||||||
if (data.orig == 'BN') {
|
if (data.iconName == 'button') {
|
||||||
attr.className = 'flat-button faux';
|
attr.className = 'flat-button faux';
|
||||||
} else {
|
} else {
|
||||||
|
let iconName;
|
||||||
|
let iconTitle;
|
||||||
|
switch (data.iconName) {
|
||||||
|
case 'form':
|
||||||
|
iconName = 'dashboard';
|
||||||
|
iconTitle = 'drafty_form';
|
||||||
|
break;
|
||||||
|
case 'attachment':
|
||||||
|
iconName = 'attachment';
|
||||||
|
iconTitle = 'drafty_attachment';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
el = React.Fragment;
|
el = React.Fragment;
|
||||||
//values = [<i >]
|
if (iconName && iconTitle) {
|
||||||
const iconKey = data.orig.toLowerCase();
|
const iconKey = data.orig.toLowerCase();
|
||||||
values = [<i key={iconKey} className="material-icons">{data.iconName}</i>,
|
values = [<i key={iconKey} className="material-icons">{iconName}</i>,
|
||||||
formatMessage(messages[data.iconTitle])].concat(values || []);
|
formatMessage(messages[iconTitle])].concat(values || []);
|
||||||
|
} else {
|
||||||
|
values = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ class BaseChatMessage extends React.PureComponent {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const menuItems = this.props.received == Tinode.MESSAGE_STATUS_FAILED ? ['menu_item_send_retry'] : [];
|
const menuItems = this.props.received == Tinode.MESSAGE_STATUS_FAILED ? ['menu_item_send_retry'] : [];
|
||||||
if (this.props.received >= Tinode.MESSAGE_STATUS_FAILED &&
|
if (this.props.userIsWriter &&
|
||||||
|
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');
|
||||||
}
|
}
|
||||||
@@ -82,8 +83,10 @@ class BaseChatMessage extends React.PureComponent {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const replyToSeq = this.props.replyToSeq;
|
const replyToSeq = this.props.replyToSeq;
|
||||||
|
if (replyToSeq) {
|
||||||
this.props.onQuoteClick(replyToSeq);
|
this.props.onQuoteClick(replyToSeq);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const sideClass = this.props.deleted ? 'center' :
|
const sideClass = this.props.deleted ? 'center' :
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default class DocPreview extends React.PureComponent {
|
|||||||
<SendMessage
|
<SendMessage
|
||||||
noInput={true}
|
noInput={true}
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
viewportWidth={this.props.viewportWidth}
|
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
|
||||||
replyTo={this.props.replyTo}
|
replyTo={this.props.replyTo}
|
||||||
formatter={this.props.formatter}
|
formatter={this.props.formatter}
|
||||||
onCancelReply={this.props.onCancelReply}
|
onCancelReply={this.props.onCancelReply}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default class ImagePreview extends React.PureComponent {
|
|||||||
messagePrompt="add_image_caption"
|
messagePrompt="add_image_caption"
|
||||||
acceptBlank={true}
|
acceptBlank={true}
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
viewportWidth={this.props.viewportWidth}
|
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
|
||||||
replyTo={this.props.replyTo}
|
replyTo={this.props.replyTo}
|
||||||
formatter={this.props.formatter}
|
formatter={this.props.formatter}
|
||||||
onCancelReply={this.props.onCancelReply}
|
onCancelReply={this.props.onCancelReply}
|
||||||
|
|||||||
Reference in New Issue
Block a user