Skip to content
This repository has been archived by the owner on Jun 30, 2022. It is now read-only.

Commit

Permalink
[NEW] Audio and Video calling in Livechat using WebRTC #646
Browse files Browse the repository at this point in the history
* add callnotification,msg,iframe code

* fix eslint issues

* fix eslint file path issues

* fix stylelint issues

* done ui changes

* add icons and improve ui

* fix error

* remove-icons

* add icons

* add i18n.t, change classname, improve ui

* remove font-family

* change iframe link

* add check for jitsi

* add jitsi link

* add time message in system message and add catch

* display alert, change function name

* Improve overall codebase

* Convert tabs to spaces for translation file

* Accept jitsi url info from the message and remove dependency from Livechat.videoCall endpoint

* Update index.js

* add message in i18n file

Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com>
add joinCallBtn, endCall message, callRing functionalities. (#618)

* add callnotification,msg,iframe code

* fix eslint issues

* fix eslint file path issues

* fix stylelint issues

* done ui changes

* add join call button

* add icons and improve ui

* fix error

* remove-icons

* add icons

* ui changes

* add icon and timeout functionality

* add I18n.t

* add i18n.t, change classname, improve ui

* remove font-family

* change iframe link

* add check for jitsi

* add jitsi link

* add time message in system message and add catch

* display alert, change function name

* Improve overall codebase

* Convert tabs to spaces for translation file

* Accept jitsi url info from the message and remove dependency from Livechat.videoCall endpoint

* Update index.js

* add message in i18n file

* add joincall btn, call time, timeout

* fix lint error

* Update ShowJoinCallButton.js

* fix conflicts

* improve codebase

* fix bugs

* update some files

* improve css, codebase and change condition

* add correct link for iframe and joinCallBtn, show correct time

* update call status at rc-core

* update api file

* rebase file

* change link, use store class

* update callIframe file

* handle corner cases

* remove file

* Update room.js file

Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com>

* update one file

* revert roomjs file

* add common condition in room.js file

Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com>
[NEW] Handle endCall and expandView control buttons (#633)

* [NEW] Handle endCall and expandView control buttons

* [FIX] Lint errors

* [FIX] Join Call Button giving call ended even when call inProgress

* [FIX] slash omitted in the url

* [REF] Remove redundant async keyword

* [REF] Use spread operator, set ongoingCall status when expanding

* [FIX] String enclosed within double quotes

Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com>

* [FIX] ongoingCall variable not destructured but used

Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com>
[FIX] Notify agent on call decline (#634)

[NEW] WebRTC-call in new tab for mobile devices (#629)

* open webrtc call in new tab for mobile devices

* improve codebase

* add helper function

* update one file

* checking call status from room object

* update one file

* handle refresh case

* change message filter condition

Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com>
[NEW] add-jitsi-call-support

Co-Authored-By: Deepak Agarwal <56799414+Deepak-learner@users.noreply.github.com>

Improve overall codebase

- Define an enum like structure for call statuses
- Fix formatting and spelling issues
- General refactoring

update Rocket.Chat.js.SDK version to 1.0.0-alpha.42

Fix Join call button layout

Fix typo

Apply review suggestions

- Use date-fns library to calculate call duration

Refactor Call Statuses to remove redundancy

Rename endTs property on Message to webRtcCallEndTs
  • Loading branch information
MartinSchoeler committed Nov 22, 2021
1 parent 7034970 commit da5aaa8
Show file tree
Hide file tree
Showing 26 changed files with 519 additions and 17 deletions.
Binary file modified .loki/reference/chrome_Components_Icons_all.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .loki/reference/chrome_Components_Icons_phone.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .loki/reference/chrome_Components_Icons_video.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
},
"dependencies": {
"@kossnocorp/desvg": "^0.2.0",
"@rocket.chat/sdk": "^1.0.0-alpha.41",
"@rocket.chat/sdk": "^1.0.0-alpha.42",
"@rocket.chat/ui-kit": "^0.14.1",
"css-vars-ponyfill": "^2.3.2",
"date-fns": "^2.15.0",
Expand Down
32 changes: 32 additions & 0 deletions src/components/Calls/CallIFrame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { h } from 'preact';

import { Livechat } from '../../api';
import store from '../../store';
import { createClassName } from '../helpers';
import { CallStatus } from './CallStatus';
import styles from './styles.scss';


export const CallIframe = () => {
const { token, room, incomingCallAlert, ongoingCall } = store.state;
const url = `${ Livechat.client.host }/meet/${ room._id }?token=${ token }&layout=embedded`;
window.handleIframeClose = () => store.setState({ incomingCallAlert: { ...incomingCallAlert, show: false } });
window.expandCall = () => {
window.open(
`${ Livechat.client.host }/meet/${ room._id }?token=${ token }`,
room._id,
);
return store.setState({
incomingCallAlert: { ...incomingCallAlert, show: false },
ongoingCall: {
...ongoingCall,
callStatus: CallStatus.IN_PROGRESS_DIFFERENT_TAB,
},
});
};
return (
<div className={createClassName(styles, 'call-iframe')}>
<iframe className={createClassName(styles, 'call-iframe__content')} allow='camera;microphone' src={url} />
</div>
);
};
111 changes: 111 additions & 0 deletions src/components/Calls/CallNotification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { h } from 'preact';
import { useState } from 'preact/compat';

import { Livechat } from '../../api';
import I18n from '../../i18n';
import PhoneAccept from '../../icons/phone.svg';
import PhoneDecline from '../../icons/phoneOff.svg';
import constants from '../../lib/constants';
import store from '../../store';
import { Avatar } from '../Avatar';
import { Button } from '../Button';
import { createClassName, getAvatarUrl, isMobileDevice } from '../helpers';
import { CallStatus } from './CallStatus';
import styles from './styles.scss';


export const CallNotification = ({
callProvider,
callerUsername,
url,
dispatch,
time,
rid,
callId,
}) => {
const [show, setShow] = useState(true);

const callInNewTab = async () => {
const { token } = store.state;
const url = `${ Livechat.client.host }/meet/${ rid }?token=${ token }`;
await dispatch({
ongoingCall: {
callStatus: CallStatus.IN_PROGRESS_DIFFERENT_TAB,
time: { time },
},
incomingCallAlert: {
show: false,
callProvider,
},
});
window.open(url, rid);
};

const acceptClick = async () => {
setShow(!{ show });
switch (callProvider) {
case constants.jitsiCallStartedMessageType: {
window.open(url, rid);
await dispatch({
incomingCallAlert: { show: false, url, callProvider },
ongoingCall: {
callStatus: CallStatus.IN_PROGRESS_DIFFERENT_TAB,
time: { time },
},
});
break;
}
case constants.webRTCCallStartedMessageType: {
await Livechat.updateCallStatus(CallStatus.IN_PROGRESS, rid, callId);
if (isMobileDevice()) {
callInNewTab();
break;
}
await dispatch({ ongoingCall: { callStatus: CallStatus.IN_PROGRESS_SAME_TAB, time: { time } } });
break;
}
}
};

const declineClick = async () => {
await Livechat.updateCallStatus(CallStatus.DECLINED, rid, callId);
await Livechat.notifyCallDeclined(rid);
await dispatch({
incomingCallAlert: null,
ongoingCall: {
callStatus: CallStatus.DECLINED,
time: { time },
},
});
};

return (
<div className={createClassName(styles, 'call-notification')}>
{
show && (
<div className = { createClassName(styles, 'call-notification__content') }>
<div className = { createClassName(styles, 'call-notification__content-avatar') }>
<Avatar src = { getAvatarUrl(callerUsername) } large />
</div>
<div className = { createClassName(styles, 'call-notification__content-message') }>
{ I18n.t('Incoming video Call') }
</div>
<div className = { createClassName(styles, 'call-notification__content-actions') }>
<Button
onClick = { declineClick }
className = { createClassName(styles, 'call-notification__content-actions-decline') }>
<PhoneDecline width = { 20 } height = { 20 } />
<span style='margin-left:5px'> {I18n.t('Decline')} </span >
</Button>
<Button onClick = { acceptClick }
className = {createClassName(styles, 'call-notification__content-actions-accept') }>
<PhoneAccept width = { 20 } height = { 20} />
<span style='margin-left:5px'> {I18n.t('Accept')} </span >
</Button>
</div>
</div>
)
}
</div>
);
};
12 changes: 12 additions & 0 deletions src/components/Calls/CallStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const CallStatus = {
RINGING: 'ringing',
DECLINED: 'declined',
IN_PROGRESS: 'inProgress', // although on Livechat we only use "IN_PROGRESS_SAME_TAB" and "IN_PROGRESS_DIFFERENT_TAB", we still need this status since on Rocket.Chat core, this is the status of ongoing calls
IN_PROGRESS_SAME_TAB: 'inProgressSameTab',
IN_PROGRESS_DIFFERENT_TAB: 'inProgressDifferentTab',
ENDED: 'ended',
};

export const isCallOngoing = (callStatus) => callStatus === CallStatus.IN_PROGRESS
|| callStatus === CallStatus.IN_PROGRESS_DIFFERENT_TAB
|| callStatus === CallStatus.IN_PROGRESS_SAME_TAB;
50 changes: 50 additions & 0 deletions src/components/Calls/JoinCallButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { h } from 'preact';

import { Livechat } from '../../api';
import I18n from '../../i18n';
import VideoIcon from '../../icons/video.svg';
import constants from '../../lib/constants';
import store from '../../store';
import { Button } from '../Button';
import { createClassName } from '../helpers';
import { isCallOngoing } from './CallStatus';
import styles from './styles.scss';


export const JoinCallButton = (props) => {
const { token, room } = store.state;

const clickJoinCall = () => {
switch (props.callProvider) {
case constants.jitsiCallStartedMessageType: {
window.open(props.url, room._id);
break;
}
case constants.webRTCCallStartedMessageType: {
window.open(`${ Livechat.client.host }/meet/${ room._id }?token=${ token }`, room._id);
break;
}
}
};
return (
<div className={createClassName(styles, 'joinCall')}>
{
isCallOngoing(props.callStatus)
&& (
<div>
<div className={createClassName(styles, 'joinCall__content')} >
<div className={createClassName(styles, 'joinCall__content-videoIcon')} >
<VideoIcon width={20} height={20} />
</div>
{ I18n.t('Join my room to start the video call') }
</div>
<Button onClick={clickJoinCall} className={createClassName(styles, 'joinCall__content-action')}>
<VideoIcon width={20} height={20} />
{I18n.t('Join Call')}
</Button>
</div>
)
}
</div>
);
};
128 changes: 128 additions & 0 deletions src/components/Calls/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
@import '../../styles/colors';
@import '../../styles/variables';

.call-notification {
position: relative;

display: flex;

width: 100%;
height: 50%;

&__content {
display: flex;
flex-direction: column;

width: 100%;
height: 100%;

background: #1f2329;

font-weight: 600;
justify-content: space-evenly;

&-avatar {
display: flex;

margin: 0 auto;
align-self: flex-end;
}

&-message {
margin: 0 auto;

color: #ffffff;
}

&-actions {
display: flex;
flex-direction: row;

margin: 0 auto;
margin-bottom: 15px;

color: white;

align-items: flex-end;

> button {
margin-bottom: 0;
margin-left: 10px;
}

&-accept {
border-color: green;
background-color: #2de0a5;
}

&-decline {
border-color: red;
background-color: #f5455c;
}
}
}
}

.call-iframe {
position: absolute;
top: 0;

width: 100%;
height: 41%;

&__content {
width: 100%;
height: 100%;
}
}

.joinCall {
width: 300px;
margin: 15px;

padding: 5px;

border: 1px solid #e4e7ea;

&__content {
display: flex;
flex-direction: row;

padding: 15px;

line-height: 16px;
justify-content: space-around;

&-videoIcon {
display: flex;

height: 7%;
margin-right: 10px;

padding: 5px;

border: 1px solid white;
background-color: #d1ebfe;
}

&-action {
display: block;

width: 120px;
margin-top: 0;
margin-bottom: 3%;
margin-left: 20%;
padding: 5px;

color: white;
border: 1px solid blue;
background-color: #1d74f5;
}
}
}

@media screen and (min-width: 410px) {
.joinCall {
margin-left: 3%;
}
}
Loading

0 comments on commit da5aaa8

Please sign in to comment.