TheCard handling in messages

This commit is contained in:
or-else
2026-01-16 20:51:57 +03:00
parent 16489432cf
commit 930f4f0656
42 changed files with 1304 additions and 126 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "Unknown",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": false,
"obsolete": false
},
"save_action": {
"translation": "Save",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": false,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "Cannot parse vCard file.",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": false,
"obsolete": false
},
"chat_now": {
"translation": "Chat",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": false,
"obsolete": false
},
"find_user": {
"translation": "Find",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": false,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+20
View File
@@ -175,6 +175,10 @@
"defaultMessage": "Cannot initiate file upload.",
"description": "Generic error messagewhen attachment fails"
},
"cannot_parse_vcard": {
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed"
},
"change_email": {
"defaultMessage": "Change email",
"description": "Prompt to change current email"
@@ -207,6 +211,10 @@
"defaultMessage": "Ignore",
"description": "Action [Ignore] for chat invitation."
},
"chat_now": {
"defaultMessage": "Chat",
"description": "Label for message button in contact card"
},
"clear_messages_warning": {
"defaultMessage": "Are you sure you want to clear all messages? It cannot be undone.",
"description": "Alert dialog warning when deleting all messages."
@@ -327,6 +335,10 @@
"defaultMessage": "The file size {size} exceeds the {limit} limit.",
"description": "Error message when attachment is too large"
},
"find_user": {
"defaultMessage": "Find",
"description": "Label for search button in contact card"
},
"forgot_password_link": {
"defaultMessage": "Forgot password?",
"description": "Link to Reset password form"
@@ -880,6 +892,10 @@
"defaultMessage": "Requested",
"description": "Title for permissions"
},
"save_action": {
"defaultMessage": "Save",
"description": "Label for Save button"
},
"save_attachment": {
"defaultMessage": "save",
"description": "Call to save an attachment"
@@ -1056,6 +1072,10 @@
"defaultMessage": "Freeform name of the group",
"description": "Prompt for entering topic name"
},
"unknown_name": {
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing"
},
"unnamed_topic": {
"defaultMessage": "Unnamed",
"description": "Title shown when the topic has no name"
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+35
View File
@@ -1906,5 +1906,40 @@
"description": "Label [Scan my ID:]",
"missing": false,
"obsolete": false
},
"unknown_name": {
"translation": "",
"defaultMessage": "Unknown",
"description": "Name to show when the name is missing",
"missing": true,
"obsolete": false
},
"save_action": {
"translation": "",
"defaultMessage": "Save",
"description": "Label for Save button",
"missing": true,
"obsolete": false
},
"cannot_parse_vcard": {
"translation": "",
"defaultMessage": "Cannot parse vCard file.",
"description": "Error message when vCard file cannot be parsed",
"missing": true,
"obsolete": false
},
"chat_now": {
"translation": "",
"defaultMessage": "Chat",
"description": "Label for message button in contact card",
"missing": true,
"obsolete": false
},
"find_user": {
"translation": "",
"defaultMessage": "Find",
"description": "Label for search button in contact card",
"missing": true,
"obsolete": false
}
}
+24 -11
View File
@@ -1,13 +1,14 @@
import React from 'react';
import { defineMessages } from 'react-intl';
import { Drafty } from 'tinode-sdk';
import { Drafty, TheCard } from 'tinode-sdk';
import AudioPlayer from '../widgets/audio-player.jsx'
import CallMessage from '../widgets/call-message.jsx'
import CallStatus from '../widgets/call-status.jsx';
import InlineVideo from '../widgets/inline-video.jsx';
import LazyImage from '../widgets/lazy-image.jsx'
import LazyImage from '../widgets/lazy-image.jsx';
import TheCardViewer from '../widgets/the-card-viewer.jsx';
import UploadingImage from '../widgets/uploading-image.jsx'
import { BROKEN_IMAGE_SIZE, CLICKABLE_URL_SCHEMES, IMAGE_THUMBNAIL_DIM, NO_DIMENSIONS_VIDEO,
@@ -48,10 +49,7 @@ const messages = defineMessages({
// 'this' must contain:
// viewportWidth:
// authorizeURL:
// onImagePreview:
// onVideoPreview:
// onFormButtonClick:
// onQuoteClick:
// onHandleClick:
export function fullFormatter(style, data, values, key, stack) {
if (stack.includes('QQ')) {
return quoteFormatter.call(this, style, data, values, key);
@@ -100,7 +98,7 @@ export function fullFormatter(style, data, values, key, stack) {
break;
case 'BN':
// Button
attr.onClick = this.onFormButtonClick;
attr.onClick = (e) => this.onHandleClick(e, 'form_button');
let inner = React.Children.map(values, (child) => {
return typeof child == 'string' ? child : undefined;
});
@@ -133,7 +131,16 @@ export function fullFormatter(style, data, values, key, stack) {
case 'QQ':
// Quote/citation.
attr.className = 'reply-quote'
attr.onClick = this.onQuoteClick;
attr.onClick = (e) => this.onHandleClick(e, 'quote');
break;
case 'TC':
// TheCard
el = TheCardViewer;
attr.content = data;
attr.authorizeURL = this.authorizeURL;
attr.onChatClick = (e) => this.onHandleClick(e, 'contact_chat');
attr.onFindClick = (e) => this.onHandleClick(e, 'contact_find');
values = null;
break;
case 'VC':
el = CallMessage;
@@ -200,7 +207,7 @@ function handleImageData(el, data, attr) {
if (attr.src) {
if (Math.max(data.width || 0, data.height || 0) > IMAGE_THUMBNAIL_DIM) {
// Allow previews for large enough images.
attr.onClick = this.onImagePreview;
attr.onClick = (e) => this.onHandleClick(e, 'image');
attr.className += ' image-clickable';
}
attr.loading = 'lazy';
@@ -241,7 +248,7 @@ function handleVideoData(el, data, attr) {
attr.src = this.authorizeURL(sanitizeUrlForMime(attr.src, 'image'));
attr.alt = data.name;
if (data.ref || data.val) {
attr.onClick = this.onVideoPreview;
attr.onClick = (e) => this.onHandleClick(e, 'video');
attr.loading = 'lazy';
}
el = InlineVideo;
@@ -329,6 +336,12 @@ export function previewFormatter(style, data, values, key) {
el = null;
values = null;
break;
case 'TC':
// TheCard
el = React.Fragment;
values = [<i key="tc" className="material-icons">contact_mail</i>, ' ',
TheCard.getFn(data) || this.formatMessage(messages.drafty_unknown)];
break;
case 'VD':
// Replace image with '[icon] Video'.
el = React.Fragment;
@@ -392,7 +405,7 @@ function inlineVideoAttr(attr, data) {
// formatMessage: this.props.intl.formatMessage
// messages: formatjs messages defined with defineMessages.
// authorizeURL: this.props.tinode.authorizeURL
// onQuoteClick: this.handleQuoteClick (optional)
// onHandleClick: this.handleDraftyClick
function quoteFormatter(style, data, values, key) {
if (['BR', 'EX', 'IM', 'MN', 'VD'].includes(style)) {
let el = Drafty.tagName(style);
+38 -3
View File
@@ -3,7 +3,7 @@
import React from 'react';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { Drafty, Tinode } from 'tinode-sdk';
import { Drafty, Tinode, TheCard } from 'tinode-sdk';
import CallPanel from '../widgets/call-panel.jsx';
import ChatMessage from '../widgets/chat-message.jsx';
@@ -104,6 +104,11 @@ const messages = defineMessages({
'other {{count, number} members}}',
description: 'Count of group topic members'
},
cannot_parse_vcard: {
id: 'cannot_parse_vcard',
defaultMessage: 'Cannot parse vCard file.',
description: 'Error message when vCard file cannot be parsed'
}
});
// Checks if the access permissions are granted but not yet accepted.
@@ -144,6 +149,7 @@ class MessagesView extends React.Component {
this.sendVideoAttachment = this.sendVideoAttachment.bind(this);
this.sendFileAttachment = this.sendFileAttachment.bind(this);
this.sendAudioAttachment = this.sendAudioAttachment.bind(this);
this.sendTheCardAttachment = this.sendTheCardAttachment.bind(this);
this.sendKeyPress = this.sendKeyPress.bind(this);
this.subscribe = this.subscribe.bind(this);
this.handleScrollReference = this.handleScrollReference.bind(this);
@@ -1039,7 +1045,7 @@ class MessagesView extends React.Component {
});
}
// Send attachment as Drafty message:
// sendFileAttachment sends the file as Drafty message:
// - if file is too large, upload it and send a s link.
// - if file is small enough, just send it in-band.
sendFileAttachment(file) {
@@ -1047,6 +1053,15 @@ class MessagesView extends React.Component {
const maxInbandAttachmentSize = (this.props.tinode.getServerParam('maxMessageSize',
MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;
if (TheCard.isFileSupported(file.type, file.name)) {
// This is a vCard file, try to convert and send as TheCard.
if (this.sendTheCardAttachment(file, maxInbandAttachmentSize)) {
// Successfully sent as TheCard.
return;
}
// Sending as TheCard failed, fall through to sending as generic file attachment.
}
// If the attachment is a JSON file, then use 'application/octet-stream' instead of 'application/json'.
// This is a temporary workaround for the collision with the 'application/json' MIME type of form responses.
// Remove this code in 2026 or so.
@@ -1081,6 +1096,26 @@ class MessagesView extends React.Component {
}
}
// Convert attached vCard to TheCard and send.
sendTheCardAttachment(file, maxInbandAttachmentSize) {
if (file.size > maxInbandAttachmentSize) {
// Cannot convert to TheCard, send as a generic file attachment instead.
return false;
}
const reader = new FileReader();
reader.onload = (e) => {
const card = TheCard.importVCard(e.target.result);
if (!card) {
// Failed to parse vCard.
this.props.onError(this.props.intl.formatMessage(messages.cannot_parse_vcard), 'err');
}
this.sendMessage(Drafty.appendTheCard(null, card));
};
reader.readAsText(file);
return true;
}
// handleAttachFile method is called when [Attach file] button is clicked: launch attachment preview.
handleAttachFile(file) {
const maxExternAttachmentSize = this.props.tinode.getServerParam('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);
@@ -1777,7 +1812,7 @@ class MessagesView extends React.Component {
null}
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
avatar={avatar}
topic={this.state.topic}
title={this.state.title}
+1 -1
View File
@@ -48,7 +48,7 @@ export default class AvatarUpload extends React.Component {
this.props.readOnly && this.props.uid ?
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
avatar={true}
topic={this.props.uid}
title={this.props.title} />
+1 -1
View File
@@ -122,7 +122,7 @@ export default class CallIncoming extends React.Component {
<div className="caller-card incoming pulse">
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
avatar={this.state.avatar || true}
topic={this.props.topic}
title={this.state.fullName} />
+1 -1
View File
@@ -680,7 +680,7 @@ class CallPanel extends React.PureComponent {
<div className={`caller-card${pulseAnimation} draggable`}>
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
avatar={this.props.avatar}
topic={this.props.topic}
title={this.props.title} />
+31 -7
View File
@@ -26,22 +26,46 @@ class BaseChatMessage extends React.PureComponent {
this.handleExpandImage = this.handleExpandImage.bind(this);
this.handlePlayVideo = this.handlePlayVideo.bind(this);
this.handleFormButtonClick = this.handleFormButtonClick.bind(this);
this.handleQuoteClick = this.handleQuoteClick.bind(this);
this.handleContextClick = this.handleContextClick.bind(this);
this.handleCancelUpload = this.handleCancelUpload.bind(this);
this.handleQuoteClick = this.handleQuoteClick.bind(this);
this.handleDraftyClick = this.handleDraftyClick.bind(this);
this.formatterContext = {
formatMessage: props.intl.formatMessage.bind(props.intl),
viewportWidth: props.viewportWidth,
authorizeURL: props.tinode.authorizeURL.bind(props.tinode),
onImagePreview: this.handleExpandImage,
onVideoPreview: this.handlePlayVideo,
onFormButtonClick: this.handleFormButtonClick,
onQuoteClick: this.handleQuoteClick
onHandleClick: this.handleDraftyClick
};
}
handleDraftyClick = (e, action) => {
switch (action) {
case 'image':
this.handleExpandImage(e);
break;
case 'video':
this.handlePlayVideo(e);
break;
case 'form_button':
this.handleFormButtonClick(e);
break;
case 'quote':
this.handleQuoteClick(e);
break;
case 'contact_chat':
case 'contact_find':
e.preventDefault();
console.log('Contact click handling not implemented yet.', action, e.target.dataset);
break;
default:
// No special handling; let the browser deal with it.
console.log('Unhandled drafty action.', action, e.target.dataset);
break;
}
}
handleExpandImage(e) {
e.preventDefault();
this.props.onExpandMedia({
@@ -99,7 +123,7 @@ class BaseChatMessage extends React.PureComponent {
if (!this.props.response) {
let immutable = false;
Drafty.entities(this.props.content, (_0, _1, tp) => {
immutable = ['AU', 'EX', 'FM', 'IM', 'VC', 'VD'].includes(tp);
immutable = ['AU', 'EX', 'FM', 'IM', 'TC', 'VC', 'VD'].includes(tp);
return immutable;
});
if (!immutable) {
@@ -200,7 +224,7 @@ class BaseChatMessage extends React.PureComponent {
<div className="avatar-box">
{fullDisplay ?
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
topic={this.props.userFrom}
title={this.props.userName}
avatar={avatar} /> :
+1 -1
View File
@@ -23,7 +23,7 @@ export default class Chip extends React.PureComponent {
<span className="spacer" /> :
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
avatar={this.props.avatar || true}
topic={this.props.topic}
title={this.props.title} />
+1 -1
View File
@@ -122,7 +122,7 @@ class Contact extends React.Component {
<li className={bgColor} onClick={this.handleClick}>
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
avatar={avatar}
title={this.props.title}
topic={this.props.item}
+1 -1
View File
@@ -22,7 +22,7 @@ export default class GroupSubs extends React.Component {
usersOnline.push(
<div className="avatar-box" key={sub.user}>
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
topic={sub.user}
avatar={makeImageUrl(sub.public ? sub.public.photo : null) || true}
title={sub.public ? sub.public.fn : null} />
+1 -1
View File
@@ -27,7 +27,7 @@ export default class LetterTile extends React.PureComponent {
}
}
} else if (this.props.avatar) {
const url = this.props.tinode.authorizeURL(sanitizeUrlForMime(this.props.avatar, 'image'));
const url = this.props.authorizeURL(sanitizeUrlForMime(this.props.avatar, 'image'));
// If avatar image is invalid, show a placeholder.
const className = 'avatar' + (this.props.deleted ? ' deleted' : '');
avatar = <img className={className} alt="avatar" src={url}
+1 -1
View File
@@ -26,7 +26,7 @@ export default class SideNavbar extends React.PureComponent {
{avatar ?
<div id="self-avatar" className="avatar-box">
<LetterTile
tinode={this.props.tinode}
authorizeURL={this.props.tinode.authorizeURL}
avatar={avatar}
topic={this.props.myUserId}
title={this.props.title} />