video uploading + preview in bubbles works
This commit is contained in:
+15
-8
@@ -2036,11 +2036,6 @@ img.image-preview::before {
|
||||
background-size: 10rem 10rem;
|
||||
}
|
||||
|
||||
.inline-image.video {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.inline-image.video > img.inline-image::before, img.image-preview.video::before {
|
||||
background: center no-repeat #ccc url("../img/broken_video.png");
|
||||
}
|
||||
@@ -2049,7 +2044,18 @@ img.image-preview::before {
|
||||
background-size: 3rem 3rem;
|
||||
}
|
||||
|
||||
.inline-image.video .duration {
|
||||
.inline-video {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.inline-video > img.inline-image::before {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.inline-video .duration {
|
||||
padding: 0.25rem 0.5rem;
|
||||
color: #333;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
@@ -2060,15 +2066,16 @@ img.image-preview::before {
|
||||
border-radius: 0.3rem;
|
||||
}
|
||||
|
||||
.inline-image.video .play-control {
|
||||
.inline-video .play-control {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Attachment styles */
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -76,6 +76,9 @@ export const MAX_IMAGE_DIM = 1024;
|
||||
// Linear dimensions of image preview: shrink image under this size for previews.
|
||||
export const IMAGE_PREVIEW_DIM = 64;
|
||||
|
||||
// Linear dimensions of video screencapture: shrink image under this size for previews.
|
||||
export const VIDEO_PREVIEW_DIM = 96;
|
||||
|
||||
// Linear dimensions of image thumbnail: shrink image under this size for thumbnails in reply quote previews.
|
||||
export const IMAGE_THUMBNAIL_DIM = 36;
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ export function imageCrop(mime, objURL, left, top, width, height, scale) {
|
||||
export function fileToBase64(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onerror = (evt) => {
|
||||
reader.onerror = _ => {
|
||||
reject(reader.error);
|
||||
};
|
||||
reader.onload = _ => {
|
||||
|
||||
+10
-14
@@ -40,11 +40,12 @@ const messages = defineMessages({
|
||||
|
||||
// The main Drafty formatter: converts Drafty elements into React classes. 'this' is set by the caller.
|
||||
// 'this' must contain:
|
||||
// viewportWidth: this.props.viewportWidth;
|
||||
// authorizeURL: this.props.tinode.authorizeURL
|
||||
// onImagePreview: this.handleImagePreview
|
||||
// onFormButtonClick: this.handleFormButtonClick
|
||||
// onQuoteClick: this.handleQuoteClick (optional)
|
||||
// viewportWidth:
|
||||
// authorizeURL:
|
||||
// onImagePreview:
|
||||
// onVideoPreview:
|
||||
// onFormButtonClick:
|
||||
// onQuoteClick:
|
||||
export function fullFormatter(style, data, values, key, stack) {
|
||||
if (stack.includes('QQ')) {
|
||||
return quoteFormatter.call(this, style, data, values, key);
|
||||
@@ -192,7 +193,7 @@ function handleImageData(el, data, attr) {
|
||||
}
|
||||
attr.loading = 'lazy';
|
||||
} else {
|
||||
attr.src = 'img/broken_image.png';
|
||||
attr.src = null;
|
||||
}
|
||||
} else {
|
||||
// Use custom element instead of <img>.
|
||||
@@ -226,18 +227,13 @@ function handleVideoData(el, data, attr) {
|
||||
minHeight: dim.dstHeight + 'px'
|
||||
};
|
||||
if (!Drafty.isProcessing(data)) {
|
||||
/*
|
||||
attr.src = this.authorizeURL(sanitizeUrlForMime(attr.src, 'video'));
|
||||
attr.alt = data.name;
|
||||
if (attr.src) {
|
||||
attr.onClick = this.onImagePreview;
|
||||
attr.className += ' image-clickable';
|
||||
if (data.ref || data.val) {
|
||||
attr.onClick = this.onVideoPreview;
|
||||
attr.loading = 'lazy';
|
||||
attr.controls = 'controls';
|
||||
} else {
|
||||
attr.src = 'img/broken_video.png';
|
||||
attr.src = null;
|
||||
}
|
||||
*/
|
||||
el = InlineVideo;
|
||||
} else {
|
||||
// Use custom element instead of <img>.
|
||||
|
||||
@@ -34,13 +34,17 @@ export function relativeDateFormat(then, locale) {
|
||||
// Convert seconds to [hours:]minutes:seconds, i.e. 156 sec -> 2:36, 3756 sec -> 1:02:36.
|
||||
// If <code>fixedMins</code> is true, then minutes are represented by at least two digits.
|
||||
export function secondsToTime(seconds, fixedMin) {
|
||||
let min = Math.floor(seconds / 60) | 0;
|
||||
let hours = Math.floor(min / 60) | 0;
|
||||
if (typeof seconds != 'number') {
|
||||
return '';
|
||||
}
|
||||
|
||||
let min = (Math.floor(seconds / 60) | 0) % 60;
|
||||
let hours = Math.floor(seconds / 36000) | 0;
|
||||
|
||||
if (fixedMin || hours > 0) {
|
||||
min = min < 10 ? `0${min}` : min;
|
||||
}
|
||||
let sec = (seconds % 60) | 0;
|
||||
let sec = (seconds | 0) % 60;
|
||||
sec = sec < 10 ? `0${sec}` : sec;
|
||||
if (hours == 0) {
|
||||
return `${min}:${sec}`;
|
||||
|
||||
+84
-62
@@ -22,7 +22,7 @@ import VideoPreview from '../widgets/video-preview.jsx';
|
||||
|
||||
import { DEFAULT_P2P_ACCESS_MODE, EDIT_PREVIEW_LENGTH, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY,
|
||||
MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE,
|
||||
READ_DELAY, QUOTED_REPLY_LENGTH } from '../config.js';
|
||||
READ_DELAY, QUOTED_REPLY_LENGTH, VIDEO_PREVIEW_DIM } from '../config.js';
|
||||
import { CALL_STATE_OUTGOING_INITATED, CALL_STATE_IN_PROGRESS } from '../constants.js';
|
||||
import { blobToBase64, fileToBase64, imageScaled, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
@@ -828,7 +828,7 @@ class MessagesView extends React.Component {
|
||||
// Small enough to send inband.
|
||||
fileToBase64(file)
|
||||
.then(b64 => this.sendMessage(Drafty.attachFile(null, {mime: b64.mime, data: b64.bits, filename: b64.name})))
|
||||
.catch(err => this.props.onError(err));
|
||||
.catch(err => this.props.onError(err.message, 'err'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,32 +885,34 @@ class MessagesView extends React.Component {
|
||||
// Convert tiny image into base64 for serialization and previewing.
|
||||
.then(scaled => blobToBase64(scaled.blob))
|
||||
.then(b64 => {
|
||||
let msg = Drafty.insertImage(null, 0, {
|
||||
mime: mime,
|
||||
_tempPreview: b64.bits, // This preview will not be serialized.
|
||||
width: width,
|
||||
height: height,
|
||||
filename: fname,
|
||||
size: blob.size,
|
||||
urlPromise: uploadCompletionPromise
|
||||
});
|
||||
if (caption) {
|
||||
msg = Drafty.appendLineBreak(msg);
|
||||
msg = Drafty.append(msg, Drafty.parse(caption));
|
||||
}
|
||||
// Pass data and the uploader to the TinodeWeb.
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
let msg = Drafty.insertImage(null, 0, {
|
||||
mime: mime,
|
||||
_tempPreview: b64.bits, // This preview will not be serialized.
|
||||
bits: b64.bits, // Image thumbnail.
|
||||
width: width,
|
||||
height: height,
|
||||
filename: fname,
|
||||
size: blob.size,
|
||||
urlPromise: uploadCompletionPromise
|
||||
});
|
||||
if (caption) {
|
||||
msg = Drafty.appendLineBreak(msg);
|
||||
msg = Drafty.append(msg, Drafty.parse(caption));
|
||||
}
|
||||
// Pass data and the uploader to the TinodeWeb.
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
})
|
||||
.catch(err => this.props.onError(err, 'err'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the image inband if it's not too big.
|
||||
// Send the image inband if it's not too big. The image has been scaled already
|
||||
// in image preview.
|
||||
blobToBase64(blob)
|
||||
.then(b64 => {
|
||||
let msg = Drafty.insertImage(null, 0, {
|
||||
mime: b64.mime,
|
||||
preview: b64.bits, // Serializable preview
|
||||
bits: b64.bits,
|
||||
width: width,
|
||||
height: height,
|
||||
filename: fname,
|
||||
@@ -926,68 +928,88 @@ class MessagesView extends React.Component {
|
||||
|
||||
// sendVideoAttachment sends the video bits as Drafty message.
|
||||
sendVideoAttachment(caption, videoBlob, previewBlob, params) {
|
||||
const mime = params.mime;
|
||||
const width = params.width;
|
||||
const height = params.height;
|
||||
const fname = params.name;
|
||||
const duration = params.duration;
|
||||
|
||||
// Server-provided limit reduced for base64 encoding and overhead.
|
||||
const maxInbandAttachmentSize = (this.props.tinode.getServerParam('maxMessageSize',
|
||||
MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;
|
||||
|
||||
if (videoBlob.size > maxInbandAttachmentSize) {
|
||||
// Too large to send inband - uploading out of band and sending as a link.
|
||||
const uploader = this.props.tinode.getLargeFileHelper();
|
||||
const uploads = [];
|
||||
let uploader;
|
||||
if ((videoBlob.size + previewBlob.size) > maxInbandAttachmentSize) {
|
||||
// One or both are too large to send inband. Uploading out of band and sending as a link.
|
||||
uploader = this.props.tinode.getLargeFileHelper();
|
||||
if (!uploader) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||||
return;
|
||||
}
|
||||
const uploadCompletionPromise = uploader.upload(videoBlob);
|
||||
// Make small preview to show while uploading.
|
||||
imageScaled(previewBlob, IMAGE_PREVIEW_DIM, IMAGE_PREVIEW_DIM, -1, false)
|
||||
// Convert tiny image into base64 for serialization and previewing.
|
||||
.then(scaled => blobToBase64(scaled.blob))
|
||||
.then(b64 => {
|
||||
let msg = Drafty.insertVideo(null, 0, {
|
||||
mime: mime,
|
||||
_tempPreview: b64.bits, // This preview will not be serialized.
|
||||
width: width,
|
||||
height: height,
|
||||
duration: duration,
|
||||
filename: fname,
|
||||
size: videoBlob.size,
|
||||
urlPromise: uploadCompletionPromise
|
||||
});
|
||||
if (caption) {
|
||||
msg = Drafty.appendLineBreak(msg);
|
||||
msg = Drafty.append(msg, Drafty.parse(caption));
|
||||
}
|
||||
// Pass data and the uploader to the TinodeWeb.
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
})
|
||||
.catch(err => /* this.props.onError(err, 'err') */ console.log(err));
|
||||
|
||||
uploads[0] = videoBlob.size > maxInbandAttachmentSize * 0.675 ? uploader.upload(videoBlob) : null;
|
||||
uploads[1] = previewBlob.size > maxInbandAttachmentSize * 0.275 ? uploader.upload(previewBlob) : null;
|
||||
}
|
||||
|
||||
if (uploads.length == 0) {
|
||||
// Both video and preview are small enough to send inband.
|
||||
Promise.all(blobToBase64(videoBlob), blobToBase64(previewBlob))
|
||||
.then(b64s => {
|
||||
const [v64, i64] = b64s;
|
||||
let msg = Drafty.insertVideo(null, 0, {
|
||||
mime: v64.mime,
|
||||
bits: v64.bits,
|
||||
preview: i64.bits,
|
||||
premime: i64.mime,
|
||||
width: width,
|
||||
height: height,
|
||||
duration: params.duration,
|
||||
filename: params.name,
|
||||
size: videoBlob.size
|
||||
});
|
||||
if (caption) {
|
||||
msg = Drafty.appendLineBreak(msg);
|
||||
msg = Drafty.append(msg, Drafty.parse(caption));
|
||||
}
|
||||
this.sendMessage(msg);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the video inband if it's small enough.
|
||||
blobToBase64(videoBlob)
|
||||
.then(b64 => {
|
||||
const uploadCompletionPromise = Promise.all(uploads);
|
||||
|
||||
const b64conv = [];
|
||||
// Small video converted to base64.
|
||||
b64conv[0] = uploads[0] ? null : blobToBase64(videoBlob);
|
||||
// Full-size preview fits inline.
|
||||
b64conv[1] = uploads[1] ? null : imageScaled(previewBlob, MAX_IMAGE_DIM, MAX_IMAGE_DIM, -1, false)
|
||||
.then(scaled => blobToBase64(scaled.blob));
|
||||
// Small preview to show while uploading.
|
||||
b64conv[2] = imageScaled(previewBlob, VIDEO_PREVIEW_DIM, VIDEO_PREVIEW_DIM, -1, false)
|
||||
.then(scaled => blobToBase64(scaled.blob));
|
||||
Promise.all(b64conv)
|
||||
// Convert tiny image into base64 for serialization and previewing.
|
||||
.then(b64s => {
|
||||
const [video, img, preview] = b64s;
|
||||
let msg = Drafty.insertVideo(null, 0, {
|
||||
mime: b64.mime,
|
||||
preview: b64.bits, // Serializable preview
|
||||
mime: params.mime,
|
||||
bits: video ? video.bits : null,
|
||||
_tempPreview: preview.bits,
|
||||
preview: img ? img.bits : preview.bits,
|
||||
premime: img ? img.mime : preview.mime,
|
||||
width: width,
|
||||
height: height,
|
||||
duration: duration,
|
||||
filename: fname,
|
||||
size: blob.size
|
||||
duration: params.duration,
|
||||
filename: params.name,
|
||||
size: videoBlob.size,
|
||||
urlPromise: uploadCompletionPromise
|
||||
});
|
||||
if (caption) {
|
||||
msg = Drafty.appendLineBreak(msg);
|
||||
msg = Drafty.append(msg, Drafty.parse(caption));
|
||||
}
|
||||
this.sendMessage(msg);
|
||||
});
|
||||
// Pass data and the uploader to the TinodeWeb.
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
})
|
||||
.catch(err => this.props.onError(err.message, 'err'));
|
||||
}
|
||||
|
||||
// handleAttachImageOrVideo method is called when [Attach image or video] button is clicked: launch image or video preview.
|
||||
@@ -1018,7 +1040,7 @@ class MessagesView extends React.Component {
|
||||
mime: scaled.mime
|
||||
}});
|
||||
}).catch(err => {
|
||||
this.props.onError(err, 'err');
|
||||
this.props.onError(err.message, 'err');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1052,15 +1074,15 @@ class MessagesView extends React.Component {
|
||||
.then(b64 => {
|
||||
this.sendMessage(Drafty.appendAudio(null, {
|
||||
mime: b64.mime,
|
||||
bits: b64.bits,
|
||||
size: blob.size,
|
||||
data: b64.bits,
|
||||
duration: duration,
|
||||
preview: preview,
|
||||
}))
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(err => {this.props.onError(err)});;
|
||||
.catch(err => {this.props.onError(err.message, 'err')});;
|
||||
}
|
||||
|
||||
handleCancelUpload(seq, uploader) {
|
||||
|
||||
@@ -35,6 +35,7 @@ class BaseChatMessage extends React.PureComponent {
|
||||
authorizeURL: props.tinode.authorizeURL.bind(props.tinode),
|
||||
|
||||
onImagePreview: this.handleImagePreview,
|
||||
onVideoPreview: this.handleImagePreview,
|
||||
onFormButtonClick: this.handleFormButtonClick,
|
||||
onQuoteClick: this.handleQuoteClick
|
||||
};
|
||||
@@ -81,7 +82,7 @@ class BaseChatMessage extends React.PureComponent {
|
||||
if (!this.props.response) {
|
||||
let immutable = false;
|
||||
Drafty.entities(this.props.content, (_0, _1, tp) => {
|
||||
immutable = ['AU', 'EX', 'FM', 'IM', 'VC'].includes(tp);
|
||||
immutable = ['AU', 'EX', 'FM', 'IM', 'QQ', 'VC', 'VD'].includes(tp);
|
||||
return immutable;
|
||||
});
|
||||
if (!immutable) {
|
||||
|
||||
@@ -3,19 +3,24 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { secondsToTime } from '../lib/strformat.js'
|
||||
|
||||
export default class InlineVideo extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const duration = secondsToTime(this.props['data-duration'] / 1000);
|
||||
return (
|
||||
<div className="inline-image video">
|
||||
<div className="inline-video">
|
||||
{React.createElement('img', this.props)}
|
||||
<div className="play-control">
|
||||
<i class="material-icons white bigger">play_arrow</i>
|
||||
</div>
|
||||
<div className="duration">1:12:45</div>
|
||||
{this.props.onClick ?
|
||||
<div className="play-control" onClick={this.props.onClick}>
|
||||
<i className="material-icons white bigger">play_arrow</i>
|
||||
</div>
|
||||
: null}
|
||||
{duration ? <div className="duration">{duration}</div> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+73
-41
@@ -2192,7 +2192,8 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ "NO_ACCESS_MODE": () => (/* binding */ NO_ACCESS_MODE),
|
||||
/* harmony export */ "QUOTED_REPLY_LENGTH": () => (/* binding */ QUOTED_REPLY_LENGTH),
|
||||
/* harmony export */ "READ_DELAY": () => (/* binding */ READ_DELAY),
|
||||
/* harmony export */ "REM_SIZE": () => (/* binding */ REM_SIZE)
|
||||
/* harmony export */ "REM_SIZE": () => (/* binding */ REM_SIZE),
|
||||
/* harmony export */ "VIDEO_PREVIEW_DIM": () => (/* binding */ VIDEO_PREVIEW_DIM)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _version_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./version.js */ "./src/version.js");
|
||||
|
||||
@@ -2223,6 +2224,7 @@ const MAX_INBAND_ATTACHMENT_SIZE = 262_144;
|
||||
const MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23;
|
||||
const MAX_IMAGE_DIM = 1024;
|
||||
const IMAGE_PREVIEW_DIM = 64;
|
||||
const VIDEO_PREVIEW_DIM = 96;
|
||||
const IMAGE_THUMBNAIL_DIM = 36;
|
||||
const MAX_ONLINE_IN_TOPIC = 4;
|
||||
const MAX_TITLE_LENGTH = 60;
|
||||
@@ -2426,7 +2428,7 @@ function imageCrop(mime, objURL, left, top, width, height, scale) {
|
||||
function fileToBase64(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onerror = evt => {
|
||||
reader.onerror = _ => {
|
||||
reject(reader.error);
|
||||
};
|
||||
reader.onload = _ => {
|
||||
@@ -2746,7 +2748,7 @@ function handleImageData(el, data, attr) {
|
||||
}
|
||||
attr.loading = 'lazy';
|
||||
} else {
|
||||
attr.src = 'img/broken_image.png';
|
||||
attr.src = null;
|
||||
}
|
||||
} else {
|
||||
el = _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_8__["default"];
|
||||
@@ -2774,6 +2776,13 @@ function handleVideoData(el, data, attr) {
|
||||
minHeight: dim.dstHeight + 'px'
|
||||
};
|
||||
if (!tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isProcessing(data)) {
|
||||
attr.alt = data.name;
|
||||
if (data.ref || data.val) {
|
||||
attr.onClick = this.onVideoPreview;
|
||||
attr.loading = 'lazy';
|
||||
} else {
|
||||
attr.src = null;
|
||||
}
|
||||
el = _widgets_inline_video_jsx__WEBPACK_IMPORTED_MODULE_6__["default"];
|
||||
} else {
|
||||
el = _widgets_uploading_image_jsx__WEBPACK_IMPORTED_MODULE_8__["default"];
|
||||
@@ -3213,12 +3222,15 @@ function relativeDateFormat(then, locale) {
|
||||
return new Intl.DateTimeFormat(locale).format(then);
|
||||
}
|
||||
function secondsToTime(seconds, fixedMin) {
|
||||
let min = Math.floor(seconds / 60) | 0;
|
||||
let hours = Math.floor(min / 60) | 0;
|
||||
if (typeof seconds != 'number') {
|
||||
return '';
|
||||
}
|
||||
let min = (Math.floor(seconds / 60) | 0) % 60;
|
||||
let hours = Math.floor(seconds / 36000) | 0;
|
||||
if (fixedMin || hours > 0) {
|
||||
min = min < 10 ? `0${min}` : min;
|
||||
}
|
||||
let sec = seconds % 60 | 0;
|
||||
let sec = (seconds | 0) % 60;
|
||||
sec = sec < 10 ? `0${sec}` : sec;
|
||||
if (hours == 0) {
|
||||
return `${min}:${sec}`;
|
||||
@@ -6103,7 +6115,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
mime: b64.mime,
|
||||
data: b64.bits,
|
||||
filename: b64.name
|
||||
}))).catch(err => this.props.onError(err));
|
||||
}))).catch(err => this.props.onError(err.message, 'err'));
|
||||
}
|
||||
}
|
||||
handleAttachFile(file) {
|
||||
@@ -6148,6 +6160,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertImage(null, 0, {
|
||||
mime: mime,
|
||||
_tempPreview: b64.bits,
|
||||
bits: b64.bits,
|
||||
width: width,
|
||||
height: height,
|
||||
filename: fname,
|
||||
@@ -6165,7 +6178,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(blob).then(b64 => {
|
||||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertImage(null, 0, {
|
||||
mime: b64.mime,
|
||||
preview: b64.bits,
|
||||
bits: b64.bits,
|
||||
width: width,
|
||||
height: height,
|
||||
filename: fname,
|
||||
@@ -6179,54 +6192,68 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
});
|
||||
}
|
||||
sendVideoAttachment(caption, videoBlob, previewBlob, params) {
|
||||
const mime = params.mime;
|
||||
const width = params.width;
|
||||
const height = params.height;
|
||||
const fname = params.name;
|
||||
const duration = params.duration;
|
||||
const maxInbandAttachmentSize = this.props.tinode.getServerParam('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_17__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
|
||||
if (videoBlob.size > maxInbandAttachmentSize) {
|
||||
const uploader = this.props.tinode.getLargeFileHelper();
|
||||
const uploads = [];
|
||||
let uploader;
|
||||
if (videoBlob.size + previewBlob.size > maxInbandAttachmentSize) {
|
||||
uploader = this.props.tinode.getLargeFileHelper();
|
||||
if (!uploader) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||||
return;
|
||||
}
|
||||
const uploadCompletionPromise = uploader.upload(videoBlob);
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.imageScaled)(previewBlob, _config_js__WEBPACK_IMPORTED_MODULE_17__.IMAGE_PREVIEW_DIM, _config_js__WEBPACK_IMPORTED_MODULE_17__.IMAGE_PREVIEW_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(scaled.blob)).then(b64 => {
|
||||
uploads[0] = videoBlob.size > maxInbandAttachmentSize * 0.675 ? uploader.upload(videoBlob) : null;
|
||||
uploads[1] = previewBlob.size > maxInbandAttachmentSize * 0.275 ? uploader.upload(previewBlob) : null;
|
||||
}
|
||||
if (uploads.length == 0) {
|
||||
Promise.all((0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(videoBlob), (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(previewBlob)).then(b64s => {
|
||||
const [v64, i64] = b64s;
|
||||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertVideo(null, 0, {
|
||||
mime: mime,
|
||||
_tempPreview: b64.bits,
|
||||
mime: v64.mime,
|
||||
bits: v64.bits,
|
||||
preview: i64.bits,
|
||||
premime: i64.mime,
|
||||
width: width,
|
||||
height: height,
|
||||
duration: duration,
|
||||
filename: fname,
|
||||
size: videoBlob.size,
|
||||
urlPromise: uploadCompletionPromise
|
||||
duration: params.duration,
|
||||
filename: params.name,
|
||||
size: videoBlob.size
|
||||
});
|
||||
if (caption) {
|
||||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendLineBreak(msg);
|
||||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(msg, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(caption));
|
||||
}
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
}).catch(err => console.log(err));
|
||||
this.sendMessage(msg);
|
||||
});
|
||||
return;
|
||||
}
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(videoBlob).then(b64 => {
|
||||
const uploadCompletionPromise = Promise.all(uploads);
|
||||
const b64conv = [];
|
||||
b64conv[0] = uploads[0] ? null : (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(videoBlob);
|
||||
b64conv[1] = uploads[1] ? null : (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.imageScaled)(previewBlob, _config_js__WEBPACK_IMPORTED_MODULE_17__.MAX_IMAGE_DIM, _config_js__WEBPACK_IMPORTED_MODULE_17__.MAX_IMAGE_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(scaled.blob));
|
||||
b64conv[2] = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.imageScaled)(previewBlob, _config_js__WEBPACK_IMPORTED_MODULE_17__.VIDEO_PREVIEW_DIM, _config_js__WEBPACK_IMPORTED_MODULE_17__.VIDEO_PREVIEW_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(scaled.blob));
|
||||
Promise.all(b64conv).then(b64s => {
|
||||
const [video, img, preview] = b64s;
|
||||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertVideo(null, 0, {
|
||||
mime: b64.mime,
|
||||
preview: b64.bits,
|
||||
mime: params.mime,
|
||||
bits: video ? video.bits : null,
|
||||
_tempPreview: preview.bits,
|
||||
preview: img ? img.bits : preview.bits,
|
||||
premime: img ? img.mime : preview.mime,
|
||||
width: width,
|
||||
height: height,
|
||||
duration: duration,
|
||||
filename: fname,
|
||||
size: blob.size
|
||||
duration: params.duration,
|
||||
filename: params.name,
|
||||
size: videoBlob.size,
|
||||
urlPromise: uploadCompletionPromise
|
||||
});
|
||||
if (caption) {
|
||||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendLineBreak(msg);
|
||||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(msg, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(caption));
|
||||
}
|
||||
this.sendMessage(msg);
|
||||
});
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
}).catch(err => this.props.onError(err.message, 'err'));
|
||||
}
|
||||
handleAttachImageOrVideo(file) {
|
||||
const maxExternAttachmentSize = this.props.tinode.getServerParam('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_17__.MAX_EXTERN_ATTACHMENT_SIZE);
|
||||
@@ -6255,7 +6282,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
}
|
||||
});
|
||||
}).catch(err => {
|
||||
this.props.onError(err, 'err');
|
||||
this.props.onError(err.message, 'err');
|
||||
});
|
||||
}
|
||||
sendAudioAttachment(url, preview, duration) {
|
||||
@@ -6280,15 +6307,15 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_19__.blobToBase64)(blob).then(b64 => {
|
||||
this.sendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendAudio(null, {
|
||||
mime: b64.mime,
|
||||
bits: b64.bits,
|
||||
size: blob.size,
|
||||
data: b64.bits,
|
||||
duration: duration,
|
||||
preview: preview
|
||||
}));
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
this.props.onError(err);
|
||||
this.props.onError(err.message, 'err');
|
||||
});
|
||||
;
|
||||
}
|
||||
@@ -10960,6 +10987,7 @@ class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
|
||||
viewportWidth: props.viewportWidth,
|
||||
authorizeURL: props.tinode.authorizeURL.bind(props.tinode),
|
||||
onImagePreview: this.handleImagePreview,
|
||||
onVideoPreview: this.handleImagePreview,
|
||||
onFormButtonClick: this.handleFormButtonClick,
|
||||
onQuoteClick: this.handleQuoteClick
|
||||
};
|
||||
@@ -11002,7 +11030,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'].includes(tp);
|
||||
immutable = ['AU', 'EX', 'FM', 'IM', 'QQ', 'VC', 'VD'].includes(tp);
|
||||
return immutable;
|
||||
});
|
||||
if (!immutable) {
|
||||
@@ -13522,21 +13550,25 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* 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 _lib_strformat_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||
|
||||
|
||||
class InlineVideo extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
const duration = (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_1__.secondsToTime)(this.props['data-duration'] / 1000);
|
||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "inline-image video"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement('img', this.props), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "play-control"
|
||||
className: "inline-video"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement('img', this.props), this.props.onClick ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "play-control",
|
||||
onClick: this.props.onClick
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
class: "material-icons white bigger"
|
||||
}, "play_arrow")), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "material-icons white bigger"
|
||||
}, "play_arrow")) : null, duration ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "duration"
|
||||
}, "1:12:45"));
|
||||
}, duration) : 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
+25
-16
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user