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
+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
+254 -67
View File
@@ -7679,11 +7679,13 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _widgets_call_status_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../widgets/call-status.jsx */ "./src/widgets/call-status.jsx");
/* harmony import */ var _widgets_inline_video_jsx__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../widgets/inline-video.jsx */ "./src/widgets/inline-video.jsx");
/* harmony import */ var _widgets_lazy_image_jsx__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../widgets/lazy-image.jsx */ "./src/widgets/lazy-image.jsx");
/* harmony import */ var _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../widgets/uploading-image.jsx */ "./src/widgets/uploading-image.jsx");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
/* harmony import */ var _blob_helpers_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./blob-helpers.js */ "./src/lib/blob-helpers.js");
/* harmony import */ var _strformat_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./strformat.js */ "./src/lib/strformat.js");
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./utils.js */ "./src/lib/utils.js");
/* harmony import */ var _widgets_the_card_viewer_jsx__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../widgets/the-card-viewer.jsx */ "./src/widgets/the-card-viewer.jsx");
/* harmony import */ var _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../widgets/uploading-image.jsx */ "./src/widgets/uploading-image.jsx");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
/* harmony import */ var _blob_helpers_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./blob-helpers.js */ "./src/lib/blob-helpers.js");
/* harmony import */ var _strformat_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./strformat.js */ "./src/lib/strformat.js");
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./utils.js */ "./src/lib/utils.js");
@@ -7747,7 +7749,7 @@ function fullFormatter(style, data, values, key, stack) {
switch (style) {
case 'AU':
if (attr.src) {
attr.src = this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.sanitizeUrlForMime)(attr.src, 'audio'));
attr.src = this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_13__.sanitizeUrlForMime)(attr.src, 'audio'));
attr.duration = data.duration > 0 ? data.duration | 0 : undefined;
attr.preview = data.preview;
attr.loading = 'lazy';
@@ -7772,7 +7774,7 @@ function fullFormatter(style, data, values, key, stack) {
values = null;
break;
case 'BN':
attr.onClick = this.onFormButtonClick;
attr.onClick = e => this.onHandleClick(e, 'form_button');
let inner = react__WEBPACK_IMPORTED_MODULE_0___default().Children.map(values, child => {
return typeof child == 'string' ? child : undefined;
});
@@ -7783,13 +7785,13 @@ function fullFormatter(style, data, values, key, stack) {
break;
case 'LN':
if (attr) {
attr.href = typeof attr.href == 'string' ? (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.sanitizeUrl)(attr.href, _config_js__WEBPACK_IMPORTED_MODULE_9__.CLICKABLE_URL_SCHEMES) : '';
attr.href = typeof attr.href == 'string' ? (0,_utils_js__WEBPACK_IMPORTED_MODULE_13__.sanitizeUrl)(attr.href, _config_js__WEBPACK_IMPORTED_MODULE_10__.CLICKABLE_URL_SCHEMES) : '';
}
break;
case 'MN':
attr.className = 'mention';
if (data) {
attr.className += ' ' + (0,_strformat_js__WEBPACK_IMPORTED_MODULE_11__.idToColorClass)(data.val, false, true);
attr.className += ' ' + (0,_strformat_js__WEBPACK_IMPORTED_MODULE_12__.idToColorClass)(data.val, false, true);
}
break;
case 'FM':
@@ -7799,7 +7801,15 @@ function fullFormatter(style, data, values, key, stack) {
break;
case 'QQ':
attr.className = 'reply-quote';
attr.onClick = this.onQuoteClick;
attr.onClick = e => this.onHandleClick(e, 'quote');
break;
case 'TC':
el = _widgets_the_card_viewer_jsx__WEBPACK_IMPORTED_MODULE_8__["default"];
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 = _widgets_call_message_jsx__WEBPACK_IMPORTED_MODULE_4__["default"];
@@ -7843,15 +7853,15 @@ function handleImageData(el, data, attr) {
if (!data) {
attr.src = 'img/broken_image.png';
attr.style = {
width: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px',
height: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px'
width: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px',
height: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px'
};
return el;
}
attr.className = 'inline-image';
const dim = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_10__.fitImageSize)(data.width, data.height, this.viewportWidth > 0 ? Math.min(this.viewportWidth - _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 6.5, _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 34.5) : _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 34.5, _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 24, false) || {
dstWidth: _config_js__WEBPACK_IMPORTED_MODULE_9__.BROKEN_IMAGE_SIZE,
dstHeight: _config_js__WEBPACK_IMPORTED_MODULE_9__.BROKEN_IMAGE_SIZE
const dim = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_11__.fitImageSize)(data.width, data.height, this.viewportWidth > 0 ? Math.min(this.viewportWidth - _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 6.5, _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 34.5) : _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 34.5, _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 24, false) || {
dstWidth: _config_js__WEBPACK_IMPORTED_MODULE_10__.BROKEN_IMAGE_SIZE,
dstHeight: _config_js__WEBPACK_IMPORTED_MODULE_10__.BROKEN_IMAGE_SIZE
};
attr.style = {
width: dim.dstWidth + 'px',
@@ -7860,11 +7870,11 @@ function handleImageData(el, data, attr) {
minHeight: dim.dstHeight + 'px'
};
if (!tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isProcessing(data)) {
attr.src = this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.sanitizeUrlForMime)(attr.src, 'image'));
attr.src = this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_13__.sanitizeUrlForMime)(attr.src, 'image'));
attr.alt = data.name;
if (attr.src) {
if (Math.max(data.width || 0, data.height || 0) > _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM) {
attr.onClick = this.onImagePreview;
if (Math.max(data.width || 0, data.height || 0) > _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM) {
attr.onClick = e => this.onHandleClick(e, 'image');
attr.className += ' image-clickable';
}
attr.loading = 'lazy';
@@ -7872,7 +7882,7 @@ function handleImageData(el, data, attr) {
attr.src = null;
}
} else {
el = _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_8__["default"];
el = _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_9__["default"];
}
return el;
}
@@ -7880,15 +7890,15 @@ function handleVideoData(el, data, attr) {
if (!data) {
attr.src = 'img/broken_video.png';
attr.style = {
width: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px',
height: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px'
width: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px',
height: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px'
};
return el;
}
attr.className = 'inline-image';
const dim = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_10__.fitImageSize)(data.width, data.height, this.viewportWidth > 0 ? Math.min(this.viewportWidth - _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 6.5, _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 34.5) : _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 34.5, _config_js__WEBPACK_IMPORTED_MODULE_9__.REM_SIZE * 24, false) || {
dstWidth: _config_js__WEBPACK_IMPORTED_MODULE_9__.NO_DIMENSIONS_VIDEO,
dstHeight: _config_js__WEBPACK_IMPORTED_MODULE_9__.NO_DIMENSIONS_VIDEO
const dim = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_11__.fitImageSize)(data.width, data.height, this.viewportWidth > 0 ? Math.min(this.viewportWidth - _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 6.5, _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 34.5) : _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 34.5, _config_js__WEBPACK_IMPORTED_MODULE_10__.REM_SIZE * 24, false) || {
dstWidth: _config_js__WEBPACK_IMPORTED_MODULE_10__.NO_DIMENSIONS_VIDEO,
dstHeight: _config_js__WEBPACK_IMPORTED_MODULE_10__.NO_DIMENSIONS_VIDEO
};
attr.style = {
width: dim.dstWidth + 'px',
@@ -7897,15 +7907,15 @@ function handleVideoData(el, data, attr) {
minHeight: dim.dstHeight + 'px'
};
if (!tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isProcessing(data)) {
attr.src = this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.sanitizeUrlForMime)(attr.src, 'image'));
attr.src = this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_13__.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 = _widgets_inline_video_jsx__WEBPACK_IMPORTED_MODULE_6__["default"];
} else {
el = _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_8__["default"];
el = _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_9__["default"];
}
return el;
}
@@ -7923,7 +7933,7 @@ function previewFormatter(style, data, values, key) {
values = [react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
key: "au",
className: "material-icons"
}, "mic"), ' ', (0,_strformat_js__WEBPACK_IMPORTED_MODULE_11__.secondsToTime)(data.duration / 1000)];
}, "mic"), ' ', (0,_strformat_js__WEBPACK_IMPORTED_MODULE_12__.secondsToTime)(data.duration / 1000)];
break;
case 'BR':
el = (react__WEBPACK_IMPORTED_MODULE_0___default().Fragment);
@@ -7985,6 +7995,13 @@ function previewFormatter(style, data, values, key) {
el = null;
values = null;
break;
case 'TC':
el = (react__WEBPACK_IMPORTED_MODULE_0___default().Fragment);
values = [react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
key: "tc",
className: "material-icons"
}, "contact_mail"), ' ', tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.getFn(data) || this.formatMessage(messages.drafty_unknown)];
break;
case 'VD':
el = (react__WEBPACK_IMPORTED_MODULE_0___default().Fragment);
values = [react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
@@ -8010,10 +8027,10 @@ function previewFormatter(style, data, values, key) {
;
function inlineImageAttr(attr, data) {
attr.style = {
width: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px',
height: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px',
maxWidth: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px',
maxHeight: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px'
width: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px',
height: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px',
maxWidth: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px',
maxHeight: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px'
};
attr.className = 'inline-image';
attr.alt = this.formatMessage(messages.drafty_image);
@@ -8026,12 +8043,12 @@ function inlineImageAttr(attr, data) {
return attr;
}
function inlineVideoAttr(attr, data) {
const dim = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_10__.fitImageSize)(data.width, data.height, _config_js__WEBPACK_IMPORTED_MODULE_9__.VIDEO_THUMBNAIL_WIDTH, _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM);
const dim = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_11__.fitImageSize)(data.width, data.height, _config_js__WEBPACK_IMPORTED_MODULE_10__.VIDEO_THUMBNAIL_WIDTH, _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM);
attr.style = {
width: dim.width + 'px',
height: dim.height + 'px',
maxWidth: _config_js__WEBPACK_IMPORTED_MODULE_9__.VIDEO_THUMBNAIL_WIDTH + 'px',
maxHeight: _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM + 'px'
maxWidth: _config_js__WEBPACK_IMPORTED_MODULE_10__.VIDEO_THUMBNAIL_WIDTH + 'px',
maxHeight: _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM + 'px'
};
attr.className = 'inline-image';
attr.alt = this.formatMessage(messages.drafty_video);
@@ -8072,7 +8089,7 @@ function quoteFormatter(style, data, values, key) {
el = 'span';
attr.className = 'mention';
if (data) {
attr.className += ' ' + (0,_strformat_js__WEBPACK_IMPORTED_MODULE_11__.idToColorClass)(data.val, false, true);
attr.className += ' ' + (0,_strformat_js__WEBPACK_IMPORTED_MODULE_12__.idToColorClass)(data.val, false, true);
}
break;
case 'EX':
@@ -8089,7 +8106,7 @@ function quoteFormatter(style, data, values, key) {
values = [react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
key: "ex",
className: "material-icons"
}, "attachment"), (0,_strformat_js__WEBPACK_IMPORTED_MODULE_11__.shortenFileName)(fname, 16) || this.formatMessage(messages.drafty_attachment)];
}, "attachment"), (0,_strformat_js__WEBPACK_IMPORTED_MODULE_12__.shortenFileName)(fname, 16) || this.formatMessage(messages.drafty_attachment)];
break;
}
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(el, attr, values);
@@ -8109,13 +8126,13 @@ function quoteImageOrVideo(data, isVideo) {
ref = data.ref;
}
if (bits) {
const blob = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_10__.base64ToBlob)(bits, mime);
const blob = (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_11__.base64ToBlob)(bits, mime);
if (!blob) {
throw new Error("Invalid image");
}
promise = Promise.resolve(blob);
} else if (ref) {
promise = fetch(this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.sanitizeUrlForMime)(ref, 'image'))).then(evt => {
promise = fetch(this.authorizeURL((0,_utils_js__WEBPACK_IMPORTED_MODULE_13__.sanitizeUrlForMime)(ref, 'image'))).then(evt => {
if (evt.ok) {
return evt.blob();
} else {
@@ -8126,7 +8143,7 @@ function quoteImageOrVideo(data, isVideo) {
throw new Error("Missing image data");
}
return promise.then(blob => {
return (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_10__.imageScaled)(blob, isVideo ? _config_js__WEBPACK_IMPORTED_MODULE_9__.VIDEO_THUMBNAIL_WIDTH : _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM, _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM, -1, !isVideo);
return (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_11__.imageScaled)(blob, isVideo ? _config_js__WEBPACK_IMPORTED_MODULE_10__.VIDEO_THUMBNAIL_WIDTH : _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM, _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM, -1, !isVideo);
}).then(scaled => {
if (isVideo) {
data.premime = scaled.mime;
@@ -8139,7 +8156,7 @@ function quoteImageOrVideo(data, isVideo) {
delete data.ref;
delete data.preref;
data.src = URL.createObjectURL(scaled.blob);
return (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_10__.blobToBase64)(scaled.blob);
return (0,_blob_helpers_js__WEBPACK_IMPORTED_MODULE_11__.blobToBase64)(scaled.blob);
}).then(b64 => {
if (isVideo) {
data.preview = b64.bits;
@@ -8151,8 +8168,8 @@ function quoteImageOrVideo(data, isVideo) {
delete data.val;
delete data.preview;
delete data.src;
data.width = _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM;
data.height = _config_js__WEBPACK_IMPORTED_MODULE_9__.IMAGE_THUMBNAIL_DIM;
data.width = _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM;
data.height = _config_js__WEBPACK_IMPORTED_MODULE_10__.IMAGE_THUMBNAIL_DIM;
throw err;
});
}
@@ -8166,10 +8183,10 @@ function replyFormatter(style, data, values, key, stack) {
}, data);
let loadedPromise;
try {
loadedPromise = (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.cancelablePromise)(quoteImageOrVideo.call(this, data, style == 'VD'));
loadedPromise = (0,_utils_js__WEBPACK_IMPORTED_MODULE_13__.cancelablePromise)(quoteImageOrVideo.call(this, data, style == 'VD'));
} catch (error) {
console.warn("Failed to quote image:", error.message);
loadedPromise = (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.cancelablePromise)(error);
loadedPromise = (0,_utils_js__WEBPACK_IMPORTED_MODULE_13__.cancelablePromise)(error);
}
attr.whenDone = loadedPromise;
values = [react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_lazy_image_jsx__WEBPACK_IMPORTED_MODULE_7__["default"], attr, null), ' ', attr.alt];
@@ -9771,6 +9788,13 @@ const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
"offset": 0,
"pluralType": "cardinal"
}]
},
cannot_parse_vcard: {
id: "cannot_parse_vcard",
defaultMessage: [{
"type": 0,
"value": "Cannot parse vCard file."
}]
}
});
function isUnconfirmed(acs) {
@@ -9802,6 +9826,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
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);
@@ -10605,6 +10630,11 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
sendFileAttachment(file) {
const maxInbandAttachmentSize = this.props.tinode.getServerParam('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_18__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.isFileSupported(file.type, file.name)) {
if (this.sendTheCardAttachment(file, maxInbandAttachmentSize)) {
return;
}
}
const jsonMimeConverter = fileType => fileType === _config_js__WEBPACK_IMPORTED_MODULE_18__.DRAFTY_FR_MIME_TYPE_LEGACY ? 'application/octet-stream' : fileType;
if (file.size > maxInbandAttachmentSize) {
const uploader = this.props.tinode.getLargeFileHelper();
@@ -10629,6 +10659,21 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}))).catch(err => this.props.onError(err.message, 'err'));
}
}
sendTheCardAttachment(file, maxInbandAttachmentSize) {
if (file.size > maxInbandAttachmentSize) {
return false;
}
const reader = new FileReader();
reader.onload = e => {
const card = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.importVCard(e.target.result);
if (!card) {
this.props.onError(this.props.intl.formatMessage(messages.cannot_parse_vcard), 'err');
}
this.sendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendTheCard(null, card));
};
reader.readAsText(file);
return true;
}
handleAttachFile(file) {
const maxExternAttachmentSize = this.props.tinode.getServerParam('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_18__.MAX_EXTERN_ATTACHMENT_SIZE);
if (file.size > maxExternAttachmentSize) {
@@ -11260,7 +11305,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}, "arrow_back")) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_11__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
avatar: avatar,
topic: this.state.topic,
title: this.state.title,
@@ -14938,7 +14983,7 @@ class AvatarUpload extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}) : this.props.readOnly && this.props.uid ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_1__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
avatar: true,
topic: this.props.uid,
title: this.props.title
@@ -15191,7 +15236,7 @@ class CallIncoming extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
avatar: this.state.avatar || true,
topic: this.props.topic,
title: this.state.fullName
@@ -15939,7 +15984,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
avatar: this.props.avatar,
topic: this.props.topic,
title: this.props.title
@@ -16109,19 +16154,41 @@ class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
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:
console.log('Unhandled drafty action.', action, e.target.dataset);
break;
}
};
handleExpandImage(e) {
e.preventDefault();
this.props.onExpandMedia({
@@ -16175,7 +16242,7 @@ class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
if (!this.props.response) {
let immutable = false;
tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.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) {
@@ -16274,7 +16341,7 @@ class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
}, this.props.isGroup && this.props.response ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, fullDisplay ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
topic: this.props.userFrom,
title: this.props.userName,
avatar: avatar
@@ -16563,7 +16630,7 @@ class Chip extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent)
}) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_1__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
avatar: this.props.avatar || true,
topic: this.props.topic,
title: this.props.title
@@ -16998,7 +17065,7 @@ class Contact extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
avatar: avatar,
title: this.props.title,
topic: this.props.item,
@@ -18216,7 +18283,7 @@ class GroupSubs extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
className: "avatar-box",
key: sub.user
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
topic: sub.user,
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageUrl)(sub.public ? sub.public.photo : null) || true,
title: sub.public ? sub.public.fn : null
@@ -18894,7 +18961,7 @@ class LetterTile extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompo
}
}
} else if (this.props.avatar) {
const url = this.props.tinode.authorizeURL((0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_3__.sanitizeUrlForMime)(this.props.avatar, 'image'));
const url = this.props.authorizeURL((0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_3__.sanitizeUrlForMime)(this.props.avatar, 'image'));
const className = 'avatar' + (this.props.deleted ? ' deleted' : '');
avatar = react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
className: className,
@@ -20247,7 +20314,7 @@ class SideNavbar extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompo
id: "self-avatar",
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_1__["default"], {
tinode: this.props.tinode,
authorizeURL: this.props.tinode.authorizeURL,
avatar: avatar,
topic: this.props.myUserId,
title: this.props.title
@@ -20481,6 +20548,132 @@ class TagManager extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component
/***/ }),
/***/ "./src/widgets/the-card-viewer.jsx":
/*!*****************************************!*\
!*** ./src/widgets/the-card-viewer.jsx ***!
\*****************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
unknown_name: {
id: "unknown_name",
defaultMessage: [{
"type": 0,
"value": "Unknown"
}]
}
});
class TheCardViewer extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
constructor(props) {
super(props);
this.handleDownload = this.handleDownload.bind(this);
this.handleMessage = this.handleMessage.bind(this);
}
handleDownload(e) {
e.preventDefault();
try {
const vcard = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.exportVCard(this.props.content);
const blob = new Blob([vcard], {
type: 'text/vcard'
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = (this.props.content.fn || 'contact') + '.vcf';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (err) {
console.error("Failed to download vcard", err);
if (this.props.onError) {
this.props.onError("Failed to download vcard", 'err');
}
}
}
handleMessage(e) {}
render() {
const card = this.props.content;
if (!card) {
return null;
}
const uid = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.getFirstTinodeID(card);
const contacts = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.getEmails(card).concat(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.getPhones(card));
const org = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.getOrg(card);
console.log('Rendering TheCardViewer', JSON.stringify(contacts), uid);
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "contact-card"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "contact-body"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
authorizeURL: this.props.authorizeURL,
topic: uid || 'usr123XXX',
avatar: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.getPhotoUrl(card) || true,
title: card.fn
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "name-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "name"
}, card.fn || react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, messages.unknown_name)), org && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "org"
}, org))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "contact-actions"
}, uid && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
className: "flat-button",
"data-val": uid,
onClick: this.props.onChatClick
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "chat_now",
defaultMessage: [{
"type": 0,
"value": "Chat"
}]
})), uid && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "divider"
}), contacts.length > 0 && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
className: "flat-button",
"data-val": contacts.join(','),
onClick: this.props.onChatClick
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "find_user",
defaultMessage: [{
"type": 0,
"value": "Find"
}]
})), contacts.length > 0 && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "divider"
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
className: "flat-button",
onClick: this.handleDownload
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "save_action",
defaultMessage: [{
"type": 0,
"value": "Save"
}]
}))));
}
}
;
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(TheCardViewer));
/***/ }),
/***/ "./src/widgets/topic-desc-edit.jsx":
/*!*****************************************!*\
!*** ./src/widgets/topic-desc-edit.jsx ***!
@@ -21263,12 +21456,6 @@ module.exports = tinode;
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Check if module exists (development only)
/******/ if (__webpack_modules__[moduleId] === undefined) {
/******/ var e = new Error("Cannot find module '" + moduleId + "'");
/******/ e.code = 'MODULE_NOT_FOUND';
/******/ throw e;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
+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
File diff suppressed because one or more lines are too long
+384 -16
View File
@@ -1400,7 +1400,7 @@ class DB {
/***/ (function(module) {
/**
* @copyright 2015-2024 Tinode LLC.
* @copyright 2015-2026 Tinode LLC.
* @summary Minimally rich text representation and formatting for Tinode.
* @license Apache 2.0
*
@@ -1460,7 +1460,7 @@ const MAX_PREVIEW_DATA_SIZE = 64;
const DRAFTY_MIME_TYPE = 'text/x-drafty';
const DRAFTY_FR_MIME_TYPE = 'text/x-drafty-fr';
const DRAFTY_FR_MIME_TYPE_LEGACY = 'application/json';
const ALLOWED_ENT_FIELDS = ['act', 'height', 'duration', 'incoming', 'mime', 'name', 'premime', 'preref', 'preview', 'ref', 'size', 'state', 'url', 'val', 'width'];
const ALLOWED_ENT_FIELDS = ['act', 'height', 'duration', 'fn', 'incoming', 'mime', 'name', 'premime', 'preref', 'preview', 'ref', 'size', 'state', 'url', 'val', 'width'];
const segmenter = new Intl.Segmenter();
const INLINE_STYLES = [{
name: 'ST',
@@ -1597,6 +1597,11 @@ const FORMAT_TAGS = {
md_tag: '*',
isVoid: false
},
TC: {
html_tag: 'div',
md_tag: undefined,
isVoid: false
},
VC: {
html_tag: 'div',
md_tag: undefined,
@@ -1769,6 +1774,17 @@ const DECORATORS = {
return data ? {} : null;
}
},
TC: {
open: _ => '<div>',
close: _ => '</div>',
props: data => {
if (!data) return {};
return {
'data-fn': data.fn,
'data-title': data.title
};
}
},
VC: {
open: _ => '<div>',
close: _ => '</div>',
@@ -2314,6 +2330,24 @@ Drafty.appendLineBreak = function (content) {
content.txt += ' ';
return content;
};
Drafty.appendTheCard = function (content, theCardData) {
content = content || {
txt: ''
};
content.ent = content.ent || [];
content.fmt = content.fmt || [];
content.fmt.push({
at: stringToGraphemes(content.txt).length,
len: 1,
key: content.ent.length
});
content.txt += ' ';
content.ent.push({
tp: 'TC',
data: theCardData
});
return content;
};
Drafty.UNSAFE_toHTML = function (doc) {
const tree = draftyToTree(doc);
const htmlFormatter = function (type, data, values) {
@@ -3930,6 +3964,336 @@ class MetaGetBuilder {
/***/ }),
/***/ "./src/the-card.js":
/*!*************************!*\
!*** ./src/the-card.js ***!
\*************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ TheCard; }
/* harmony export */ });
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config.js */ "./src/config.js");
class TheCard {
constructor(fn, imageUrl, imageMimeType, note) {
this.card = theCard(fn, imageUrl, imageMimeType, note);
}
getCard() {
return this.card;
}
}
TheCard.init = theCard;
TheCard.contentType = 'text/x-card';
TheCard.setFn = function (card, fn) {
fn = fn && fn.trim();
card = card || {};
card.fn = fn || undefined;
return card;
};
TheCard.getFn = function (card) {
if (card && card.fn) {
return card.fn;
}
return null;
};
TheCard.setNote = function (card, note) {
note = note && note.trim();
card = card || {};
card.note = note ? note : _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR;
return card;
};
TheCard.setPhoto = function (card, imageUrl, imageMimeType) {
if (imageUrl) {
card = card || {};
let mimeType = imageMimeType;
const matches = /^data:(image\/[-a-z0-9+.]+)?(;base64)?,/i.exec(imageUrl);
if (matches) {
mimeType = matches[1];
card.photo = {
data: imageUrl.substring(imageUrl.indexOf(',') + 1),
ref: _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR
};
} else {
card.photo = {
data: _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR,
ref: imageUrl
};
if (!mimeType) {
const ext = /\.([a-z0-9]+)$/i.exec(imageUrl);
if (ext) {
const extLower = ext[1].toLowerCase();
const mimeMap = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'bmp': 'image/bmp',
'webp': 'image/webp',
'svg': 'image/svg+xml'
};
mimeType = mimeMap[extLower] || null;
}
}
}
card.photo.type = (mimeType || 'image/jpeg').substring('image/'.length);
} else {
card = card || {};
card.photo = _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR;
}
return card;
};
TheCard.getPhotoUrl = function (card) {
if (card && card.photo) {
if (card.photo.ref && card.photo.ref != _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR) {
return card.photo.ref;
} else if (card.photo.data && card.photo.data != _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR) {
return 'data:image/' + (card.photo.type || 'jpeg') + ';base64,' + card.photo.data;
}
}
return null;
};
TheCard.getOrg = function (card) {
if (card && card.org) {
return card.org.fn || null;
}
return null;
};
TheCard.setPhone = function (card, phone, type = 'voice') {
return addOrSetComm(card, 'tel', phone, type, true);
};
TheCard.setEmail = function (card, email, type = 'home') {
return addOrSetComm(card, 'email', email, type, true);
};
TheCard.setTinodeID = function (card, tinodeID, type = 'home') {
return addOrSetComm(card, 'tinode', tinodeID, type, true);
};
TheCard.addPhone = function (card, phone, type = 'voice') {
return addOrSetComm(card, 'tel', phone, type, false);
};
TheCard.addEmail = function (card, email, type = 'home') {
return addOrSetComm(card, 'email', email, type, false);
};
TheCard.addTinodeID = function (card, tinodeID, type = 'home') {
return addOrSetComm(card, 'tinode', tinodeID, type, false);
};
TheCard.clearPhone = function (card, phone, type) {
return clearComm(card, 'tel', phone, type);
};
TheCard.clearEmail = function (card, email, type) {
return clearComm(card, 'email', email, type);
};
TheCard.clearTinodeID = function (card, tinodeID, type) {
return clearComm(card, 'tinode', tinodeID, type);
};
TheCard.getComm = function (card, proto) {
if (card && Array.isArray(card.comm)) {
return card.comm.filter(c => c.proto == proto);
}
return [];
};
TheCard.getEmails = function (card) {
return TheCard.getComm(card, 'email').map(c => c.value);
};
TheCard.getPhones = function (card) {
return TheCard.getComm(card, 'tel').map(c => c.value);
};
TheCard.getFirstTinodeID = function (card) {
const comms = TheCard.getComm(card, 'tinode');
if (comms.length > 0) {
return comms[0].value;
}
return null;
};
TheCard.exportVCard = function (card) {
if (!card) {
return null;
}
let vcard = 'BEGIN:VCARD\r\nVERSION:3.0\r\n';
if (card.fn) {
vcard += `FN:${card.fn}\r\n`;
}
if (card.n) {
vcard += `N:${card.n.surname || ''};${card.n.given || ''};${card.n.additional || ''};${card.n.prefix || ''};${card.n.suffix || ''}\r\n`;
}
if (card.org) {
if (card.org.fn) {
vcard += `ORG:${card.org.fn}\r\n`;
}
if (card.org.title) {
vcard += `TITLE:${card.org.title}\r\n`;
}
}
if (card.note && card.note != _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR) {
vcard += `NOTE:${card.note}\r\n`;
}
if (card.photo) {
if (card.photo.ref && card.photo.ref != _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR) {
vcard += `PHOTO;VALUE=URI:${card.photo.ref}\r\n`;
} else if (card.photo.data && card.photo.data != _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR) {
vcard += `PHOTO;TYPE=${card.photo.type.toUpperCase()};ENCODING=b:${card.photo.data}\r\n`;
}
}
if (Array.isArray(card.comm)) {
card.comm.forEach(comm => {
if (comm.proto === 'tel') {
vcard += `TEL;TYPE=${comm.des.toUpperCase()}:${comm.value}\r\n`;
} else if (comm.proto === 'email') {
vcard += `EMAIL;TYPE=${comm.des.toUpperCase()}:${comm.value}\r\n`;
} else if (comm.proto === 'tinode') {
vcard += `IMPP;TYPE=${comm.des.toUpperCase()};tinode:${comm.value}\r\n`;
} else if (comm.proto === 'http') {
vcard += `URL;TYPE=${comm.des.toUpperCase()}:${comm.value}\r\n`;
}
});
}
vcard += 'END:VCARD\r\n';
return vcard;
};
TheCard.importVCard = function (vcardStr) {
if (!vcardStr || typeof vcardStr !== 'string') {
return null;
}
const lines = vcardStr.split(/\r\n|\n/);
let card = {};
lines.forEach(line => {
const [keyPart, ...valueParts] = line.split(':');
if (!keyPart || valueParts.length === 0) {
return;
}
const value = valueParts.join(':');
const keyParams = keyPart.split(';');
const key = keyParams[0].trim().toUpperCase();
if (key === 'FN') {
card.fn = value;
} else if (key === 'N') {
const parts = value.split(';');
card.n = {
surname: parts[0] || undefined,
given: parts[1] || undefined,
additional: parts[2] || undefined,
prefix: parts[3] || undefined,
suffix: parts[4] || undefined
};
Object.keys(card.n).forEach(key => card.n[key] === undefined && delete card.n[key]);
if (Object.keys(card.n).length === 0) {
delete card.n;
}
} else if (key === 'ORG') {
const parts = value.split(';');
card.org = card.org || {};
card.org.fn = parts[0] || undefined;
if (!card.org.fn) delete card.org.fn;
} else if (key === 'TITLE') {
card.org = card.org || {};
card.org.title = value || undefined;
if (!card.org.title) delete card.org.title;
} else if (key === 'NOTE') {
card.note = value;
} else if (key === 'PHOTO') {
const params = keyPart.split(';').slice(1);
let type = 'jpeg';
let encoding = null;
params.forEach(param => {
const [pKey, pValue] = param.split('=');
if (pKey && pKey.trim().toUpperCase() === 'TYPE') {
type = pValue ? pValue.trim().toLowerCase() : 'jpeg';
} else if (pKey && pKey.trim().toUpperCase() === 'ENCODING') {
encoding = pValue ? pValue.trim().toLowerCase() : null;
}
});
if (encoding === 'b') {
card.photo = {
type: type,
data: value,
ref: _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR
};
} else {
card.photo = {
type: type,
data: _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR,
ref: value
};
}
} else if (key === 'TEL') {
const des = keyPart.split(';').find(param => param.trim().toUpperCase().startsWith('TYPE='))?.split('=')[1].trim().toLowerCase() || 'voice';
card = TheCard.addPhone(card, value, des);
} else if (key === 'EMAIL') {
const des = keyPart.split(';').find(param => param.trim().toUpperCase().startsWith('TYPE='))?.split('=')[1].trim().toLowerCase() || 'home';
card = TheCard.addEmail(card, value, des);
} else if (key === 'IMPP') {
const des = keyPart.split(';').find(param => param.trim().toUpperCase().startsWith('TYPE='))?.split('=')[1].trim().toLowerCase() || 'home';
const tinodeID = value.replace(/^tinode:/, '');
card = TheCard.addTinodeID(card, tinodeID, des);
}
});
return card;
};
TheCard.isFileSupported = function (type, name) {
return type == 'text/vcard' || (name || '').endsWith('.vcf') || (name || '').endsWith('.vcard');
};
function theCard(fn, imageUrl, imageMimeType, note) {
let card = null;
fn = fn && fn.trim();
note = note && note.trim();
if (fn) {
card = {
fn: fn
};
}
if (typeof note == 'string') {
card = card || {};
card.note = note ? note : _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR;
}
if (imageUrl) {
card = card || {};
let mimeType = imageMimeType;
const matches = /^data:(image\/[-a-z0-9+.]+)?(;base64)?,/i.exec(imageUrl);
if (matches) {
mimeType = matches[1];
card.photo = {
data: imageUrl.substring(imageUrl.indexOf(',') + 1),
ref: _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR
};
} else {
card.photo = {
data: _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR,
ref: imageUrl
};
}
card.photo.type = (mimeType || 'image/jpeg').substring('image/'.length);
}
return card;
}
function addOrSetComm(card, proto, value, type, setOnly) {
proto = proto && proto.trim();
value = value && value.trim();
if (proto && value) {
card = card || {};
card.comm = card.comm || [];
if (setOnly) {
card.comm = card.comm.filter(c => !(c.proto == proto && c.des == type));
}
card.comm.push({
proto: proto,
des: type,
value: value
});
}
return card;
}
function clearComm(card, proto, value, type) {
if (card && Array.isArray(card.comm)) {
card.comm = card.comm.filter(c => !(c.proto == proto && (!type || c.des == type) && (!value || c.value == value)));
}
return card;
}
/***/ }),
/***/ "./src/topic.js":
/*!**********************!*\
!*** ./src/topic.js ***!
@@ -5549,6 +5913,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AccessMode: function() { return /* reexport safe */ _access_mode_js__WEBPACK_IMPORTED_MODULE_0__["default"]; },
/* harmony export */ Drafty: function() { return /* reexport default from dynamic */ _drafty_js__WEBPACK_IMPORTED_MODULE_5___default.a; },
/* harmony export */ TheCard: function() { return /* reexport safe */ _the_card_js__WEBPACK_IMPORTED_MODULE_11__["default"]; },
/* harmony export */ Tinode: function() { return /* binding */ Tinode; }
/* harmony export */ });
/* harmony import */ var _access_mode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./access-mode.js */ "./src/access-mode.js");
@@ -5563,11 +5928,12 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _topic_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./topic.js */ "./src/topic.js");
/* harmony import */ var _fnd_topic_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./fnd-topic.js */ "./src/fnd-topic.js");
/* harmony import */ var _me_topic_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./me-topic.js */ "./src/me-topic.js");
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./utils.js */ "./src/utils.js");
/* harmony import */ var _the_card_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./the-card.js */ "./src/the-card.js");
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./utils.js */ "./src/utils.js");
/**
* @module tinode-sdk
*
* @copyright 2015-2025 Tinode LLC.
* @copyright 2015-2026 Tinode LLC.
* @summary Javascript bindings for Tinode.
* @license Apache 2.0
* @version 0.25
@@ -5622,6 +5988,8 @@ __webpack_require__.r(__webpack_exports__);
let WebSocketProvider;
if (typeof WebSocket != 'undefined') {
WebSocketProvider = WebSocket;
@@ -5698,7 +6066,7 @@ function b64EncodeUnicode(str) {
}
function jsonBuildHelper(key, val) {
if (val instanceof Date) {
val = (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.rfc3339DateString)(val);
val = (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.rfc3339DateString)(val);
} else if (val instanceof _access_mode_js__WEBPACK_IMPORTED_MODULE_0__["default"]) {
val = val.jsonHelper();
} else if (val === undefined || val === null || val === false || Array.isArray(val) && val.length == 0 || typeof val == 'object' && Object.keys(val).length == 0) {
@@ -5858,7 +6226,7 @@ class Tinode {
});
}).then(_ => {
return this._db.mapUsers(data => {
this.#cachePut('user', data.uid, (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.mergeObj)({}, data.public));
this.#cachePut('user', data.uid, (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.mergeObj)({}, data.public));
});
}).then(_ => {
return Promise.all(prom);
@@ -5919,7 +6287,7 @@ class Tinode {
if (id) {
promise = this.#makePromise(id);
}
pkt = (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.simplify)(pkt);
pkt = (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.simplify)(pkt);
let msg = JSON.stringify(pkt);
this.logger("out: " + (this._trimLongStrings ? JSON.stringify(pkt, jsonLoggerHelper) : msg));
try {
@@ -5945,7 +6313,7 @@ class Tinode {
}
return;
}
let pkt = JSON.parse(data, _utils_js__WEBPACK_IMPORTED_MODULE_11__.jsonParseHelper);
let pkt = JSON.parse(data, _utils_js__WEBPACK_IMPORTED_MODULE_12__.jsonParseHelper);
if (!pkt) {
this.logger("in: " + data);
this.logger("ERROR: failed to parse data");
@@ -6206,13 +6574,13 @@ class Tinode {
if (pub) {
return {
user: uid,
public: (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.mergeObj)({}, pub)
public: (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.mergeObj)({}, pub)
};
}
return undefined;
};
topic._cachePutUser = (uid, user) => {
this.#cachePut('user', uid, (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.mergeObj)({}, user.public));
this.#cachePut('user', uid, (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.mergeObj)({}, user.public));
};
topic._cacheDelUser = uid => {
this.#cacheDel('user', uid);
@@ -6391,7 +6759,7 @@ class Tinode {
if (typeof url != 'string') {
return url;
}
if ((0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.isUrlRelative)(url)) {
if ((0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.isUrlRelative)(url)) {
const base = 'scheme://host/';
const parsed = new URL(url, base);
if (this._apiKey) {
@@ -6422,7 +6790,7 @@ class Tinode {
pkt.acc.tmpsecret = params.secret;
if (Array.isArray(params.attachments) && params.attachments.length > 0) {
pkt.extra = {
attachments: params.attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.isUrlRelative)(ref))
attachments: params.attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.isUrlRelative)(ref))
};
}
}
@@ -6531,7 +6899,7 @@ class Tinode {
}
if (Array.isArray(setParams.attachments) && setParams.attachments.length > 0) {
pkt.extra = {
attachments: setParams.attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.isUrlRelative)(ref))
attachments: setParams.attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.isUrlRelative)(ref))
};
}
if (setParams.tags) {
@@ -6574,7 +6942,7 @@ class Tinode {
};
if (attachments) {
msg.extra = {
attachments: attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.isUrlRelative)(ref))
attachments: attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.isUrlRelative)(ref))
};
}
return this.#send(msg, pub.id);
@@ -6647,7 +7015,7 @@ class Tinode {
}
getMeta(topic, params) {
const pkt = this.#initPacket('get', topic);
pkt.get = (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.mergeObj)(pkt.get, params);
pkt.get = (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.mergeObj)(pkt.get, params);
return this.#send(pkt, pkt.get.id);
}
setMeta(topic, params) {
@@ -6662,7 +7030,7 @@ class Tinode {
});
if (Array.isArray(params.attachments) && params.attachments.length > 0) {
pkt.extra = {
attachments: params.attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_11__.isUrlRelative)(ref))
attachments: params.attachments.filter(ref => (0,_utils_js__WEBPACK_IMPORTED_MODULE_12__.isUrlRelative)(ref))
};
}
}
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