video works although may have bugs
This commit is contained in:
@@ -189,6 +189,7 @@ function handleImageData(el, data, attr) {
|
||||
};
|
||||
if (!Drafty.isProcessing(data)) {
|
||||
attr.src = this.authorizeURL(sanitizeUrlForMime(attr.src, 'image'));
|
||||
attr['data-src'] = sanitizeUrlForMime(attr.src, 'video');
|
||||
attr.alt = data.name;
|
||||
if (attr.src) {
|
||||
if (Math.max(data.width || 0, data.height || 0) > IMAGE_THUMBNAIL_DIM) {
|
||||
|
||||
+32
-10
@@ -123,7 +123,7 @@ class MessagesView extends React.Component {
|
||||
this.handleMessageUpdate = this.handleMessageUpdate.bind(this);
|
||||
this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);
|
||||
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
|
||||
this.handleImagePostview = this.handleImagePostview.bind(this);
|
||||
this.handleExpandMedia = this.handleExpandMedia.bind(this);
|
||||
this.handleClosePreview = this.handleClosePreview.bind(this);
|
||||
this.handleFormResponse = this.handleFormResponse.bind(this);
|
||||
this.handleContextClick = this.handleContextClick.bind(this);
|
||||
@@ -274,6 +274,8 @@ class MessagesView extends React.Component {
|
||||
docPreview: null,
|
||||
imagePreview: null,
|
||||
imagePostview: null,
|
||||
videoPreview: null,
|
||||
videoPostview: null,
|
||||
rtcPanel: null,
|
||||
typingIndicator: false,
|
||||
scrollPosition: 0,
|
||||
@@ -282,7 +284,8 @@ class MessagesView extends React.Component {
|
||||
channel: false,
|
||||
reply: null,
|
||||
contentToEdit: null,
|
||||
showGoToLastButton: false
|
||||
showGoToLastButton: false,
|
||||
dragging: false
|
||||
};
|
||||
} else if (nextProps.topic != prevState.topic) {
|
||||
const topic = nextProps.tinode.getTopic(nextProps.topic);
|
||||
@@ -292,12 +295,15 @@ class MessagesView extends React.Component {
|
||||
docPreview: null,
|
||||
imagePreview: null,
|
||||
imagePostview: null,
|
||||
videoPreview: null,
|
||||
videoPostview: null,
|
||||
rtcPanel: null,
|
||||
typingIndicator: false,
|
||||
scrollPosition: 0,
|
||||
fetchingMessages: false,
|
||||
showGoToLastButton: false,
|
||||
deleted: topic._deleted
|
||||
deleted: topic._deleted,
|
||||
dragging: false
|
||||
};
|
||||
|
||||
if (nextProps.forwardMessage) {
|
||||
@@ -721,8 +727,16 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleImagePostview(content) {
|
||||
this.setState({ imagePostview: content });
|
||||
handleExpandMedia(content) {
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.video) {
|
||||
this.setState({ videoPostview: content });
|
||||
} else {
|
||||
this.setState({ imagePostview: content });
|
||||
}
|
||||
}
|
||||
|
||||
handleClosePreview() {
|
||||
@@ -732,7 +746,7 @@ class MessagesView extends React.Component {
|
||||
if (this.state.videoPreview && this.state.videoPreview.url) {
|
||||
URL.revokeObjectURL(this.state.videoPreview.url);
|
||||
}
|
||||
this.setState({ imagePostview: null, imagePreview: null, docPreview: null, videoPreview: null });
|
||||
this.setState({ imagePostview: null, imagePreview: null, docPreview: null, videoPreview: null, videoPostview: null});
|
||||
}
|
||||
|
||||
handleFormResponse(action, text, data) {
|
||||
@@ -912,7 +926,7 @@ class MessagesView extends React.Component {
|
||||
const mime = this.state.imagePreview.mime;
|
||||
const width = this.state.imagePreview.width;
|
||||
const height = this.state.imagePreview.height;
|
||||
const fname = this.state.imagePreview.name;
|
||||
const fname = this.state.imagePreview.filename;
|
||||
|
||||
// Server-provided limit reduced for base64 encoding and overhead.
|
||||
const maxInbandAttachmentSize = (this.props.tinode.getServerParam('maxMessageSize',
|
||||
@@ -1067,7 +1081,7 @@ class MessagesView extends React.Component {
|
||||
this.setState({videoPreview: {
|
||||
url: URL.createObjectURL(file),
|
||||
blob: file,
|
||||
name: file.name,
|
||||
filename: file.name,
|
||||
size: file.size,
|
||||
mime: file.type
|
||||
}});
|
||||
@@ -1080,7 +1094,7 @@ class MessagesView extends React.Component {
|
||||
this.setState({imagePreview: {
|
||||
url: URL.createObjectURL(scaled.blob),
|
||||
blob: scaled.blob,
|
||||
name: scaled.name,
|
||||
filename: scaled.name,
|
||||
width: scaled.width,
|
||||
height: scaled.height,
|
||||
size: scaled.blob.size,
|
||||
@@ -1309,6 +1323,14 @@ class MessagesView extends React.Component {
|
||||
content={this.state.imagePostview}
|
||||
onClose={this.handleClosePreview} />
|
||||
);
|
||||
} else if (this.state.videoPostview) {
|
||||
// Play received video.
|
||||
component2 = (
|
||||
<VideoPreview
|
||||
content={this.state.videoPostview}
|
||||
tinode={this.props.tinode}
|
||||
onClose={this.handleClosePreview} />
|
||||
);
|
||||
} else if (this.state.docPreview) {
|
||||
// Preview attachment before sending.
|
||||
component2 = (
|
||||
@@ -1432,7 +1454,7 @@ class MessagesView extends React.Component {
|
||||
userIsWriter={this.state.isWriter}
|
||||
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
|
||||
showContextMenu={this.handleShowMessageContextMenu}
|
||||
onImagePreview={this.handleImagePostview}
|
||||
onExpandMedia={this.handleExpandMedia}
|
||||
onFormResponse={this.handleFormResponse}
|
||||
onCancelUpload={this.handleCancelUpload}
|
||||
pickReply={this.handlePickReply}
|
||||
|
||||
@@ -23,7 +23,8 @@ class BaseChatMessage extends React.PureComponent {
|
||||
props.uploader.onProgress = this.handleProgress.bind(this);
|
||||
}
|
||||
|
||||
this.handleImagePreview = this.handleImagePreview.bind(this);
|
||||
this.handleExpandImage = this.handleExpandImage.bind(this);
|
||||
this.handlePlayVideo = this.handlePlayVideo.bind(this);
|
||||
this.handleFormButtonClick = this.handleFormButtonClick.bind(this);
|
||||
this.handleContextClick = this.handleContextClick.bind(this);
|
||||
this.handleCancelUpload = this.handleCancelUpload.bind(this);
|
||||
@@ -34,18 +35,18 @@ class BaseChatMessage extends React.PureComponent {
|
||||
viewportWidth: props.viewportWidth,
|
||||
authorizeURL: props.tinode.authorizeURL.bind(props.tinode),
|
||||
|
||||
onImagePreview: this.handleImagePreview,
|
||||
onVideoPreview: this.handleImagePreview,
|
||||
onImagePreview: this.handleExpandImage,
|
||||
onVideoPreview: this.handlePlayVideo,
|
||||
onFormButtonClick: this.handleFormButtonClick,
|
||||
onQuoteClick: this.handleQuoteClick
|
||||
};
|
||||
}
|
||||
|
||||
handleImagePreview(e) {
|
||||
handleExpandImage(e) {
|
||||
e.preventDefault();
|
||||
this.props.onImagePreview({
|
||||
this.props.onExpandMedia({
|
||||
url: e.target.src,
|
||||
filename: e.target.title,
|
||||
filename: e.target.dataset.name,
|
||||
width: e.target.dataset.width,
|
||||
height: e.target.dataset.height,
|
||||
size: e.target.dataset.size,
|
||||
@@ -53,6 +54,21 @@ class BaseChatMessage extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
handlePlayVideo(e) {
|
||||
e.preventDefault();
|
||||
this.props.onExpandMedia({
|
||||
video: true,
|
||||
url: e.target.dataset.src,
|
||||
preview: e.target.src,
|
||||
filename: e.target.dataset.name,
|
||||
width: e.target.dataset.width,
|
||||
height: e.target.dataset.height,
|
||||
duration: e.target.dataset.duration,
|
||||
size: e.target.dataset.size,
|
||||
type: e.target.dataset.mime
|
||||
});
|
||||
}
|
||||
|
||||
handleFormButtonClick(e) {
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
|
||||
@@ -47,25 +47,25 @@ export default class ImagePreview extends React.PureComponent {
|
||||
|
||||
// Average font aspect ratio is ~0.5; File name takes 1/3 of the viewport width.
|
||||
const maxlength = Math.max(((this.state.width / REM_SIZE / 1.5) | 0) - 2, 12);
|
||||
const fname = shortenFileName(this.props.content.name, maxlength) || '-';
|
||||
const fname = shortenFileName(this.props.content.filename, maxlength) || '-';
|
||||
|
||||
const width = this.props.content.width || '-';
|
||||
const height = this.props.content.height || '-';
|
||||
return (
|
||||
<div id="image-preview">
|
||||
<div id="image-preview-caption-panel">
|
||||
{!this.props.onSendMessage ?
|
||||
<a href={this.props.content.url} download={true}>
|
||||
{this.props.onSendMessage ?
|
||||
<span>{fname}</span>
|
||||
:
|
||||
<a href={this.props.content.url} download={this.props.content.filename}>
|
||||
<i className="material-icons">file_download</i> <FormattedMessage
|
||||
id="download_action" defaultMessage="download" description="Call to action [download]" />
|
||||
</a>
|
||||
:
|
||||
<span>{fname}</span>
|
||||
}
|
||||
<a href="#" onClick={(e) => {e.preventDefault(); this.props.onClose();}}><i className="material-icons gray">close</i></a>
|
||||
</div>
|
||||
<div id="image-preview-container" ref={(node) => this.assignWidth(node)}>
|
||||
<img src={this.props.content.url} style={size} className="image-preview" alt={this.props.content.name} />
|
||||
<img src={this.props.content.url} style={size} className="image-preview" alt={this.props.content.filename} />
|
||||
</div>
|
||||
{this.props.onSendMessage ?
|
||||
<SendMessage
|
||||
@@ -81,7 +81,7 @@ export default class ImagePreview extends React.PureComponent {
|
||||
<div>
|
||||
<div><b><FormattedMessage id="label_file_name" defaultMessage="File name:"
|
||||
description="Label for a file name" /></b></div>
|
||||
<div><span title={this.props.content.name}>{fname}</span></div>
|
||||
<div><span title={this.props.content.filename}>{fname}</span></div>
|
||||
</div>
|
||||
<div>
|
||||
<div><b><FormattedMessage id="label_content_type" defaultMessage="Content type:"
|
||||
|
||||
@@ -8,15 +8,27 @@ import { secondsToTime } from '../lib/strformat.js'
|
||||
export default class InlineVideo extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.videoRef = React.createRef();
|
||||
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
if (this.props.onClick) {
|
||||
// the click target has to be the image element.
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const duration = secondsToTime(this.props['data-duration'] / 1000);
|
||||
const className = 'inline-video' + (this.props.onClick ? ' clickable' : '');
|
||||
return (
|
||||
<div className="inline-video">
|
||||
<div className={className}>
|
||||
{React.createElement('img', this.props)}
|
||||
{this.props.onClick ?
|
||||
<div className="play-control" onClick={this.props.onClick}>
|
||||
<div className="play-control">
|
||||
<i className="material-icons white bigger">play_arrow</i>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import SendMessage from './send-message.jsx';
|
||||
|
||||
import { bytesToHumanSize } from '../lib/strformat.js';
|
||||
|
||||
export default class VideoPreview extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -17,7 +20,7 @@ export default class VideoPreview extends React.PureComponent {
|
||||
height: this.videoRef.current.videoHeight,
|
||||
duration: (this.videoRef.current.duration * 1000) | 0,
|
||||
mime: this.props.content.mime,
|
||||
name: this.props.content.name
|
||||
name: this.props.content.filename
|
||||
}
|
||||
|
||||
// Capture screen from a video.
|
||||
@@ -37,15 +40,27 @@ export default class VideoPreview extends React.PureComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
const width = this.props.content.width || '-';
|
||||
const height = this.props.content.height || '-';
|
||||
|
||||
const controlist = this.props.onSendMessage ? 'nodownload' : '';
|
||||
|
||||
return (
|
||||
<div id="image-preview">
|
||||
<div id="image-preview-caption-panel">
|
||||
<span>{this.props.content.name}</span>
|
||||
<span>{this.props.content.filename}</span>
|
||||
<a href="#" onClick={e => {e.preventDefault(); this.props.onClose();}}><i className="material-icons gray">close</i></a>
|
||||
</div>
|
||||
<div id="image-preview-container">
|
||||
<video controls ref={this.videoRef} src={this.props.content.url} className="image-preview" alt={this.props.content.name} />
|
||||
<video
|
||||
className="image-preview"
|
||||
controls controlist={controlist}
|
||||
disablePictureInPicture ref={this.videoRef}
|
||||
src={this.props.tinode.authorizeURL(this.props.content.url)}
|
||||
poster={this.props.content.preview}
|
||||
alt={this.props.content.filename} />
|
||||
</div>
|
||||
{this.props.onSendMessage ?
|
||||
<SendMessage
|
||||
messagePrompt="add_image_caption"
|
||||
acceptBlank={true}
|
||||
@@ -54,6 +69,24 @@ export default class VideoPreview extends React.PureComponent {
|
||||
onCancelReply={this.props.onCancelReply}
|
||||
onSendMessage={this.handleSendVideo}
|
||||
onError={this.props.onError} />
|
||||
:
|
||||
<div id="image-preview-footer">
|
||||
<div>
|
||||
<div><b><FormattedMessage id="label_file_name" defaultMessage="File name:"
|
||||
description="Label for a file name" /></b></div>
|
||||
<div><span title={this.props.content.filename}>{this.props.content.filename}</span></div>
|
||||
</div>
|
||||
<div>
|
||||
<div><b><FormattedMessage id="label_content_type" defaultMessage="Content type:"
|
||||
description="Label for file content type (mime)" /></b></div>
|
||||
<div>{this.props.content.type}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div><b><FormattedMessage id="label_size" defaultMessage="Size:"
|
||||
description="Label for file size" /></b></div>
|
||||
<div>{width} × {height} px; {bytesToHumanSize(this.props.content.size)}</div>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user