split out a few modules, handle esc key
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
"use strict";(self.webpackChunktinode_webapp=self.webpackChunktinode_webapp||[]).push([[371],{6371:function(e,t,n){n.r(t);var i=n(1594),o=n.n(i),s=n(8181),r=n(1725),a=n(8589);const l=(0,s.defineMessages)({unrecognized_video_format:{id:"unrecognized_video_format",defaultMessage:[{type:0,value:"Format of this video is not recognized"}]}});class d extends o().PureComponent{constructor(e){super(e),this.videoRef=o().createRef(),this.handleSendVideo=this.handleSendVideo.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this)}componentDidMount(){document.addEventListener("keydown",this.handleKeyDown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleKeyDown)}handleKeyDown(e){this.props.onSendMessage||(e.preventDefault(),"Escape"===e.key&&this.props.onClose())}handleSendVideo(e){this.props.onClose();const t={width:this.videoRef.current.videoWidth,height:this.videoRef.current.videoHeight,duration:1e3*this.videoRef.current.duration|0,mime:this.props.content.mime,name:this.props.content.filename};if(0==t.width||0==t.height)return void this.props.onError(this.props.intl.formatMessage(l.unrecognized_video_format),"err");const n=document.createElement("canvas");n.width=t.width,n.height=t.height;const i=n.getContext("2d");i.drawImage(this.videoRef.current,0,0,n.width,n.height),i.canvas.toBlob(n=>this.props.onSendMessage(e,this.props.content.blob,n,t),"image/jpeg",.75)}render(){if(!this.props.content)return null;const e=this.props.content.width||"-",t=this.props.content.height||"-",n=this.props.onSendMessage?"nodownload":"",i=!this.props.onSendMessage;return o().createElement("div",{id:"image-preview"},o().createElement("div",{id:"preview-caption-panel"},o().createElement("span",null,this.props.content.filename),o().createElement("a",{href:"#",onClick:e=>{e.preventDefault(),this.props.onClose()}},o().createElement("i",{className:"material-icons gray"},"close"))),o().createElement("div",{id:"image-preview-container"},o().createElement("video",{className:"image-preview",controls:!0,controlsList:n,disablePictureInPicture:!0,ref:this.videoRef,autoPlay:i,src:this.props.tinode.authorizeURL(this.props.content.url),poster:this.props.content.preview,alt:this.props.content.filename})),this.props.onSendMessage?o().createElement(r.A,{messagePrompt:"add_image_caption",acceptBlank:!0,tinode:this.props.tinode,reply:this.props.reply,onCancelReply:this.props.onCancelReply,onSendMessage:this.handleSendVideo,onError:this.props.onError}):o().createElement("div",{id:"image-preview-footer"},o().createElement("div",null,o().createElement("div",null,o().createElement("b",null,o().createElement(s.FormattedMessage,{id:"label_file_name",defaultMessage:[{type:0,value:"File name:"}]}))),o().createElement("div",null,o().createElement("span",{title:this.props.content.filename},this.props.content.filename))),o().createElement("div",null,o().createElement("div",null,o().createElement("b",null,o().createElement(s.FormattedMessage,{id:"label_content_type",defaultMessage:[{type:0,value:"Content type:"}]}))),o().createElement("div",null,this.props.content.type)),o().createElement("div",null,o().createElement("div",null,o().createElement("b",null,o().createElement(s.FormattedMessage,{id:"label_size",defaultMessage:[{type:0,value:"Size:"}]}))),o().createElement("div",null,e," × ",t," px; ",(0,a.lX)(this.props.content.size)))))}}t.default=(0,s.injectIntl)(d)}}]);
|
||||
//# sourceMappingURL=371.prod.js.map
|
||||
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
@@ -0,0 +1,2 @@
|
||||
"use strict";(self.webpackChunktinode_webapp=self.webpackChunktinode_webapp||[]).push([[472],{4472:function(e,t,n){n.r(t),n.d(t,{default:function(){return r}});var a=n(1594),l=n.n(a),s=n(8181),o=n(1725),i=n(8589);class r extends l().PureComponent{constructor(e){super(e),this.handleSendDoc=this.handleSendDoc.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this)}componentDidMount(){document.addEventListener("keydown",this.handleKeyDown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleKeyDown)}handleKeyDown(e){e.preventDefault(),"Escape"===e.key&&this.props.onClose()}handleSendDoc(e){this.props.onClose(),this.props.onSendMessage(this.props.content.file)}render(){return this.props.content?l().createElement("div",{id:"image-preview"},l().createElement("div",{id:"preview-caption-panel"},l().createElement("span",null,this.props.content.name),l().createElement("a",{href:"#",onClick:e=>{e.preventDefault(),this.props.onClose()}},l().createElement("i",{className:"material-icons gray"},"close"))),l().createElement("div",{id:"image-preview-container"},l().createElement("div",{className:"doc-card"},l().createElement("i",{className:"material-icons gray"},function(e){const t={default:"insert_drive_file",image:"image",text:"description",video:"theaters"};return t[e]||t[(e||"").split("/")[0]]||t.default}(this.props.content.type)),l().createElement("div",null,l().createElement("b",null,l().createElement(s.FormattedMessage,{id:"label_file_name",defaultMessage:[{type:0,value:"File name:"}]}))," ",(0,i.Tu)(this.props.content.name,24)||"-"),l().createElement("div",null,l().createElement("b",null,l().createElement(s.FormattedMessage,{id:"label_content_type",defaultMessage:[{type:0,value:"Content type:"}]}))," ",this.props.content.type||"application/octet-stream"),l().createElement("div",null,l().createElement("b",null,l().createElement(s.FormattedMessage,{id:"label_size",defaultMessage:[{type:0,value:"Size:"}]}))," ",(0,i.lX)(this.props.content.size)))),l().createElement(o.A,{noInput:!0,tinode:this.props.tinode,reply:this.props.reply,onCancelReply:this.props.onCancelReply,onSendMessage:this.handleSendDoc,onError:this.props.onError})):null}}}}]);
|
||||
//# sourceMappingURL=472.prod.js.map
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
"use strict";(self.webpackChunktinode_webapp=self.webpackChunktinode_webapp||[]).push([[889],{8889:function(e,t,n){n.r(t),n.d(t,{default:function(){return d}});var s=n(1594),i=n.n(s),a=n(8181),l=n(1725),o=n(9027),r=n(6110),p=n(8589),h=n(3540);class d extends i().PureComponent{constructor(e){super(e),this.state={width:0,height:0},this.handleSendImage=this.handleSendImage.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this)}componentDidMount(){document.addEventListener("keydown",this.handleKeyDown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleKeyDown)}handleKeyDown(e){this.props.onSendMessage||(e.preventDefault(),"Escape"===e.key&&this.props.onClose())}assignWidth(e){if(e&&!this.state.width){const t=e.getBoundingClientRect();this.setState({width:0|t.width,height:0|t.height})}}handleSendImage(e){this.props.onClose(),this.props.onSendMessage(e,this.props.content.blob)}render(){if(!this.props.content)return null;const e=(0,r.ae)(this.props.content.width,this.props.content.height,this.state.width,this.state.height,!1),t=e?{width:e.dstWidth+"px",height:e.dstHeight+"px"}:this.props.content.width>this.props.content.height?{width:"100%"}:{height:"100%"};t.maxWidth="100%",t.maxHeight="100%";const n=Math.max((this.state.width/o.JS/1.5|0)-2,12),s=(0,p.Tu)(this.props.content.filename,n)||"-",d=this.props.content.width||"-",c=this.props.content.height||"-",m=(0,h.FH)(this.props.content.url);return i().createElement("div",{id:"image-preview"},i().createElement("div",{id:"preview-caption-panel"},this.props.onSendMessage?i().createElement("span",null,s):i().createElement("a",{href:m,download:this.props.content.filename},i().createElement("i",{className:"material-icons"},"file_download")," ",i().createElement(a.FormattedMessage,{id:"download_action",defaultMessage:[{type:0,value:"download"}]})),i().createElement("a",{href:"#",onClick:e=>{e.preventDefault(),this.props.onClose()}},i().createElement("i",{className:"material-icons gray"},"close"))),i().createElement("div",{id:"image-preview-container",ref:e=>this.assignWidth(e)},i().createElement("img",{src:this.props.content.url,style:t,className:"image-preview",alt:this.props.content.filename})),this.props.onSendMessage?i().createElement(l.A,{messagePrompt:"add_image_caption",acceptBlank:!0,tinode:this.props.tinode,reply:this.props.reply,onCancelReply:this.props.onCancelReply,onSendMessage:this.handleSendImage,onError:this.props.onError}):i().createElement("div",{id:"image-preview-footer"},i().createElement("div",null,i().createElement("div",null,i().createElement("b",null,i().createElement(a.FormattedMessage,{id:"label_file_name",defaultMessage:[{type:0,value:"File name:"}]}))),i().createElement("div",null,i().createElement("span",{title:this.props.content.filename},s))),i().createElement("div",null,i().createElement("div",null,i().createElement("b",null,i().createElement(a.FormattedMessage,{id:"label_content_type",defaultMessage:[{type:0,value:"Content type:"}]}))),i().createElement("div",null,this.props.content.type)),i().createElement("div",null,i().createElement("div",null,i().createElement("b",null,i().createElement(a.FormattedMessage,{id:"label_size",defaultMessage:[{type:0,value:"Size:"}]}))),i().createElement("div",null,d," × ",c," px; ",(0,p.lX)(this.props.content.size)))))}}}}]);
|
||||
//# sourceMappingURL=889.prod.js.map
|
||||
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
@@ -0,0 +1,2 @@
|
||||
"use strict";(self.webpackChunktinode_webapp=self.webpackChunktinode_webapp||[]).push([[942],{8942:function(e,t,a){a.r(t),a.d(t,{default:function(){return p}});var n=a(1594),l=a.n(n),s=a(8181),r=a(8892),o=a(1725),c=a(6904),d=a(4532),i=a(6110);const m=(0,s.defineMessages)({unknown_name:{id:"unknown_name",defaultMessage:[{type:0,value:"Unknown"}]}});class p extends l().PureComponent{constructor(e){super(e),this.state={content:e.content.object},e.content.object||(0,i.QJ)(e.content.file).then(e=>{this.setState({content:e})}),this.handleSendCard=this.handleSendCard.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this)}componentDidMount(){document.addEventListener("keydown",this.handleKeyDown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleKeyDown)}handleKeyDown(e){e.preventDefault(),"Escape"===e.key&&this.props.onClose()}handleSendCard(){this.props.onClose(),this.props.onSendMessage(this.props.content)}render(){const e=this.state.content,t=d.TheCard.getFn(e),a=d.TheCard.getPhotoUrl(e)||null,n=e?.note,i=d.TheCard.getOrg(e),p=d.TheCard.getComm(e,"email")||[],u=d.TheCard.getComm(e,"tel")||[],h=d.TheCard.getComm(e,"tinode")||[],E=d.TheCard.getComm(e,"http")||[],v=[];return p.forEach((e,t)=>{const a=Array.isArray(e.des)?e.des.join(", "):"";v.push(l().createElement("div",{className:"group quoted",key:`email-${t}`},l().createElement("tt",null,e.value),l().createElement("span",{className:"small gray"}," ",a?`(${a})`:"")))}),u.forEach((e,t)=>{let a=e.value;try{const t=(0,r.L)(e.value);a=t?t.formatInternational():e.value}catch(e){}const n=Array.isArray(e.des)?e.des.join(", "):"";v.push(l().createElement("div",{className:"group quoted",key:`tel-${t}`},l().createElement("tt",null,a),l().createElement("span",{className:"small gray"}," ",n?`(${n})`:"")))}),h.forEach((e,t)=>{const a=Array.isArray(e.des)?e.des.join(", "):"";v.push(l().createElement("div",{className:"group quoted",key:`tinode-${t}`},l().createElement("tt",null,e.value),l().createElement("span",{className:"small gray"}," ",a?`(${a})`:"")))}),E.forEach((e,t)=>{const a=Array.isArray(e.des)?e.des.join(", "):"";v.push(l().createElement("div",{className:"group quoted",key:`url-${t}`},l().createElement("tt",null,e.value),l().createElement("span",{className:"small gray"}," ",a?`(${a})`:"")))}),l().createElement("div",{id:"image-preview"},l().createElement("div",{id:"preview-caption-panel"},l().createElement("span",null,l().createElement(s.FormattedMessage,{id:"label_contact_card",defaultMessage:[{type:0,value:"Contact Card"}]})),l().createElement("a",{href:"#",onClick:e=>{e.preventDefault(),this.props.onClose()}},l().createElement("i",{className:"material-icons gray"},"close"))),l().createElement("div",{id:"image-preview-container"},l().createElement("div",{className:"doc-card"},l().createElement("div",{className:"panel-form-column"},l().createElement("center",null,l().createElement("div",{className:"avatar-box"},l().createElement(c.A,{authorizeURL:this.props.tinode.authorizeURL,avatar:a||!0,topic:"usrXXX123",title:t}))),l().createElement("div",{className:"group"},l().createElement("div",{className:"large ellipsized"},t||l().createElement(s.FormattedMessage,m.unknown_name))),i?l().createElement("div",{className:"group"},l().createElement("div",null,i)):null,n?l().createElement("div",{className:"group"},l().createElement("label",{className:"small"},l().createElement(s.FormattedMessage,{id:"label_description",defaultMessage:[{type:0,value:"Description"}]})),l().createElement("div",{className:"quoted"},n)):null,v.length>0?l().createElement("div",{className:"group"},l().createElement("label",{className:"small"},l().createElement(s.FormattedMessage,{id:"label_contacts",defaultMessage:[{type:0,value:"Contacts"}]})),v):null))),l().createElement(o.A,{noInput:!0,tinode:this.props.tinode,reply:this.props.reply,onCancelReply:this.props.onCancelReply,onSendMessage:this.handleSendCard,onError:this.props.onError}))}}}}]);
|
||||
//# sourceMappingURL=942.prod.js.map
|
||||
File diff suppressed because one or more lines are too long
+137
-8312
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -27,9 +27,7 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony import */ var _widgets_topic_security_jsx__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../widgets/topic-security.jsx */ "./src/widgets/topic-security.jsx");
|
||||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||
|
||||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||
|
||||
|
||||
|
||||
@@ -198,6 +196,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);
|
||||
this.handleShowQRCode = this.handleShowQRCode.bind(this);
|
||||
this.handleCopyToClipboard = this.handleCopyToClipboard.bind(this);
|
||||
this.handleShare = this.handleShare.bind(this);
|
||||
this.handleShowAddMembers = this.handleShowAddMembers.bind(this);
|
||||
this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);
|
||||
this.handleMemberSelected = this.handleMemberSelected.bind(this);
|
||||
@@ -279,11 +278,11 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
sharer: acs && acs.isSharer(),
|
||||
deleter: acs && acs.isDeleter(),
|
||||
muted: acs && acs.isMuted(),
|
||||
fullName: isSelf ? props.intl.formatMessage(messages.self_topic_name) : (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.truncateString)(topic.public && topic.public.fn, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
||||
description: isSelf ? props.intl.formatMessage(messages.self_topic_comment) : (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.truncateString)(topic.public && topic.public.note, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TOPIC_DESCRIPTION_LENGTH),
|
||||
fullName: isSelf ? props.intl.formatMessage(messages.self_topic_name) : (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_16__.truncateString)(topic.public && topic.public.fn, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
||||
description: isSelf ? props.intl.formatMessage(messages.self_topic_comment) : (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_16__.truncateString)(topic.public && topic.public.note, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TOPIC_DESCRIPTION_LENGTH),
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(topic.public ? topic.public.photo : null),
|
||||
trustedBadges: badges,
|
||||
private: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.truncateString)(topic.private && topic.private.comment, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
||||
private: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_16__.truncateString)(topic.private && topic.private.comment, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
||||
archived: topic.isArchived(),
|
||||
address: topic.name,
|
||||
groupTopic: topic.isGroupType(),
|
||||
@@ -329,7 +328,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
this.setState({
|
||||
avatar: img
|
||||
});
|
||||
this.props.onTopicDescUpdate(this.props.topic, (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.theCard)(null, img || tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.DEL_CHAR), null);
|
||||
this.props.onTopicDescUpdate(this.props.topic, new tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard(null, img || tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.DEL_CHAR), null);
|
||||
}
|
||||
handleMuted(ignored, checked) {
|
||||
this.setState({
|
||||
@@ -457,6 +456,12 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
}, _config_js__WEBPACK_IMPORTED_MODULE_14__.TOAST_DURATION);
|
||||
});
|
||||
}
|
||||
handleShare(e) {
|
||||
e.preventDefault();
|
||||
const card = new tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard(this.state.fullName, this.state.avatar, null, this.state.description);
|
||||
tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.setTinodeID(card, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.URI_TOPIC_ID_PREFIX + this.props.topic);
|
||||
this.props.onShareTheCard(card);
|
||||
}
|
||||
handleShowAddMembers(e) {
|
||||
e.preventDefault();
|
||||
this.props.onInitFind();
|
||||
@@ -677,7 +682,12 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
onClick: this.handleShowQRCode
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, "qr_code")), "\xA0")), alias ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
}, "qr_code")), "\xA0 \xA0", react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||
href: "#",
|
||||
onClick: this.handleShare
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, "share")), "\xA0")), alias ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "panel-form-row"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
||||
className: "small"
|
||||
@@ -1376,7 +1386,7 @@ class ShowQRCode extends (react__WEBPACK_IMPORTED_MODULE_1___default().PureCompo
|
||||
}, react__WEBPACK_IMPORTED_MODULE_1___default().createElement("br", null), react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", {
|
||||
className: "qr-code",
|
||||
ref: this.qrCodeRef
|
||||
}), react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", {
|
||||
}), react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tt", null, this.props.uri)), react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", {
|
||||
className: "dialog-buttons"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", {
|
||||
className: "outline",
|
||||
|
||||
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
@@ -0,0 +1,663 @@
|
||||
"use strict";
|
||||
(self["webpackChunktinode_webapp"] = self["webpackChunktinode_webapp"] || []).push([["src_widgets_call-panel_jsx"],{
|
||||
|
||||
/***/ "./src/widgets/call-panel.jsx":
|
||||
/*!************************************!*\
|
||||
!*** ./src/widgets/call-panel.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 _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
|
||||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||||
/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../constants.js */ "./src/constants.js");
|
||||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const RING_SOUND = new Audio('audio/call-out.m4a');
|
||||
RING_SOUND.loop = true;
|
||||
const CALL_ENDED_SOUND = new Audio('audio/call-end.m4a');
|
||||
CALL_ENDED_SOUND.loop = true;
|
||||
const DIALING_SOUND = new Audio('audio/dialing.m4a');
|
||||
const VIDEO_MUTED_EVENT = 'video:muted';
|
||||
const VIDEO_UNMUTED_EVENT = 'video:unmuted';
|
||||
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
|
||||
already_in_call: {
|
||||
id: "already_in_call",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "You already in an ongoing call!"
|
||||
}]
|
||||
}
|
||||
});
|
||||
class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
localStream: undefined,
|
||||
remoteStream: undefined,
|
||||
pc: undefined,
|
||||
dataChannel: undefined,
|
||||
previousOnInfo: undefined,
|
||||
waitingForPeer: false,
|
||||
callInitialSetupComplete: false,
|
||||
audioOnly: props.callAudioOnly,
|
||||
videoToggleInProgress: false,
|
||||
remoteVideoLive: false
|
||||
};
|
||||
this.localStreamConstraints = {
|
||||
audio: true,
|
||||
video: !props.callAudioOnly
|
||||
};
|
||||
this.isOutgoingCall = props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_OUTGOING_INITATED;
|
||||
this.containerRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||||
this.localRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||||
this.remoteRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||||
this.remoteIceCandidatesCache = [];
|
||||
this.onInfo = this.onInfo.bind(this);
|
||||
this.start = this.start.bind(this);
|
||||
this.stop = this.stop.bind(this);
|
||||
this.createPeerConnection = this.createPeerConnection.bind(this);
|
||||
this.canSendOffer = this.canSendOffer.bind(this);
|
||||
this.drainRemoteIceCandidatesCache = this.drainRemoteIceCandidatesCache.bind(this);
|
||||
this.handleNegotiationNeededEvent = this.handleNegotiationNeededEvent.bind(this);
|
||||
this.handleICECandidateEvent = this.handleICECandidateEvent.bind(this);
|
||||
this.handleNewICECandidateMsg = this.handleNewICECandidateMsg.bind(this);
|
||||
this.handleICEConnectionStateChangeEvent = this.handleICEConnectionStateChangeEvent.bind(this);
|
||||
this.handleSignalingStateChangeEvent = this.handleSignalingStateChangeEvent.bind(this);
|
||||
this.handleICEGatheringStateChangeEvent = this.handleICEGatheringStateChangeEvent.bind(this);
|
||||
this.handleIceCandidateErrorEvent = this.handleIceCandidateErrorEvent.bind(this);
|
||||
this.handleTrackEvent = this.handleTrackEvent.bind(this);
|
||||
this.handleVideoOfferMsg = this.handleVideoOfferMsg.bind(this);
|
||||
this.handleVideoAnswerMsg = this.handleVideoAnswerMsg.bind(this);
|
||||
this.handleNewICECandidateMsg = this.handleNewICECandidateMsg.bind(this);
|
||||
this.reportError = this.reportError.bind(this);
|
||||
this.handleGetUserMediaError = this.handleGetUserMediaError.bind(this);
|
||||
this.stopTracks = this.stopTracks.bind(this);
|
||||
this.disconnectMedia = this.disconnectMedia.bind(this);
|
||||
this.handleCloseClick = this.handleCloseClick.bind(this);
|
||||
this.handleToggleCameraClick = this.handleToggleCameraClick.bind(this);
|
||||
this.handleToggleMicClick = this.handleToggleMicClick.bind(this);
|
||||
this.handleRemoteHangup = this.handleRemoteHangup.bind(this);
|
||||
this.handleVideoCallAccepted = this.handleVideoCallAccepted.bind(this);
|
||||
this.muteVideo = this.muteVideo.bind(this);
|
||||
this.unmuteVideo = this.unmuteVideo.bind(this);
|
||||
this.emptyVideoTrack = this.emptyVideoTrack.bind(this);
|
||||
this.handleDataChannelEvent = this.handleDataChannelEvent.bind(this);
|
||||
this.handleDataChannelError = this.handleDataChannelError.bind(this);
|
||||
this.handleDataChannelMessage = this.handleDataChannelMessage.bind(this);
|
||||
this.handleDataChannelOpen = this.handleDataChannelOpen.bind(this);
|
||||
this.handleDataChannelClose = this.handleDataChannelClose.bind(this);
|
||||
this.bounds = {};
|
||||
this.dragStart = event => {
|
||||
if (!event.target.classList.contains('draggable')) {
|
||||
return;
|
||||
}
|
||||
if (window.matchMedia('(width<=640px)')) {
|
||||
return;
|
||||
}
|
||||
if (this.props.minimized && this.containerRef) {
|
||||
event.stopPropagation();
|
||||
this.containerRef.current.setPointerCapture(event.pointerId);
|
||||
this.containerRef.current.style.cursor = 'grabbing';
|
||||
this.bounds.left = this.containerRef.current.offsetLeft;
|
||||
this.bounds.top = this.containerRef.current.offsetTop;
|
||||
this.bounds.width = this.containerRef.current.offsetWidth;
|
||||
this.bounds.height = this.containerRef.current.offsetHeight;
|
||||
const parent = this.containerRef.current.offsetParent;
|
||||
this.bounds.maxX = parent.offsetWidth;
|
||||
this.bounds.maxY = parent.offsetHeight;
|
||||
}
|
||||
};
|
||||
this.drag = event => {
|
||||
event.stopPropagation();
|
||||
const el = this.containerRef.current;
|
||||
if (this.props.minimized && this.containerRef && el.hasPointerCapture(event.pointerId)) {
|
||||
this.bounds.left = Math.min(Math.max(this.bounds.left + event.movementX, 0), this.bounds.maxX - this.bounds.width);
|
||||
this.bounds.top = Math.min(Math.max(this.bounds.top + event.movementY, 0), this.bounds.maxY - this.bounds.height);
|
||||
el.style.left = `${this.bounds.left}px`;
|
||||
el.style.top = `${this.bounds.top}px`;
|
||||
}
|
||||
};
|
||||
this.dragEnd = event => {
|
||||
event.stopPropagation();
|
||||
if (this.props.minimized && this.containerRef) {
|
||||
this.containerRef.current.releasePointerCapture(event.pointerId);
|
||||
this.containerRef.current.style.cursor = 'grab';
|
||||
}
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
const topic = this.props.tinode.getTopic(this.props.topic);
|
||||
this.previousOnInfo = topic.onInfo;
|
||||
topic.onInfo = this.onInfo;
|
||||
if ((this.props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_OUTGOING_INITATED || this.props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_IN_PROGRESS) && this.localRef.current) {
|
||||
this.start();
|
||||
}
|
||||
if (this.containerRef) {
|
||||
this.containerRef.current.addEventListener('pointerdown', this.dragStart);
|
||||
this.containerRef.current.addEventListener('pointermove', this.drag);
|
||||
this.containerRef.current.addEventListener('pointerup', this.dragEnd);
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
const topic = this.props.tinode.getTopic(this.props.topic);
|
||||
topic.onInfo = this.previousOnInfo;
|
||||
this.stop();
|
||||
if (this.containerRef) {
|
||||
this.containerRef.current.removeEventListener('pointerdown', this.dragStart);
|
||||
this.containerRef.current.removeEventListener('pointermove', this.drag);
|
||||
this.containerRef.current.removeEventListener('pointerup', this.dragEnd);
|
||||
}
|
||||
}
|
||||
handleVideoCallAccepted(info) {
|
||||
RING_SOUND.pause();
|
||||
const pc = this.createPeerConnection(true);
|
||||
const stream = this.state.localStream;
|
||||
stream.getTracks().forEach(track => {
|
||||
pc.addTrack(track, stream);
|
||||
if (track.kind == 'video' && this.state.audioOnly) {
|
||||
track.enabled = false;
|
||||
track.stop();
|
||||
stream.removeTrack(track);
|
||||
}
|
||||
});
|
||||
}
|
||||
onInfo(info) {
|
||||
if (info.what != 'call') {
|
||||
return;
|
||||
}
|
||||
switch (info.event) {
|
||||
case 'accept':
|
||||
this.handleVideoCallAccepted(info);
|
||||
break;
|
||||
case 'answer':
|
||||
this.handleVideoAnswerMsg(info);
|
||||
break;
|
||||
case 'ice-candidate':
|
||||
this.handleNewICECandidateMsg(info);
|
||||
break;
|
||||
case 'hang-up':
|
||||
this.handleRemoteHangup(info);
|
||||
break;
|
||||
case 'offer':
|
||||
this.handleVideoOfferMsg(info);
|
||||
break;
|
||||
case 'ringing':
|
||||
RING_SOUND.play().catch(_ => {});
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown call event", info.event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
emptyVideoTrack() {
|
||||
const width = 640;
|
||||
const height = 480;
|
||||
const canvas = Object.assign(document.createElement("canvas"), {
|
||||
width,
|
||||
height
|
||||
});
|
||||
canvas.getContext('2d').fillRect(0, 0, width, height);
|
||||
const stream = canvas.captureStream(0);
|
||||
return Object.assign(stream.getVideoTracks()[0], {
|
||||
enabled: false
|
||||
});
|
||||
}
|
||||
start() {
|
||||
if (this.state.localStream) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.already_in_call), 'info');
|
||||
return;
|
||||
}
|
||||
if (this.props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_IN_PROGRESS) {
|
||||
this.props.onInvite(this.props.topic, this.props.seq, _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_IN_PROGRESS, this.props.callAudioOnly);
|
||||
return;
|
||||
}
|
||||
navigator.mediaDevices.getUserMedia(this.localStreamConstraints).then(stream => {
|
||||
if (!this.localStreamConstraints.video) {
|
||||
stream.addTrack(this.emptyVideoTrack());
|
||||
}
|
||||
this.setState({
|
||||
localStream: stream,
|
||||
waitingForPeer: true
|
||||
});
|
||||
this.localRef.current.srcObject = stream;
|
||||
DIALING_SOUND.play();
|
||||
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState, this.props.callAudioOnly);
|
||||
}).catch(this.handleGetUserMediaError);
|
||||
}
|
||||
stop() {
|
||||
CALL_ENDED_SOUND.pause();
|
||||
CALL_ENDED_SOUND.currentTime = 0;
|
||||
RING_SOUND.pause();
|
||||
RING_SOUND.currentTime = 0;
|
||||
this.stopTracks(this.state.localStream);
|
||||
this.stopTracks(this.state.remoteStream);
|
||||
this.disconnectMedia(this.localRef.current);
|
||||
this.disconnectMedia(this.remoteRef.current);
|
||||
if (this.state.pc) {
|
||||
this.state.pc.ontrack = null;
|
||||
this.state.pc.onremovetrack = null;
|
||||
this.state.pc.onremovestream = null;
|
||||
this.state.pc.onicecandidate = null;
|
||||
this.state.pc.oniceconnectionstatechange = null;
|
||||
this.state.pc.onsignalingstatechange = null;
|
||||
this.state.pc.onicegatheringstatechange = null;
|
||||
this.state.pc.onnegotiationneeded = null;
|
||||
this.state.pc.onicecandidateerror = null;
|
||||
this.state.pc.ondatachannel = null;
|
||||
if (this.state.dataChannel && (this.state.dataChannel.readyState == 'open' || this.state.dataChannel.readyState == 'connecting')) {
|
||||
this.state.dataChannel.close();
|
||||
}
|
||||
this.state.pc.close();
|
||||
}
|
||||
this.setState({
|
||||
pc: null,
|
||||
waitingForPeer: false
|
||||
});
|
||||
}
|
||||
disconnectMedia(el) {
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.srcObject = null;
|
||||
el.src = '';
|
||||
}
|
||||
stopTracks(stream) {
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
let tracks = stream.getTracks();
|
||||
if (tracks) {
|
||||
tracks.forEach(track => {
|
||||
track.stop();
|
||||
track.enabled = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
handleDataChannelError(error) {
|
||||
console.error('data channel error', error);
|
||||
}
|
||||
handleDataChannelMessage(event) {
|
||||
switch (event.data) {
|
||||
case VIDEO_MUTED_EVENT:
|
||||
this.setState({
|
||||
remoteVideoLive: false
|
||||
}, _ => {
|
||||
this.remoteRef.current.srcObject = this.state.remoteStream;
|
||||
});
|
||||
break;
|
||||
case VIDEO_UNMUTED_EVENT:
|
||||
this.setState({
|
||||
remoteVideoLive: true
|
||||
}, _ => {
|
||||
this.remoteRef.current.srcObject = this.state.remoteStream;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
handleDataChannelOpen(event) {
|
||||
if (!this.state.audioOnly) {
|
||||
event.target.send(VIDEO_UNMUTED_EVENT);
|
||||
}
|
||||
}
|
||||
handleDataChannelClose(event) {
|
||||
console.log('close data channel:', event);
|
||||
}
|
||||
handleDataChannelEvent(event) {
|
||||
console.log('data channel event:', event);
|
||||
const channel = event.channel;
|
||||
channel.onerror = this.handleDataChannelError;
|
||||
channel.onmessage = this.handleDataChannelMessage;
|
||||
channel.onopen = this.handleDataChannelOpen;
|
||||
channel.onclose = this.handleDataChannelClose;
|
||||
this.setState({
|
||||
dataChannel: channel
|
||||
});
|
||||
}
|
||||
createPeerConnection(withDataChannel) {
|
||||
const iceServers = this.props.tinode.getServerParam('iceServers', null);
|
||||
const pc = iceServers ? new RTCPeerConnection({
|
||||
iceServers: iceServers
|
||||
}) : new RTCPeerConnection();
|
||||
pc.onicecandidate = this.handleICECandidateEvent;
|
||||
pc.oniceconnectionstatechange = this.handleICEConnectionStateChangeEvent;
|
||||
pc.onicegatheringstatechange = this.handleICEGatheringStateChangeEvent;
|
||||
pc.onsignalingstatechange = this.handleSignalingStateChangeEvent;
|
||||
pc.onnegotiationneeded = this.handleNegotiationNeededEvent;
|
||||
pc.onicecandidateerror = this.handleIceCandidateErrorEvent;
|
||||
pc.ontrack = this.handleTrackEvent;
|
||||
pc.ondatachannel = this.handleDataChannelEvent;
|
||||
let stateUpdate = {
|
||||
pc: pc,
|
||||
waitingForPeer: false
|
||||
};
|
||||
if (withDataChannel) {
|
||||
const channel = pc.createDataChannel("events", {
|
||||
ordered: true
|
||||
});
|
||||
channel.onerror = this.handleDataChannelError;
|
||||
channel.onmessage = this.handleDataChannelMessage;
|
||||
channel.onopen = this.handleDataChannelOpen;
|
||||
channel.onclose = this.handleDataChannelClose;
|
||||
stateUpdate.dataChannel = channel;
|
||||
}
|
||||
this.setState(stateUpdate);
|
||||
return pc;
|
||||
}
|
||||
handleVideoAnswerMsg(info) {
|
||||
const desc = new RTCSessionDescription(info.payload);
|
||||
this.state.pc.setRemoteDescription(desc).then(_ => {
|
||||
this.setState({
|
||||
callInitialSetupComplete: true
|
||||
}, _ => this.drainRemoteIceCandidatesCache());
|
||||
}).catch(err => this.reportError(err));
|
||||
}
|
||||
reportError(err) {
|
||||
this.props.onError(err.message, 'err');
|
||||
}
|
||||
canSendOffer() {
|
||||
return this.isOutgoingCall || this.state.callInitialSetupComplete;
|
||||
}
|
||||
handleNegotiationNeededEvent(event) {
|
||||
const pc = event.target;
|
||||
if (!this.canSendOffer()) {
|
||||
return;
|
||||
}
|
||||
pc.createOffer().then(offer => {
|
||||
return pc.setLocalDescription(offer);
|
||||
}).then(_ => {
|
||||
this.props.onSendOffer(this.props.topic, this.props.seq, pc.localDescription.toJSON());
|
||||
}).catch(err => this.reportError(err));
|
||||
}
|
||||
handleIceCandidateErrorEvent(event) {
|
||||
console.warn("ICE candidate error:", event);
|
||||
}
|
||||
handleICECandidateEvent(event) {
|
||||
if (event.candidate) {
|
||||
this.props.onIceCandidate(this.props.topic, this.props.seq, event.candidate.toJSON());
|
||||
}
|
||||
}
|
||||
handleNewICECandidateMsg(info) {
|
||||
const candidate = new RTCIceCandidate(info.payload);
|
||||
if (this.state.callInitialSetupComplete) {
|
||||
this.state.pc.addIceCandidate(candidate).catch(err => {
|
||||
if (candidate.candidate) {
|
||||
this.reportError(err);
|
||||
}
|
||||
console.warn("Error adding new ice candidate", candidate, err);
|
||||
});
|
||||
} else {
|
||||
this.remoteIceCandidatesCache.push(candidate);
|
||||
}
|
||||
}
|
||||
drainRemoteIceCandidatesCache() {
|
||||
this.remoteIceCandidatesCache.forEach(candidate => {
|
||||
this.state.pc.addIceCandidate(candidate).catch(err => {
|
||||
if (candidate.candidate) {
|
||||
this.reportError(err);
|
||||
}
|
||||
console.warn("Error adding cached ice candidate", candidate, err);
|
||||
});
|
||||
});
|
||||
this.remoteIceCandidatesCache = [];
|
||||
}
|
||||
handleICEConnectionStateChangeEvent(event) {
|
||||
switch (event.target.iceConnectionState) {
|
||||
case 'closed':
|
||||
case 'failed':
|
||||
this.handleCloseClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
handleSignalingStateChangeEvent(event) {
|
||||
if (event.target.signalingState == 'closed') {
|
||||
this.handleCloseClick();
|
||||
}
|
||||
}
|
||||
handleICEGatheringStateChangeEvent(event) {}
|
||||
handleTrackEvent(event) {
|
||||
this.remoteRef.current.srcObject = event.streams[0];
|
||||
this.setState({
|
||||
remoteStream: event.streams[0]
|
||||
});
|
||||
}
|
||||
handleGetUserMediaError(e) {
|
||||
console.error("Error opening camera and/or microphone", e);
|
||||
switch (e.name) {
|
||||
case 'NotFoundError':
|
||||
this.reportError(e);
|
||||
break;
|
||||
case 'SecurityError':
|
||||
case 'PermissionDeniedError':
|
||||
break;
|
||||
default:
|
||||
this.reportError(e);
|
||||
break;
|
||||
}
|
||||
this.handleCloseClick();
|
||||
}
|
||||
handleVideoOfferMsg(info) {
|
||||
let localStream = null;
|
||||
const pc = this.state.pc ? this.state.pc : this.createPeerConnection(false);
|
||||
const desc = new RTCSessionDescription(info.payload);
|
||||
pc.setRemoteDescription(desc).then(_ => {
|
||||
return navigator.mediaDevices.getUserMedia(this.localStreamConstraints);
|
||||
}).then(stream => {
|
||||
let dummyVideo;
|
||||
if (!this.localStreamConstraints.video) {
|
||||
dummyVideo = this.emptyVideoTrack();
|
||||
stream.addTrack(dummyVideo);
|
||||
}
|
||||
localStream = stream;
|
||||
this.localRef.current.srcObject = stream;
|
||||
this.setState({
|
||||
localStream: stream
|
||||
});
|
||||
localStream.getTracks().forEach(track => {
|
||||
pc.addTrack(track, localStream);
|
||||
});
|
||||
if (dummyVideo) {
|
||||
dummyVideo.enabled = false;
|
||||
dummyVideo.stop();
|
||||
stream.removeTrack(dummyVideo);
|
||||
}
|
||||
}).then(_ => {
|
||||
return pc.createAnswer();
|
||||
}).then(answer => {
|
||||
return pc.setLocalDescription(answer);
|
||||
}).then(_ => {
|
||||
this.props.onSendAnswer(this.props.topic, this.props.seq, pc.localDescription.toJSON());
|
||||
this.setState({
|
||||
callInitialSetupComplete: true
|
||||
}, _ => this.drainRemoteIceCandidatesCache());
|
||||
}).catch(this.handleGetUserMediaError);
|
||||
}
|
||||
handleRemoteHangup() {
|
||||
if (!this.state.waitingForPeer) {
|
||||
this.handleCloseClick();
|
||||
} else {
|
||||
this.setState({
|
||||
waitingForPeer: false
|
||||
});
|
||||
RING_SOUND.pause();
|
||||
RING_SOUND.currentTime = 0;
|
||||
CALL_ENDED_SOUND.loop = true;
|
||||
CALL_ENDED_SOUND.play().catch(_ => {});
|
||||
setTimeout(_ => {
|
||||
this.handleCloseClick();
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
handleCloseClick() {
|
||||
this.stop();
|
||||
this.props.onHangup(this.props.topic, this.props.seq);
|
||||
}
|
||||
muteVideo() {
|
||||
if (!this.state.pc || !this.state.dataChannel) {
|
||||
return;
|
||||
}
|
||||
const stream = this.state.localStream;
|
||||
const t = stream.getVideoTracks()[0];
|
||||
t.enabled = false;
|
||||
t.stop();
|
||||
stream.removeTrack(t);
|
||||
this.state.dataChannel.send(VIDEO_MUTED_EVENT);
|
||||
this.setState({
|
||||
videoToggleInProgress: false
|
||||
});
|
||||
}
|
||||
unmuteVideo() {
|
||||
if (!this.state.pc || !this.state.dataChannel) {
|
||||
return;
|
||||
}
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
video: true
|
||||
}).then(stream => {
|
||||
this.localRef.current.srcObject = null;
|
||||
const sender = this.state.pc.getSenders().find(s => s.track.kind == 'video');
|
||||
const track = stream.getVideoTracks()[0];
|
||||
stream.removeTrack(track);
|
||||
this.state.localStream.addTrack(track);
|
||||
return sender.replaceTrack(track);
|
||||
}).then(_ => {
|
||||
this.localRef.current.srcObject = this.state.localStream;
|
||||
this.state.dataChannel.send(VIDEO_UNMUTED_EVENT);
|
||||
}).catch(err => this.handleGetUserMediaError(err)).finally(_ => {
|
||||
this.setState({
|
||||
videoToggleInProgress: false
|
||||
});
|
||||
});
|
||||
}
|
||||
handleToggleCameraClick() {
|
||||
if (this.state.videoToggleInProgress) {
|
||||
return;
|
||||
}
|
||||
const tracks = this.state.localStream.getVideoTracks();
|
||||
this.setState({
|
||||
videoToggleInProgress: true
|
||||
}, _ => {
|
||||
if (tracks && tracks.length > 0 && tracks[0].enabled && tracks[0].readyState == 'live') {
|
||||
this.muteVideo();
|
||||
} else {
|
||||
this.unmuteVideo();
|
||||
}
|
||||
this.setState({
|
||||
audioOnly: !this.state.audioOnly
|
||||
});
|
||||
});
|
||||
}
|
||||
handleToggleMicClick() {
|
||||
const stream = this.state.localStream;
|
||||
const t = stream.getAudioTracks()[0];
|
||||
t.enabled = !t.enabled;
|
||||
this.forceUpdate();
|
||||
}
|
||||
render() {
|
||||
const audioTracks = this.state.localStream && this.state.localStream.getAudioTracks();
|
||||
const videoTracks = !this.state.audioOnly && this.state.localStream && this.state.localStream.getVideoTracks();
|
||||
const disabled = !this.state.pc || !this.state.dataChannel || !(audioTracks && audioTracks[0]);
|
||||
const audioIcon = audioTracks && audioTracks[0] && audioTracks[0].enabled ? 'mic' : 'mic_off';
|
||||
const videoIcon = videoTracks && videoTracks[0] && videoTracks[0].enabled && videoTracks[0].readyState == 'live' ? 'videocam' : 'videocam_off';
|
||||
const peerTitle = (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_5__.truncateString)(this.props.title, _config_js__WEBPACK_IMPORTED_MODULE_3__.MAX_PEER_TITLE_LENGTH);
|
||||
const pulseAnimation = this.state.waitingForPeer ? ' pulse' : '';
|
||||
let remoteActive = false;
|
||||
if (this.remoteRef.current && this.remoteRef.current.srcObject && this.state.remoteVideoLive) {
|
||||
const rstream = this.remoteRef.current.srcObject;
|
||||
if (rstream.getVideoTracks().length > 0) {
|
||||
const t = rstream.getVideoTracks()[0];
|
||||
remoteActive = t.enabled && t.readyState == 'live';
|
||||
}
|
||||
}
|
||||
const minimizedClass = this.props.minimized ? 'minimized' : null;
|
||||
const fullScreen = this.props.minimized ? 'fullscreen' : 'fullscreen_exit';
|
||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "video-container",
|
||||
className: minimizedClass,
|
||||
ref: this.containerRef
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "video-container-panel"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "call-party self",
|
||||
disabled: this.state.audioOnly || this.props.minimized
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
|
||||
ref: this.localRef,
|
||||
autoPlay: true,
|
||||
muted: true,
|
||||
playsInline: true
|
||||
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "caller-name inactive"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "calls_you_label",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "You"
|
||||
}]
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "call-party peer",
|
||||
disabled: !remoteActive
|
||||
}, remoteActive ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
|
||||
ref: this.remoteRef,
|
||||
autoPlay: true,
|
||||
playsInline: true
|
||||
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "caller-name inactive"
|
||||
}, peerTitle)) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("audio", {
|
||||
ref: this.remoteRef,
|
||||
autoPlay: true
|
||||
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: `caller-card${pulseAnimation} draggable`
|
||||
}, 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"], {
|
||||
authorizeURL: this.props.tinode.authorizeURL,
|
||||
avatar: this.props.avatar,
|
||||
topic: this.props.topic,
|
||||
title: this.props.title
|
||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "caller-name"
|
||||
}, peerTitle))))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
className: "full-screen",
|
||||
onClick: this.props.onToggleMinimize
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, fullScreen)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "controls",
|
||||
className: minimizedClass
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
className: "danger",
|
||||
onClick: this.handleCloseClick
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, "call_end")), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
className: "secondary",
|
||||
onClick: this.handleToggleCameraClick,
|
||||
disabled: disabled
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, videoIcon)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
className: "secondary",
|
||||
onClick: this.handleToggleMicClick,
|
||||
disabled: disabled
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, audioIcon))));
|
||||
}
|
||||
}
|
||||
;
|
||||
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(CallPanel));
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=src_widgets_call-panel_jsx.dev.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,110 @@
|
||||
"use strict";
|
||||
(self["webpackChunktinode_webapp"] = self["webpackChunktinode_webapp"] || []).push([["src_widgets_doc-preview_jsx"],{
|
||||
|
||||
/***/ "./src/widgets/doc-preview.jsx":
|
||||
/*!*************************************!*\
|
||||
!*** ./src/widgets/doc-preview.jsx ***!
|
||||
\*************************************/
|
||||
/***/ (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 */ DocPreview; }
|
||||
/* harmony export */ });
|
||||
/* 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 _widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../widgets/send-message.jsx */ "./src/widgets/send-message.jsx");
|
||||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||
|
||||
|
||||
|
||||
|
||||
function iconFromMime(mime) {
|
||||
const mimeToIcon = {
|
||||
default: 'insert_drive_file',
|
||||
image: 'image',
|
||||
text: 'description',
|
||||
video: 'theaters'
|
||||
};
|
||||
return mimeToIcon[mime] || mimeToIcon[(mime || '').split('/')[0]] || mimeToIcon['default'];
|
||||
}
|
||||
class DocPreview extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSendDoc = this.handleSendDoc.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
handleKeyDown(e) {
|
||||
e.preventDefault();
|
||||
if (e.key === 'Escape') {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
handleSendDoc(caption) {
|
||||
this.props.onClose();
|
||||
this.props.onSendMessage(this.props.content.file);
|
||||
}
|
||||
render() {
|
||||
if (!this.props.content) {
|
||||
return null;
|
||||
}
|
||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "preview-caption-panel"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, this.props.content.name), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||
href: "#",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
this.props.onClose();
|
||||
}
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons gray"
|
||||
}, "close"))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview-container"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "doc-card"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons gray"
|
||||
}, iconFromMime(this.props.content.type)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_file_name",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "File name:"
|
||||
}]
|
||||
})), " ", (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__.shortenFileName)(this.props.content.name, 24) || '-'), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_content_type",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Content type:"
|
||||
}]
|
||||
})), " ", this.props.content.type || 'application/octet-stream'), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_size",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Size:"
|
||||
}]
|
||||
})), " ", (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__.bytesToHumanSize)(this.props.content.size)))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
noInput: true,
|
||||
tinode: this.props.tinode,
|
||||
reply: this.props.reply,
|
||||
onCancelReply: this.props.onCancelReply,
|
||||
onSendMessage: this.handleSendDoc,
|
||||
onError: this.props.onError
|
||||
}));
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=src_widgets_doc-preview_jsx.dev.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,157 @@
|
||||
"use strict";
|
||||
(self["webpackChunktinode_webapp"] = self["webpackChunktinode_webapp"] || []).push([["src_widgets_image-preview_jsx"],{
|
||||
|
||||
/***/ "./src/widgets/image-preview.jsx":
|
||||
/*!***************************************!*\
|
||||
!*** ./src/widgets/image-preview.jsx ***!
|
||||
\***************************************/
|
||||
/***/ (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 */ ImagePreview; }
|
||||
/* harmony export */ });
|
||||
/* 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 _send_message_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./send-message.jsx */ "./src/widgets/send-message.jsx");
|
||||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ImagePreview extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
this.handleSendImage = this.handleSendImage.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
handleKeyDown(e) {
|
||||
if (this.props.onSendMessage) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
if (e.key === 'Escape') {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
assignWidth(node) {
|
||||
if (node && !this.state.width) {
|
||||
const bounds = node.getBoundingClientRect();
|
||||
this.setState({
|
||||
width: bounds.width | 0,
|
||||
height: bounds.height | 0
|
||||
});
|
||||
}
|
||||
}
|
||||
handleSendImage(caption) {
|
||||
this.props.onClose();
|
||||
this.props.onSendMessage(caption, this.props.content.blob);
|
||||
}
|
||||
render() {
|
||||
if (!this.props.content) {
|
||||
return null;
|
||||
}
|
||||
const dim = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.fitImageSize)(this.props.content.width, this.props.content.height, this.state.width, this.state.height, false);
|
||||
const size = dim ? {
|
||||
width: dim.dstWidth + 'px',
|
||||
height: dim.dstHeight + 'px'
|
||||
} : this.props.content.width > this.props.content.height ? {
|
||||
width: '100%'
|
||||
} : {
|
||||
height: '100%'
|
||||
};
|
||||
size.maxWidth = '100%';
|
||||
size.maxHeight = '100%';
|
||||
const maxlength = Math.max((this.state.width / _config_js__WEBPACK_IMPORTED_MODULE_3__.REM_SIZE / 1.5 | 0) - 2, 12);
|
||||
const fname = (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_5__.shortenFileName)(this.props.content.filename, maxlength) || '-';
|
||||
const width = this.props.content.width || '-';
|
||||
const height = this.props.content.height || '-';
|
||||
const download_url = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_6__.urlAsAttachment)(this.props.content.url);
|
||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "preview-caption-panel"
|
||||
}, this.props.onSendMessage ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, fname) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||
href: download_url,
|
||||
download: this.props.content.filename
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, "file_download"), " ", react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "download_action",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "download"
|
||||
}]
|
||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||
href: "#",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
this.props.onClose();
|
||||
}
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons gray"
|
||||
}, "close"))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview-container",
|
||||
ref: node => this.assignWidth(node)
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
||||
src: this.props.content.url,
|
||||
style: size,
|
||||
className: "image-preview",
|
||||
alt: this.props.content.filename
|
||||
})), this.props.onSendMessage ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_send_message_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
messagePrompt: "add_image_caption",
|
||||
acceptBlank: true,
|
||||
tinode: this.props.tinode,
|
||||
reply: this.props.reply,
|
||||
onCancelReply: this.props.onCancelReply,
|
||||
onSendMessage: this.handleSendImage,
|
||||
onError: this.props.onError
|
||||
}) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview-footer"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_file_name",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "File name:"
|
||||
}]
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
title: this.props.content.filename
|
||||
}, fname))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_content_type",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Content type:"
|
||||
}]
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, this.props.content.type)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_size",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Size:"
|
||||
}]
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, width, " \xD7 ", height, " px; ", (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_5__.bytesToHumanSize)(this.props.content.size)))));
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=src_widgets_image-preview_jsx.dev.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,197 @@
|
||||
"use strict";
|
||||
(self["webpackChunktinode_webapp"] = self["webpackChunktinode_webapp"] || []).push([["src_widgets_the-card-preview_jsx"],{
|
||||
|
||||
/***/ "./src/widgets/the-card-preview.jsx":
|
||||
/*!******************************************!*\
|
||||
!*** ./src/widgets/the-card-preview.jsx ***!
|
||||
\******************************************/
|
||||
/***/ (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 */ TheCardPreview; }
|
||||
/* harmony export */ });
|
||||
/* 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 libphonenumber_js_mobile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! libphonenumber-js/mobile */ "./node_modules/libphonenumber-js/mobile/exports/parsePhoneNumberWithError.js");
|
||||
/* harmony import */ var _send_message_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./send-message.jsx */ "./src/widgets/send-message.jsx");
|
||||
/* harmony import */ var _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
|
||||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
|
||||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_5__);
|
||||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
|
||||
unknown_name: {
|
||||
id: "unknown_name",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Unknown"
|
||||
}]
|
||||
}
|
||||
});
|
||||
class TheCardPreview extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
content: props.content.object
|
||||
};
|
||||
if (!props.content.object) {
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_6__.importVCard)(props.content.file).then(card => {
|
||||
this.setState({
|
||||
content: card
|
||||
});
|
||||
});
|
||||
}
|
||||
this.handleSendCard = this.handleSendCard.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
handleKeyDown(e) {
|
||||
e.preventDefault();
|
||||
if (e.key === 'Escape') {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
handleSendCard() {
|
||||
this.props.onClose();
|
||||
this.props.onSendMessage(this.props.content);
|
||||
}
|
||||
render() {
|
||||
const card = this.state.content;
|
||||
const fullName = tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.TheCard.getFn(card);
|
||||
const photoUrl = tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.TheCard.getPhotoUrl(card) || null;
|
||||
const note = card?.note;
|
||||
const org = tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.TheCard.getOrg(card);
|
||||
const emails = tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.TheCard.getComm(card, 'email') || [];
|
||||
const phones = tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.TheCard.getComm(card, 'tel') || [];
|
||||
const tinodeIds = tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.TheCard.getComm(card, 'tinode') || [];
|
||||
const urls = tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.TheCard.getComm(card, 'http') || [];
|
||||
const contacts = [];
|
||||
emails.forEach((email, idx) => {
|
||||
const types = Array.isArray(email.des) ? email.des.join(', ') : '';
|
||||
contacts.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "group quoted",
|
||||
key: `email-${idx}`
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tt", null, email.value), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
className: "small gray"
|
||||
}, " ", types ? `(${types})` : '')));
|
||||
});
|
||||
phones.forEach((phone, idx) => {
|
||||
let val = phone.value;
|
||||
try {
|
||||
const number = (0,libphonenumber_js_mobile__WEBPACK_IMPORTED_MODULE_2__.parsePhoneNumberWithError)(phone.value);
|
||||
val = number ? number.formatInternational() : phone.value;
|
||||
} catch (err) {}
|
||||
const types = Array.isArray(phone.des) ? phone.des.join(', ') : '';
|
||||
contacts.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "group quoted",
|
||||
key: `tel-${idx}`
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tt", null, val), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
className: "small gray"
|
||||
}, " ", types ? `(${types})` : '')));
|
||||
});
|
||||
tinodeIds.forEach((tid, idx) => {
|
||||
const types = Array.isArray(tid.des) ? tid.des.join(', ') : '';
|
||||
contacts.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "group quoted",
|
||||
key: `tinode-${idx}`
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tt", null, tid.value), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
className: "small gray"
|
||||
}, " ", types ? `(${types})` : '')));
|
||||
});
|
||||
urls.forEach((url, idx) => {
|
||||
const types = Array.isArray(url.des) ? url.des.join(', ') : '';
|
||||
contacts.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "group quoted",
|
||||
key: `url-${idx}`
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tt", null, url.value), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
className: "small gray"
|
||||
}, " ", types ? `(${types})` : '')));
|
||||
});
|
||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "preview-caption-panel"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_contact_card",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Contact Card"
|
||||
}]
|
||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||
href: "#",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
this.props.onClose();
|
||||
}
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons gray"
|
||||
}, "close"))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview-container"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "doc-card"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "panel-form-column"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("center", null, 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"], {
|
||||
authorizeURL: this.props.tinode.authorizeURL,
|
||||
avatar: photoUrl || true,
|
||||
topic: "usrXXX123",
|
||||
title: fullName
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "group"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "large ellipsized"
|
||||
}, fullName || 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: "group"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, org)) : null, note ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "group"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
||||
className: "small"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_description",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Description"
|
||||
}]
|
||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "quoted"
|
||||
}, note)) : null, contacts.length > 0 ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "group"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
||||
className: "small"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_contacts",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Contacts"
|
||||
}]
|
||||
})), contacts) : null))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_send_message_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
noInput: true,
|
||||
tinode: this.props.tinode,
|
||||
reply: this.props.reply,
|
||||
onCancelReply: this.props.onCancelReply,
|
||||
onSendMessage: this.handleSendCard,
|
||||
onError: this.props.onError
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=src_widgets_the-card-preview_jsx.dev.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,143 @@
|
||||
"use strict";
|
||||
(self["webpackChunktinode_webapp"] = self["webpackChunktinode_webapp"] || []).push([["src_widgets_video-preview_jsx"],{
|
||||
|
||||
/***/ "./src/widgets/video-preview.jsx":
|
||||
/*!***************************************!*\
|
||||
!*** ./src/widgets/video-preview.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 _send_message_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./send-message.jsx */ "./src/widgets/send-message.jsx");
|
||||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||
|
||||
|
||||
|
||||
|
||||
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
|
||||
unrecognized_video_format: {
|
||||
id: "unrecognized_video_format",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Format of this video is not recognized"
|
||||
}]
|
||||
}
|
||||
});
|
||||
class VideoPreview extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.videoRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||||
this.handleSendVideo = this.handleSendVideo.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
handleKeyDown(e) {
|
||||
if (this.props.onSendMessage) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
if (e.key === 'Escape') {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
handleSendVideo(caption) {
|
||||
this.props.onClose();
|
||||
const params = {
|
||||
width: this.videoRef.current.videoWidth,
|
||||
height: this.videoRef.current.videoHeight,
|
||||
duration: this.videoRef.current.duration * 1000 | 0,
|
||||
mime: this.props.content.mime,
|
||||
name: this.props.content.filename
|
||||
};
|
||||
if (params.width == 0 || params.height == 0) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.unrecognized_video_format), 'err');
|
||||
return;
|
||||
}
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = params.width;
|
||||
canvas.height = params.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(this.videoRef.current, 0, 0, canvas.width, canvas.height);
|
||||
ctx.canvas.toBlob(preview => this.props.onSendMessage(caption, this.props.content.blob, preview, params), 'image/jpeg', 0.75);
|
||||
}
|
||||
render() {
|
||||
if (!this.props.content) {
|
||||
return null;
|
||||
}
|
||||
const width = this.props.content.width || '-';
|
||||
const height = this.props.content.height || '-';
|
||||
const controlist = this.props.onSendMessage ? 'nodownload' : '';
|
||||
const autoPlay = !this.props.onSendMessage;
|
||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "preview-caption-panel"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, this.props.content.filename), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||
href: "#",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
this.props.onClose();
|
||||
}
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons gray"
|
||||
}, "close"))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview-container"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
|
||||
className: "image-preview",
|
||||
controls: true,
|
||||
controlsList: controlist,
|
||||
disablePictureInPicture: true,
|
||||
ref: this.videoRef,
|
||||
autoPlay: autoPlay,
|
||||
src: this.props.tinode.authorizeURL(this.props.content.url),
|
||||
poster: this.props.content.preview,
|
||||
alt: this.props.content.filename
|
||||
})), this.props.onSendMessage ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_send_message_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
messagePrompt: "add_image_caption",
|
||||
acceptBlank: true,
|
||||
tinode: this.props.tinode,
|
||||
reply: this.props.reply,
|
||||
onCancelReply: this.props.onCancelReply,
|
||||
onSendMessage: this.handleSendVideo,
|
||||
onError: this.props.onError
|
||||
}) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "image-preview-footer"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_file_name",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "File name:"
|
||||
}]
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||
title: this.props.content.filename
|
||||
}, this.props.content.filename))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_content_type",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Content type:"
|
||||
}]
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, this.props.content.type)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("b", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "label_size",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Size:"
|
||||
}]
|
||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, width, " \xD7 ", height, " px; ", (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__.bytesToHumanSize)(this.props.content.size)))));
|
||||
}
|
||||
}
|
||||
;
|
||||
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(VideoPreview));
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=src_widgets_video-preview_jsx.dev.js.map
|
||||
File diff suppressed because one or more lines are too long
+134
-17
@@ -2607,6 +2607,7 @@ Drafty.attrValue = function (style, data) {
|
||||
Drafty.getContentType = function () {
|
||||
return DRAFTY_MIME_TYPE;
|
||||
};
|
||||
Drafty.contentType = DRAFTY_MIME_TYPE;
|
||||
Drafty.isFormResponseType = function (mimeType) {
|
||||
return mimeType === DRAFTY_FR_MIME_TYPE || mimeType === DRAFTY_FR_MIME_TYPE_LEGACY;
|
||||
};
|
||||
@@ -3978,16 +3979,19 @@ __webpack_require__.r(__webpack_exports__);
|
||||
|
||||
|
||||
|
||||
const THE_CARD_MIME_TYPE = 'text/x-the-card';
|
||||
class TheCard {
|
||||
constructor(fn, imageUrl, imageMimeType, note) {
|
||||
this.card = theCard(fn, imageUrl, imageMimeType, note);
|
||||
Object.assign(this, theCard(fn, imageUrl, imageMimeType, note) || {});
|
||||
}
|
||||
getCard() {
|
||||
return this.card;
|
||||
merge(that) {
|
||||
Object.assign(this, that);
|
||||
}
|
||||
get contentType() {
|
||||
return THE_CARD_MIME_TYPE;
|
||||
}
|
||||
}
|
||||
TheCard.init = theCard;
|
||||
TheCard.contentType = 'text/x-card';
|
||||
TheCard.contentType = THE_CARD_MIME_TYPE;
|
||||
TheCard.setFn = function (card, fn) {
|
||||
fn = fn && fn.trim();
|
||||
card = card || {};
|
||||
@@ -4130,6 +4134,12 @@ TheCard.exportVCard = function (card) {
|
||||
if (card.note && card.note != _config_js__WEBPACK_IMPORTED_MODULE_0__.DEL_CHAR) {
|
||||
vcard += `NOTE:${card.note}\r\n`;
|
||||
}
|
||||
if (card.bday && card.bday.m && card.bday.d) {
|
||||
const year = card.bday.y ? String(card.bday.y).padStart(4, '0') : '--';
|
||||
const month = String(card.bday.m).padStart(2, '0');
|
||||
const day = String(card.bday.d).padStart(2, '0');
|
||||
vcard += `BDAY:${year}-${month}-${day}\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`;
|
||||
@@ -4165,6 +4175,8 @@ TheCard.importVCard = function (vcardStr) {
|
||||
const line = rawLines[i];
|
||||
if (line.startsWith(' ') || line.startsWith('\t')) {
|
||||
currentLine += line.substring(1);
|
||||
} else if (currentLine.endsWith('=')) {
|
||||
currentLine = currentLine.substring(0, currentLine.length - 1) + line;
|
||||
} else {
|
||||
if (currentLine) {
|
||||
lines.push(currentLine);
|
||||
@@ -4177,6 +4189,39 @@ TheCard.importVCard = function (vcardStr) {
|
||||
}
|
||||
let card = {};
|
||||
const commMap = new Map();
|
||||
const unescapeValue = val => {
|
||||
return val.replace(/\\([,;\\n])/g, (match, char) => {
|
||||
if (char === 'n') return '\n';
|
||||
return char;
|
||||
});
|
||||
};
|
||||
const decodeQuotedPrintable = val => {
|
||||
const bytes = [];
|
||||
let i = 0;
|
||||
while (i < val.length) {
|
||||
if (val[i] === '=' && i + 2 < val.length) {
|
||||
const hex = val.substring(i + 1, i + 3);
|
||||
if (/^[0-9A-F]{2}$/i.test(hex)) {
|
||||
bytes.push(parseInt(hex, 16));
|
||||
i += 3;
|
||||
} else {
|
||||
bytes.push(val.charCodeAt(i));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
bytes.push(val.charCodeAt(i));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const uint8Array = new Uint8Array(bytes);
|
||||
return new TextDecoder('utf-8').decode(uint8Array);
|
||||
} catch (e) {
|
||||
return val.replace(/=([0-9A-F]{2})/gi, (match, hex) => {
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
});
|
||||
}
|
||||
};
|
||||
lines.forEach(line => {
|
||||
const [keyPart, ...valueParts] = line.split(':');
|
||||
if (!keyPart || valueParts.length === 0) {
|
||||
@@ -4185,32 +4230,104 @@ TheCard.importVCard = function (vcardStr) {
|
||||
const value = valueParts.join(':');
|
||||
const keyParams = keyPart.split(';');
|
||||
const key = keyParams[0].trim().toUpperCase();
|
||||
const isQuotedPrintable = keyParams.some(param => param.trim().toUpperCase() === 'QUOTED-PRINTABLE' || param.trim().toUpperCase() === 'ENCODING=QUOTED-PRINTABLE');
|
||||
if (key === 'FN') {
|
||||
card.fn = value;
|
||||
let processedValue = value;
|
||||
if (isQuotedPrintable) {
|
||||
processedValue = decodeQuotedPrintable(processedValue);
|
||||
}
|
||||
card.fn = unescapeValue(processedValue);
|
||||
} else if (key === 'N') {
|
||||
const parts = value.split(';');
|
||||
let processedValue = value;
|
||||
if (isQuotedPrintable) {
|
||||
processedValue = decodeQuotedPrintable(processedValue);
|
||||
}
|
||||
const parts = processedValue.split(';');
|
||||
card.n = {
|
||||
surname: parts[0] || undefined,
|
||||
given: parts[1] || undefined,
|
||||
additional: parts[2] || undefined,
|
||||
prefix: parts[3] || undefined,
|
||||
suffix: parts[4] || undefined
|
||||
surname: parts[0] ? unescapeValue(parts[0]) : undefined,
|
||||
given: parts[1] ? unescapeValue(parts[1]) : undefined,
|
||||
additional: parts[2] ? unescapeValue(parts[2]) : undefined,
|
||||
prefix: parts[3] ? unescapeValue(parts[3]) : undefined,
|
||||
suffix: parts[4] ? unescapeValue(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(';');
|
||||
let processedValue = value;
|
||||
if (isQuotedPrintable) {
|
||||
processedValue = decodeQuotedPrintable(processedValue);
|
||||
}
|
||||
const parts = processedValue.split(';');
|
||||
card.org = card.org || {};
|
||||
card.org.fn = parts[0] || undefined;
|
||||
card.org.fn = parts[0] ? unescapeValue(parts[0]) : undefined;
|
||||
if (!card.org.fn) delete card.org.fn;
|
||||
} else if (key === 'TITLE') {
|
||||
let processedValue = value;
|
||||
if (isQuotedPrintable) {
|
||||
processedValue = decodeQuotedPrintable(processedValue);
|
||||
}
|
||||
card.org = card.org || {};
|
||||
card.org.title = value || undefined;
|
||||
card.org.title = processedValue ? unescapeValue(processedValue) : undefined;
|
||||
if (!card.org.title) delete card.org.title;
|
||||
} else if (key === 'NOTE') {
|
||||
card.note = value;
|
||||
let processedValue = value;
|
||||
if (isQuotedPrintable) {
|
||||
processedValue = decodeQuotedPrintable(processedValue);
|
||||
}
|
||||
card.note = unescapeValue(processedValue);
|
||||
} else if (key === 'BDAY') {
|
||||
let dateStr = value.split(/[T ]/)[0].trim();
|
||||
let year = null;
|
||||
let month = null;
|
||||
let day = null;
|
||||
const noHyphens = dateStr.replace(/-/g, '');
|
||||
if (noHyphens.length === 6 && /^\d{6}$/.test(noHyphens)) {
|
||||
const yy = parseInt(noHyphens.substring(0, 2), 10);
|
||||
month = parseInt(noHyphens.substring(2, 4), 10);
|
||||
day = parseInt(noHyphens.substring(4, 6), 10);
|
||||
year = yy >= 35 ? 1900 + yy : 2000 + yy;
|
||||
} else if (dateStr.startsWith('--') || dateStr.startsWith('----')) {
|
||||
const cleaned = dateStr.replace(/^-+/, '');
|
||||
if (cleaned.length === 4 && /^\d{4}$/.test(cleaned)) {
|
||||
month = parseInt(cleaned.substring(0, 2), 10);
|
||||
day = parseInt(cleaned.substring(2, 4), 10);
|
||||
} else if (cleaned.includes('-')) {
|
||||
const parts = cleaned.split('-');
|
||||
if (parts.length >= 2) {
|
||||
month = parseInt(parts[0], 10);
|
||||
day = parseInt(parts[1], 10);
|
||||
}
|
||||
}
|
||||
} else if (noHyphens.length === 8 && /^\d{8}$/.test(noHyphens)) {
|
||||
year = parseInt(noHyphens.substring(0, 4), 10);
|
||||
month = parseInt(noHyphens.substring(4, 6), 10);
|
||||
day = parseInt(noHyphens.substring(6, 8), 10);
|
||||
} else if (dateStr.includes('-')) {
|
||||
const parts = dateStr.split('-');
|
||||
if (parts[0] && parts[0] !== '' && !/^-+$/.test(parts[0])) {
|
||||
year = parseInt(parts[0], 10);
|
||||
}
|
||||
if (parts.length >= 2) {
|
||||
month = parseInt(parts[1], 10);
|
||||
}
|
||||
if (parts.length >= 3) {
|
||||
day = parseInt(parts[2], 10);
|
||||
}
|
||||
}
|
||||
const isValidMonth = month && month >= 1 && month <= 12;
|
||||
const isValidDay = day && day >= 1 && day <= 31;
|
||||
const isValidYear = !year || year >= 1800 && year <= 2200;
|
||||
if (isValidMonth && isValidDay && isValidYear) {
|
||||
card.bday = {
|
||||
m: month,
|
||||
d: day
|
||||
};
|
||||
if (year) {
|
||||
card.bday.y = year;
|
||||
}
|
||||
}
|
||||
} else if (key === 'PHOTO') {
|
||||
const params = keyPart.split(';').slice(1);
|
||||
let type = 'jpeg';
|
||||
@@ -4313,7 +4430,7 @@ TheCard.importVCard = function (vcardStr) {
|
||||
return card;
|
||||
};
|
||||
TheCard.isFileSupported = function (type, name) {
|
||||
return type == 'text/vcard' || (name || '').endsWith('.vcf') || (name || '').endsWith('.vcr') || (name || '').endsWith('.vcard');
|
||||
return type == 'text/vcard' || (name || '').endsWith('.vcf') || (name || '').endsWith('.vcard');
|
||||
};
|
||||
function theCard(fn, imageUrl, imageMimeType, note) {
|
||||
let card = null;
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3827
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user