Move video call message state and duration to head.

'webrtc' header for state.
'webrtc-duration' header for duration.

Content is now Drafty.videoCall() / VC.
This commit is contained in:
aforge
2022-05-29 15:29:13 +03:00
parent df460c6b61
commit 3c87341280
13 changed files with 476 additions and 641 deletions
+3
View File
@@ -6,3 +6,6 @@ export const CALL_STATE_IN_PROGRESS = 3;
// Mime type used for video call messages. // Mime type used for video call messages.
export const CALL_MESSAGE_MIME_TYPE = 'application/x-tinode-webrtc'; export const CALL_MESSAGE_MIME_TYPE = 'application/x-tinode-webrtc';
// Video call drafty headers representing call state.
export const CALL_HEAD_STARTED = 'started';
+16
View File
@@ -4,6 +4,8 @@ import { defineMessages } from 'react-intl';
import { Drafty } from 'tinode-sdk'; import { Drafty } from 'tinode-sdk';
import AudioPlayer from '../widgets/audio-player.jsx' import AudioPlayer from '../widgets/audio-player.jsx'
import CallMessage from '../widgets/call-message.jsx'
import CallStatus from '../widgets/call-status.jsx';
import LazyImage from '../widgets/lazy-image.jsx' import LazyImage from '../widgets/lazy-image.jsx'
import UploadingImage from '../widgets/uploading-image.jsx' import UploadingImage from '../widgets/uploading-image.jsx'
@@ -163,6 +165,14 @@ export function fullFormatter(style, data, values, key, stack) {
attr.className = 'reply-quote' attr.className = 'reply-quote'
attr.onClick = this.onQuoteClick; attr.onClick = this.onQuoteClick;
break; break;
case 'VC':
el = CallMessage;
// Video call messages do not have content.
values = null;
attr.content = this.callState;
attr.response = this.isResponse;
attr.duration = this.callDuration;
break;
default: default:
if (!el) { if (!el) {
// Unknown element. // Unknown element.
@@ -245,6 +255,12 @@ export function previewFormatter(style, data, values, key) {
el = React.Fragment; el = React.Fragment;
values = [<i key="ex" className="material-icons">attachment</i>, ' ', this.formatMessage(messages.drafty_attachment)]; values = [<i key="ex" className="material-icons">attachment</i>, ' ', this.formatMessage(messages.drafty_attachment)];
break; break;
case 'VC':
el = CallStatus;
attr.incoming = this.previewIsResponse;
attr.success = true; // FIXME: use actuall call completion status instead of TRUE.
values = null;
break;
case 'QQ': case 'QQ':
case 'HD': case 'HD':
el = null; el = null;
+2 -8
View File
@@ -1137,20 +1137,14 @@ class MessagesView extends React.Component {
); );
prevDate = thisDate; prevDate = thisDate;
} }
let duration; const duration = msg.head ? msg.head['webrtc-duration'] : undefined;
if (msg.head && isVideoCall(msg.head.mime)) {
topic.messageVersions(msg, (cur, before) => {
if (cur.content == 'finished' && before.content == 'accepted') {
duration = msg.ts - before.ts;
}
});
}
messageNodes.push( messageNodes.push(
<ChatMessage <ChatMessage
tinode={this.props.tinode} tinode={this.props.tinode}
content={msg.content} content={msg.content}
mimeType={msg.head ? msg.head.mime : null} mimeType={msg.head ? msg.head.mime : null}
timestamp={msg.ts} timestamp={msg.ts}
callState={msg.head ? msg.head.webrtc : null}
duration={duration} duration={duration}
response={isReply} response={isReply}
seq={msg.seq} seq={msg.seq}
+6 -3
View File
@@ -21,7 +21,7 @@ import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, FORWARDED_PREVIEW_LENGTH, L
MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js'; MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';
import { CALL_STATE_NONE, CALL_STATE_OUTGOING_INITATED, import { CALL_STATE_NONE, CALL_STATE_OUTGOING_INITATED,
CALL_STATE_INCOMING_RECEIVED, CALL_STATE_IN_PROGRESS, CALL_STATE_INCOMING_RECEIVED, CALL_STATE_IN_PROGRESS,
CALL_MESSAGE_MIME_TYPE } from '../constants.js'; CALL_MESSAGE_MIME_TYPE, CALL_HEAD_STARTED } from '../constants.js';
import { PACKAGE_VERSION } from '../version.js'; import { PACKAGE_VERSION } from '../version.js';
import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js'; import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';
import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js'; import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';
@@ -1687,8 +1687,11 @@ class TinodeWeb extends React.Component {
handleCallInvite(callTopic, callSeq, callState) { handleCallInvite(callTopic, callSeq, callState) {
switch (callState) { switch (callState) {
case CALL_STATE_OUTGOING_INITATED: case CALL_STATE_OUTGOING_INITATED:
let head = {mime: CALL_MESSAGE_MIME_TYPE }; let head = {
this.handleSendMessage('started', undefined, undefined, head) mime: CALL_MESSAGE_MIME_TYPE,
webrtc: CALL_HEAD_STARTED
};
this.handleSendMessage(Drafty.videoCall(), undefined, undefined, head)
.then((ctrl) => { .then((ctrl) => {
if (ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) { if (ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) {
this.handleCallClose(); this.handleCallClose();
+1 -1
View File
@@ -11,7 +11,7 @@ export default class CallStatus extends React.PureComponent {
return ( return (
<div className="composed-material"> <div className="composed-material">
<i class="material-icons">call</i><i class="material-icons second">{icon2}</i> <i className="material-icons">call</i><i className="material-icons second">{icon2}</i>
</div> </div>
); );
} }
+8 -8
View File
@@ -5,10 +5,10 @@ import { FormattedMessage, injectIntl } from 'react-intl';
import { Drafty, Tinode } from 'tinode-sdk'; import { Drafty, Tinode } from 'tinode-sdk';
import Attachment from './attachment.jsx'; import Attachment from './attachment.jsx';
import CallMessage from './call-message.jsx';
import LetterTile from './letter-tile.jsx'; import LetterTile from './letter-tile.jsx';
import ReceivedMarker from './received-marker.jsx' import ReceivedMarker from './received-marker.jsx'
import { CALL_MESSAGE_MIME_TYPE } from '../constants.js'
import { fullFormatter } from '../lib/formatters.js'; import { fullFormatter } from '../lib/formatters.js';
import { isVideoCall, sanitizeUrl } from '../lib/utils.js'; import { isVideoCall, sanitizeUrl } from '../lib/utils.js';
@@ -34,6 +34,12 @@ class BaseChatMessage extends React.PureComponent {
formatMessage: props.intl.formatMessage.bind(props.intl), formatMessage: props.intl.formatMessage.bind(props.intl),
viewportWidth: props.viewportWidth, viewportWidth: props.viewportWidth,
authorizeURL: props.tinode.authorizeURL.bind(props.tinode), authorizeURL: props.tinode.authorizeURL.bind(props.tinode),
// Video call related bits.
callState: props.callState,
isResponse: props.response,
callDuration: props.duration,
onImagePreview: this.handleImagePreview, onImagePreview: this.handleImagePreview,
onFormButtonClick: this.handleFormButtonClick, onFormButtonClick: this.handleFormButtonClick,
onQuoteClick: this.handleQuoteClick onQuoteClick: this.handleQuoteClick
@@ -120,7 +126,7 @@ class BaseChatMessage extends React.PureComponent {
let content = this.props.content; let content = this.props.content;
const attachments = []; const attachments = [];
if (this.props.mimeType == Drafty.getContentType() && Drafty.isValid(content)) { if ([Drafty.getContentType(), CALL_MESSAGE_MIME_TYPE].includes(this.props.mimeType) && Drafty.isValid(content)) {
Drafty.attachments(content, (att, i) => { Drafty.attachments(content, (att, i) => {
if (att.mime == 'application/json') { if (att.mime == 'application/json') {
// Don't show json objects as attachments. // Don't show json objects as attachments.
@@ -147,12 +153,6 @@ class BaseChatMessage extends React.PureComponent {
<FormattedMessage id="deleted_content" <FormattedMessage id="deleted_content"
defaultMessage="content deleted" description="Shown when messages are deleted" /> defaultMessage="content deleted" description="Shown when messages are deleted" />
</i></> </i></>
} else if (isVideoCall(this.props.mimeType)) {
// Video call message.
content = <CallMessage
content={this.props.content}
response={this.props.response}
duration={this.props.duration} />
} else if (typeof content != 'string') { } else if (typeof content != 'string') {
content = <><i className="material-icons gray">warning_amber</i> <i className="gray"> content = <><i className="material-icons gray">warning_amber</i> <i className="gray">
<FormattedMessage id="invalid_content" <FormattedMessage id="invalid_content"
+2 -8
View File
@@ -2,7 +2,6 @@
import React from 'react'; import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl'; import { FormattedMessage, injectIntl } from 'react-intl';
import CallStatus from './call-status.jsx';
import ContactBadges from './contact-badges.jsx'; import ContactBadges from './contact-badges.jsx';
import LetterTile from './letter-tile.jsx'; import LetterTile from './letter-tile.jsx';
import UnreadBadge from './unread-badge.jsx'; import UnreadBadge from './unread-badge.jsx';
@@ -71,16 +70,11 @@ class Contact extends React.Component {
// The this.props.preview contains alreay shortened Drafty or string. // The this.props.preview contains alreay shortened Drafty or string.
let preview; let preview;
if (this.props.previewIsVideoCall) { if (typeof this.props.preview == 'string') {
// TODO: use words to describe call status and duration.
preview = <CallStatus
incoming={this.props.previewIsResponse}
success={true} />; // FIXME: use actuall call completion status instead of TRUE.
} else if (typeof this.props.preview == 'string') {
preview = this.props.preview; preview = this.props.preview;
} else if (Drafty.isValid(this.props.preview)) { } else if (Drafty.isValid(this.props.preview)) {
preview = React.createElement(React.Fragment, null, Drafty.format(this.props.preview, previewFormatter, preview = React.createElement(React.Fragment, null, Drafty.format(this.props.preview, previewFormatter,
{formatMessage: this.props.intl.formatMessage})); {formatMessage: this.props.intl.formatMessage, previewIsResponse: this.props.previewIsResponse}));
} else if (this.props.preview) { } else if (this.props.preview) {
preview = preview =
<><i className="material-icons gray">warning_amber</i> <i className="gray"> <><i className="material-icons gray">warning_amber</i> <i className="gray">
+360 -521
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+74 -88
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long