|
@@ -24,57 +24,75 @@
|
|
|
|
|
|
<div id="proCallComponent">
|
|
|
|
|
|
- @if($client)
|
|
|
- <div class="text-center py-2 border-bottom font-weight-normal mcp-theme-1">
|
|
|
- <i class="fa fa-user-injured small mr-2"></i><a href="#" onclick="return window.top.openInLHS('/patients/view/{{$client->uid}}')">
|
|
|
- <span class="font-weight-bold">{{ $client->displayName() }}</span>
|
|
|
- </a>
|
|
|
- </div>
|
|
|
- @endif
|
|
|
-
|
|
|
- <div class="py-2 d-flex align-items-center justify-content-center border-bottom">
|
|
|
- <a href="#" v-if="ringer" v-on:click.prevent="toggleRinger()"
|
|
|
- class="font-weight-bold btn btn-sm btn-success">
|
|
|
- Ringer
|
|
|
- <i class="ml-1 fa fa-volume-up"></i>
|
|
|
- </a>
|
|
|
- <a href="#" v-if="!ringer" v-on:click.prevent="toggleRinger()"
|
|
|
- class="font-weight-bold btn btn-sm btn-warning">
|
|
|
- Ringer
|
|
|
- <i class="ml-1 fa fa-volume-mute"></i>
|
|
|
- </a>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="" v-show="false">
|
|
|
- <div class="py-3 text-center" xv-if="started">
|
|
|
- <h6 class="text-black font-weight-bold m-0">Call in progress: @{{ timeDisplay() }}</h6>
|
|
|
+ <div class="d-flex px-2 border-bottom mb-2 {{ $client ? '' : 'justify-content-center' }}">
|
|
|
+ @if($client)
|
|
|
+ <div class="py-2 font-weight-normal mcp-theme-1 flex-grow-1">
|
|
|
+ <i class="fa fa-user-injured small mr-2"></i><a href="#" onclick="return window.top.openInLHS('/patients/view/{{$client->uid}}')">
|
|
|
+ <span class="font-weight-bold">{{ $client->displayName() }}</span>
|
|
|
+ <span class="text-secondary ml-4">{{ $pro->displayName() }}</span>
|
|
|
+ <span class="text-secondary ml-4">{{ date('H:i:s') }}</span>
|
|
|
+ </a>
|
|
|
</div>
|
|
|
- <div class="py-3 text-center" v-if="!otherParticipants.length">
|
|
|
- <h6 class="text-black font-weight-bold m-0">No other participants in the call.
|
|
|
- <a href="#" class="text-danger font-weight-bold" v-on:click.prevent="hangUp()">Hang up</a>
|
|
|
- </h6>
|
|
|
+ @endif
|
|
|
+
|
|
|
+ <div class="py-2 d-flex align-items-center">
|
|
|
+ <a href="#" v-if="ringer" v-on:click.prevent="toggleRinger()"
|
|
|
+ class="font-weight-bold btn btn-sm btn-success">
|
|
|
+ Ringer
|
|
|
+ <i class="ml-1 fa fa-volume-up"></i>
|
|
|
+ </a>
|
|
|
+ <a href="#" v-if="!ringer" v-on:click.prevent="toggleRinger()"
|
|
|
+ class="font-weight-bold btn btn-sm btn-warning">
|
|
|
+ Ringer
|
|
|
+ <i class="ml-1 fa fa-volume-mute"></i>
|
|
|
+ </a>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ @if($client)
|
|
|
+ <div class="">
|
|
|
<div class="main-view mx-auto">
|
|
|
- <div id="self-view" class="full-view" data-stream="self" data-name="You" data-type="PRO"></div>
|
|
|
+ <div id="self-view" class="full-view" :data-uid="myMediaServiceIdentifier" data-name="You" data-type="PRO"></div>
|
|
|
<div class="thumbs">
|
|
|
-
|
|
|
+ <div v-for="participant in otherParticipants"
|
|
|
+ :id="'remote-view-' + participant.uid"
|
|
|
+ :data-uid="participant.mediaServiceIdentifier"
|
|
|
+ :data-name="participant.displayName"
|
|
|
+ :data-type="participant.participantType"
|
|
|
+ :data-audio="participant.media.isMicrophoneOn ? 'on' : 'off'"
|
|
|
+ class="remote-view thumb-view">
|
|
|
+ <i v-show="!participant.media.isMicrophoneOn" class="fa fa-microphone-slash muted"></i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="call-actions d-flex align-items-center">
|
|
|
+ <button class="btn btn-danger rounded-circle"
|
|
|
+ title="Leave Call"
|
|
|
+ v-on:click.prevent="leaveClientRoom()">
|
|
|
+ <i class="fa fa-phone"></i>
|
|
|
+ </button>
|
|
|
+ <button v-if="myMedia.isCameraOn" class="btn btn-default bg-light border rounded-circle"
|
|
|
+ title="Stop Camera"
|
|
|
+ v-on:click.prevent="myCameraIsOff()">
|
|
|
+ <i class="fa fa-video"></i>
|
|
|
+ </button>
|
|
|
+ <button v-if="!myMedia.isCameraOn" class="btn btn-secondary rounded-circle"
|
|
|
+ title="Start Camera"
|
|
|
+ v-on:click.prevent="myCameraIsOn()">
|
|
|
+ <i class="fa fa-video-slash"></i>
|
|
|
+ </button>
|
|
|
+ <button v-if="myMedia.isMicrophoneOn" class="btn btn-default bg-light border rounded-circle"
|
|
|
+ title="Stop Microphone"
|
|
|
+ v-on:click.prevent="myMicrophoneIsOff()">
|
|
|
+ <i class="fa fa-microphone"></i>
|
|
|
+ </button>
|
|
|
+ <button v-if="!myMedia.isMicrophoneOn" class="btn btn-secondary rounded-circle"
|
|
|
+ title="Start Microphone"
|
|
|
+ v-on:click.prevent="myMicrophoneIsOn()">
|
|
|
+ <i class="fa fa-microphone-slash"></i>
|
|
|
+ </button>
|
|
|
</div>
|
|
|
- <button class="btn btn-danger rounded-circle hang-up"
|
|
|
- xv-if="started"
|
|
|
- title="Leave Call"
|
|
|
- v-on:click.prevent="hangUp()">
|
|
|
- <i class="fa fa-phone"></i>
|
|
|
- </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- @if($client)
|
|
|
- {{--<div class="" v-show="!videoActive">
|
|
|
- <button class="btn btn-sm btn-primary font-weight-bold mx-auto mt-4 d-block"
|
|
|
- v-on:click.prevent="connect()">
|
|
|
- Start video call with {{ $client->displayName() }}
|
|
|
- </button>
|
|
|
- </div>--}}
|
|
|
@endif
|
|
|
|
|
|
</div>
|
|
@@ -100,7 +118,7 @@
|
|
|
|
|
|
<script>
|
|
|
(function () {
|
|
|
- new Vue({
|
|
|
+ window.proCallComponent = new Vue({
|
|
|
el: '#proCallComponent',
|
|
|
delimiters: ['@{{', '}}'],
|
|
|
data: {
|
|
@@ -144,15 +162,24 @@
|
|
|
awayMessage: '',
|
|
|
|
|
|
// agora
|
|
|
- agoraClient: null, // set on agora init
|
|
|
+ mediaServiceClient: null, // set on agora init
|
|
|
appId: '{{ config('app.agora_appid') }}',
|
|
|
channel: '', // set on mount
|
|
|
- ringer: {{ $pro->is_ring_on ? 'true' : 'false' }},
|
|
|
+ myMicrophone: null,
|
|
|
+ myCamera: null,
|
|
|
|
|
|
// sockets
|
|
|
backendWsURL: '{{ config('app.backend_ws_url') }}',
|
|
|
socketClient: null,
|
|
|
|
|
|
+ // other
|
|
|
+ ringer: {{ $pro->is_ring_on ? 'true' : 'false' }},
|
|
|
+
|
|
|
+ // agora <-> WS sync
|
|
|
+ unrenderedParticipants: [], // exists in otherParticipants, but not yet in DOM
|
|
|
+ unresolvedParticipants: [], // does not exist in otherParticipants, but came in via Agora
|
|
|
+ unrenderedParticipantsTimer: false,
|
|
|
+ unresolvedParticipantsTimer: false,
|
|
|
},
|
|
|
methods: {
|
|
|
|
|
@@ -170,132 +197,6 @@
|
|
|
}
|
|
|
}, 'json');
|
|
|
},
|
|
|
- connect: function () {
|
|
|
- var self = this;
|
|
|
- self.selfName = '{{ $pro->name_display }}';
|
|
|
- $.get('/api/agora/getClientToken', {
|
|
|
- clientUid: self.clientUid,
|
|
|
- }, function (_data) {
|
|
|
- console.log(_data);
|
|
|
- self.selfToken = _data.data;
|
|
|
- self.initAgora();
|
|
|
- });
|
|
|
- },
|
|
|
- timeDisplay: function () {
|
|
|
- var seconds = this.time / 1000,
|
|
|
- minutes = parseInt(seconds / 60, 10);
|
|
|
- seconds = parseInt(seconds % 60, 10);
|
|
|
- return minutes + " min, " + seconds + " sec";
|
|
|
- },
|
|
|
- hangUp: function () {
|
|
|
- var self = this;
|
|
|
- async function _leave() {
|
|
|
- if(self.agoraClient) {
|
|
|
- await self.agoraClient.leave();
|
|
|
- window.top.hideRHS();
|
|
|
- window.location.reload();
|
|
|
- }
|
|
|
- }
|
|
|
- _leave();
|
|
|
- },
|
|
|
- initAgora: function () {
|
|
|
-
|
|
|
- let self = this;
|
|
|
-
|
|
|
- async function _initAgora(){
|
|
|
-
|
|
|
- self.agoraClient = AgoraRTC.createClient({mode:'rtc', codec:'h264'})
|
|
|
- let camera, mic
|
|
|
- try { mic = await AgoraRTC.createMicrophoneAudioTrack() } catch {
|
|
|
- console.log('ALIX: error in getting mic');
|
|
|
- }
|
|
|
- try { camera = await AgoraRTC.createCameraVideoTrack() } catch {
|
|
|
- console.log('ALIX: error in getting camera');
|
|
|
- }
|
|
|
-
|
|
|
- // testing
|
|
|
- @if(config('app.agora_mode') === 'screen')
|
|
|
- try { camera = await AgoraRTC.createScreenVideoTrack() } catch { }
|
|
|
- @endif
|
|
|
-
|
|
|
- if (!mic && !camera){
|
|
|
- alert('Do you have camera/mic? Unable to hear or see you.')
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // Add myself to the page.
|
|
|
- if(camera) {
|
|
|
- camera.play($('#self-view')[0]);
|
|
|
- }
|
|
|
-
|
|
|
- // events
|
|
|
- self.agoraClient.on('user-joined', user => {
|
|
|
-
|
|
|
- // add a div for remove view
|
|
|
- $('[data-stream="' + user.uid + '"]').remove();
|
|
|
- var remoteViewID = 'remote-view-' + user.uid;
|
|
|
- var remoteElem = $('<div id="' + remoteViewID + '" class="remote-view thumb-view" data-stream="' + user.uid + '"></div>');
|
|
|
- remoteElem.appendTo('.thumbs');
|
|
|
-
|
|
|
- if (!self.startTime) {
|
|
|
- self.startTime = new Date().getTime();
|
|
|
- window.setInterval(function () {
|
|
|
- self.time = new Date().getTime() - self.startTime;
|
|
|
- }, 1000);
|
|
|
- self.started = true;
|
|
|
- }
|
|
|
- self.activateParty(user.uid);
|
|
|
- self.noOneElseInCall = false;
|
|
|
- self.resolveParticipantNames();
|
|
|
- })
|
|
|
- self.agoraClient.on('user-left', user => {
|
|
|
-
|
|
|
- if ($('.full-view[data-stream="' + user.uid + '"]').length) {
|
|
|
- var allThumbs = $('.thumbs [data-stream]:not([data-stream=""]):not(.disconnected-view):visible');
|
|
|
- if (allThumbs.length) {
|
|
|
- $('.thumbs [data-stream]:not([data-stream=""])').each(function () {
|
|
|
- if ($(this).attr('data-stream') !== user.uid) {
|
|
|
- self.activateParty($(this).attr('data-stream'));
|
|
|
- return false;
|
|
|
- }
|
|
|
- });
|
|
|
- } else {
|
|
|
- self.noOneElseInCall = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- $('[data-stream="' + user.uid + '"]').remove();
|
|
|
-
|
|
|
- // if no other parties in call, hang up
|
|
|
- if (!$('[data-stream]:not([data-stream="' + {{ $session->id }} + '"])').length) {
|
|
|
- console.warn('No other parties in the call!');
|
|
|
- self.startTime = 0;
|
|
|
- self.started = false;
|
|
|
- self.noOneElseInCall = true;
|
|
|
- }
|
|
|
- })
|
|
|
- self.agoraClient.on('user-published', async function(user, mediaType){
|
|
|
- await self.agoraClient.subscribe(user, mediaType)
|
|
|
- mediaType === 'audio'
|
|
|
- ? user.audioTrack.play()
|
|
|
- : user.videoTrack.play($('[data-stream="' + user.uid + '"]')[0]);
|
|
|
- })
|
|
|
-
|
|
|
- await self.agoraClient.join(self.appId, self.channel, self.selfToken, self.uid)
|
|
|
- await self.agoraClient.publish([mic, camera].filter(Boolean))
|
|
|
-
|
|
|
- // assume connected by this point, notify backend & show self video
|
|
|
- if (mic || camera) {
|
|
|
- self.joinMeetingAsPro(self.selfUserType);
|
|
|
- $('#self-view').attr('data-type', 'PRO').show();
|
|
|
- self.activateParty('self');
|
|
|
- self.videoActive = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- _initAgora();
|
|
|
-
|
|
|
- },
|
|
|
activateParty: function (_stream = 'self') {
|
|
|
var current = $('.full-view');
|
|
|
if (current.attr('data-stream') === _stream) return;
|
|
@@ -320,16 +221,17 @@
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- enterClientRoomAsPro: function(_done) {
|
|
|
+ enterClientRoomAsPro: function() {
|
|
|
@if($client)
|
|
|
+
|
|
|
$.post('/api/meeting/enterClientRoomAsPro', {clientUid: '{{ $client->uid }}'}, (_data) => {
|
|
|
// TODO: check success
|
|
|
console.log(_data);
|
|
|
- _done.call(this);
|
|
|
+ this.getMeetingInfo(true);
|
|
|
});
|
|
|
@endif
|
|
|
},
|
|
|
- getMeetingInfo: function(_done) {
|
|
|
+ getMeetingInfo: function(_firstRun = false) {
|
|
|
$.post('/api/meeting/getMyMeeting', (_data) => {
|
|
|
if(_data && _data.success) {
|
|
|
let state = JSON.parse(_data.data);
|
|
@@ -348,14 +250,16 @@
|
|
|
this.myMediaServiceIdentifier = state.myMediaServiceIdentifier;
|
|
|
this.otherParticipants = state.otherParticipants;
|
|
|
|
|
|
- // agora stuff
|
|
|
- this.channel = this.inMeetingForClient.clientMediaServiceRoomIdentifier;
|
|
|
+ if(_firstRun) {
|
|
|
+ this.channel = this.inMeetingForClient.clientMediaServiceRoomIdentifier;
|
|
|
+ this.registerSocket();
|
|
|
+ }
|
|
|
|
|
|
- _done.call(this);
|
|
|
+ console.log(this.$data);
|
|
|
}
|
|
|
});
|
|
|
},
|
|
|
- registerSocket: function() {
|
|
|
+ registerSocket: function(_done) {
|
|
|
let socket = new SockJS(this.backendWsURL);
|
|
|
this.socketClient = Stomp.over(socket);
|
|
|
this.socketClient.connect({}, (frame) => {
|
|
@@ -366,123 +270,288 @@
|
|
|
sessionKey: '{{$performer->session_key}}'
|
|
|
})
|
|
|
);
|
|
|
+ this.initMediaService();
|
|
|
});
|
|
|
},
|
|
|
initSocketListeners: function() {
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/newParticipant", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/newParticipant", (message) => {
|
|
|
console.log("newParticipant received:", message.body);
|
|
|
+
|
|
|
+ // refresh meeting data
|
|
|
+ // TODO: do participant replace
|
|
|
+ this.getMeetingInfo();
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myMicrophoneIsAcquired", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myMicrophoneIsAcquired", (message) => {
|
|
|
console.log("myMicrophoneIsAcquired received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myMicrophoneIsNotAcquired", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myMicrophoneIsNotAcquired", (message) => {
|
|
|
console.log("myMicrophoneIsNotAcquired received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myMicrophoneIsOn", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myMicrophoneIsOn", (message) => {
|
|
|
console.log("myMicrophoneIsOn received:", message.body);
|
|
|
+ // refresh meeting data
|
|
|
+ // TODO: do participant replace
|
|
|
+ this.getMeetingInfo();
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myMicrophoneIsOff", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myMicrophoneIsOff", (message) => {
|
|
|
console.log("myMicrophoneIsOff received:", message.body);
|
|
|
+ // refresh meeting data
|
|
|
+ // TODO: do participant replace
|
|
|
+ this.getMeetingInfo();
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myCameraIsAcquired", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myCameraIsAcquired", (message) => {
|
|
|
console.log("myCameraIsAcquired received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myCameraIsNotAcquired", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myCameraIsNotAcquired", (message) => {
|
|
|
console.log("myCameraIsNotAcquired received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myCameraIsOn", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myCameraIsOn", (message) => {
|
|
|
console.log("myCameraIsOn received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/myCameraIsOff", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/myCameraIsOff", (message) => {
|
|
|
console.log("myCameraIsOff received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/editMyName", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/editMyName", (message) => {
|
|
|
console.log("editMyName received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/setMyAwayMessage", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/setMyAwayMessage", (message) => {
|
|
|
console.log("setMyAwayMessage received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/removeMyAwayMessage", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/removeMyAwayMessage", (message) => {
|
|
|
console.log("removeMyAwayMessage received:", message.body);
|
|
|
});
|
|
|
|
|
|
- this.socketClient.subscribe("/user/topic/leaveClientRoom", function(message) {
|
|
|
+ this.socketClient.subscribe("/user/topic/leaveClientRoom", (message) => {
|
|
|
console.log("leaveClientRoom received:", message.body);
|
|
|
});
|
|
|
|
|
|
},
|
|
|
+ initMediaService: function() {
|
|
|
+
|
|
|
+ this.mediaServiceClient = AgoraRTC.createClient({mode:'rtc', codec:'h264'})
|
|
|
+
|
|
|
+ async function _acquireMicrophone() {
|
|
|
+ this.myMedia.isMicrophoneAcquired = false;
|
|
|
+ this.myMedia.isMicrophoneOn = false;
|
|
|
+ try {
|
|
|
+ this.myMicrophone = await AgoraRTC.createMicrophoneAudioTrack();
|
|
|
+ this.myMedia.isMicrophoneAcquired = true;
|
|
|
+ }
|
|
|
+ catch (e) {
|
|
|
+ console.log('ALIX: error in getting mic');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ async function _acquireCamera() {
|
|
|
+ this.myMedia.isCameraAcquired = false;
|
|
|
+ this.myMedia.isCameraOn = false;
|
|
|
+ try {
|
|
|
+ @if(config('app.agora_mode') === 'screen') // testing
|
|
|
+ this.myCamera = await AgoraRTC.createScreenVideoTrack();
|
|
|
+ @else
|
|
|
+ this.myCamera = await AgoraRTC.createCameraVideoTrack();
|
|
|
+ @endif
|
|
|
+ this.myMedia.isCameraAcquired = true;
|
|
|
+ }
|
|
|
+ catch (e) {
|
|
|
+ console.log('ALIX: error in getting mic');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ async function _initMediaServiceEvents() {
|
|
|
+ this.mediaServiceClient.on('user-joined', user => {
|
|
|
+ // do nothing, newParticipant logic handled via WS
|
|
|
+ });
|
|
|
+ this.mediaServiceClient.on('user-left', user => {
|
|
|
+ // do nothing, leaveClientRoom logic handled via WS
|
|
|
+ });
|
|
|
+ this.mediaServiceClient.on('user-published', async (user, mediaType) => {
|
|
|
+ await this.mediaServiceClient.subscribe(user, mediaType)
|
|
|
+ this.attemptToPlayParticipantMedia(user, mediaType);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ async function _initMediaService() {
|
|
|
+
|
|
|
+ await _acquireMicrophone.call(this); // get mic
|
|
|
+ await _acquireCamera.call(this); // get cam (or screen for testing)
|
|
|
+
|
|
|
+ if (!this.myMicrophone && !this.myCamera) {
|
|
|
+ alert('Do you have camera/mic? Unable to hear or see you.');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ await _initMediaServiceEvents.call(this);
|
|
|
+
|
|
|
+ // Show own feed
|
|
|
+ if(this.myCamera && this.myMedia.isCameraAcquired) {
|
|
|
+ this.myCamera.play($('#self-view')[0]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // init unrenderedParticipantsTimer and unresolvedParticipantsTimer
|
|
|
+ this.initUnrenderedParticipantsTimer();
|
|
|
+ this.initUnresolvedParticipantsTimer();
|
|
|
+
|
|
|
+ await this.mediaServiceClient.join( // join agora channel
|
|
|
+ this.appId,
|
|
|
+ this.channel,
|
|
|
+ this.myMediaServiceToken,
|
|
|
+ +this.myMediaServiceIdentifier
|
|
|
+ );
|
|
|
+ await this.mediaServiceClient.publish( // publish audio/video
|
|
|
+ [this.myMicrophone, this.myCamera].filter(Boolean)
|
|
|
+ );
|
|
|
+
|
|
|
+ // notify others that I have arrived
|
|
|
+ if(this.myCamera && this.myMedia.isCameraAcquired) {
|
|
|
+ this.myCameraIsOn();
|
|
|
+ }
|
|
|
+ if(this.myMicrophone && this.myMedia.isMicrophoneAcquired) {
|
|
|
+ this.myMicrophoneIsOn();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ _initMediaService.call(this);
|
|
|
+ },
|
|
|
+
|
|
|
+ // attemptToPlayParticipantMedia
|
|
|
+ // if user already in otherParticipants
|
|
|
+ // if user's thumb already rendered
|
|
|
+ // if yes, check participant's isCameraOn is true
|
|
|
+ // if yes, play participant's video in his thumb
|
|
|
+ // else store "user" in unrenderedParticipants
|
|
|
+ // and keep retrying after 500mx (max 2 times) - i.e. give vue a cycle or 2 to render thumb
|
|
|
+ // else store "user" in unresolvedParticipants
|
|
|
+ // and keep retrying after 500ms (max 10 times) till resolved - i.e. give WS time to receive the newParticipant event
|
|
|
+ attemptToPlayParticipantMedia: function(user, mediaType) {
|
|
|
+ let participant = this.otherParticipants.filter(function(_participant) {
|
|
|
+ return (+_participant.mediaServiceIdentifier) === user.uid;
|
|
|
+ });
|
|
|
+ if(participant && participant.length) {
|
|
|
+ participant = participant[0];
|
|
|
+ if($('[data-uid="' + participant.mediaServiceIdentifier + '"]').length) {
|
|
|
+ if(mediaType === 'audio' && participant.media.isMicrophoneOn) {
|
|
|
+ user.audioTrack.play();
|
|
|
+ }
|
|
|
+ else if(mediaType === 'video' && participant.media.isCameraOn) {
|
|
|
+ user.videoTrack.play($('[data-uid="' + user.uid + '"]')[0]);
|
|
|
+ }
|
|
|
+ this.markUserAsRendered(user);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.warn('Thumb not yet in DOM for participant!', user.uid);
|
|
|
+ this.markUserAsUnrendered(user, mediaType);
|
|
|
+ }
|
|
|
+ this.markUserAsResolved(user);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.warn('Participant not found in otherParticipants!', user.uid);
|
|
|
+ this.markUserAsUnresolved(user, mediaType);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // start: agora <-> WS sync helpers
|
|
|
+ initUnrenderedParticipantsTimer: function() {
|
|
|
+ this.unrenderedParticipantsTimer = window.setInterval(() => {
|
|
|
+ this.unrenderedParticipants.forEach((_user) => {
|
|
|
+ this.attemptToPlayParticipantMedia(_user, _user.mediaType);
|
|
|
+ });
|
|
|
+ }, 500);
|
|
|
+ },
|
|
|
+ initUnresolvedParticipantsTimer: function() {
|
|
|
+ this.unresolvedParticipantsTimer = window.setInterval(() => {
|
|
|
+ this.unresolvedParticipants.forEach((_user) => {
|
|
|
+ this.attemptToPlayParticipantMedia(_user, _user.mediaType);
|
|
|
+ });
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+ markUserAsUnrendered: function(_user, _mediaType) {
|
|
|
+ let existing = !!this.unrenderedParticipants.filter((_item) => _item.uid === _user.uid).length;
|
|
|
+ if(!existing) {
|
|
|
+ _user.mediaType = _mediaType;
|
|
|
+ this.unrenderedParticipants.push(_user);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ markUserAsRendered: function(_user) {
|
|
|
+ this.unrenderedParticipants = this.unrenderedParticipants.filter((_item) => _item.uid !== _user.uid);
|
|
|
+ },
|
|
|
+ markUserAsUnresolved: function(_user, _mediaType) {
|
|
|
+ let existing = !!this.unresolvedParticipants.filter((_item) => _item.uid === _user.uid).length;
|
|
|
+ if(!existing) {
|
|
|
+ _user.mediaType = _mediaType;
|
|
|
+ this.unresolvedParticipants.push(_user);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ markUserAsResolved: function(_user) {
|
|
|
+ this.unresolvedParticipants = this.unresolvedParticipants.filter((_item) => _item.uid !== _user.uid);
|
|
|
+ },
|
|
|
+ // end: agora <-> WS sync helpers
|
|
|
|
|
|
// start: actions that notify participants via socket
|
|
|
myMicrophoneIsAcquired: function () {
|
|
|
- self.socketClient.send("/app/myMicrophoneIsAcquired", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ this.socketClient.send("/app/myMicrophoneIsAcquired", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
myMicrophoneIsNotAcquired: function () {
|
|
|
- self.socketClient.send("/app/myMicrophoneIsNotAcquired", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ this.socketClient.send("/app/myMicrophoneIsNotAcquired", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
myMicrophoneIsOn: function () {
|
|
|
- self.socketClient.send("/app/myMicrophoneIsOn", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ if(!this.myMedia.isMicrophoneAcquired) return;
|
|
|
+ this.myMicrophone.setEnabled(true);
|
|
|
+ this.myMedia.isMicrophoneOn = true;
|
|
|
+ this.socketClient.send("/app/myMicrophoneIsOn", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
myMicrophoneIsOff: function () {
|
|
|
- self.socketClient.send("/app/myMicrophoneIsOff", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ if(!this.myMedia.isMicrophoneAcquired) return;
|
|
|
+ this.myMicrophone.setEnabled(false);
|
|
|
+ this.myMedia.isMicrophoneOn = false;
|
|
|
+ this.socketClient.send("/app/myMicrophoneIsOff", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
myCameraIsAcquired: function () {
|
|
|
- self.socketClient.send("/app/myCameraIsAcquired", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ this.socketClient.send("/app/myCameraIsAcquired", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
myCameraIsNotAcquired: function () {
|
|
|
- self.socketClient.send("/app/myCameraIsNotAcquired", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ this.socketClient.send("/app/myCameraIsNotAcquired", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
myCameraIsOn: function () {
|
|
|
- self.socketClient.send("/app/myCameraIsOn", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ if(!this.myMedia.isCameraAcquired) return;
|
|
|
+ this.myCamera.setEnabled(true);
|
|
|
+ this.myMedia.isCameraOn = true;
|
|
|
+ this.socketClient.send("/app/myCameraIsOn", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
myCameraIsOff: function () {
|
|
|
- self.socketClient.send("/app/myCameraIsOff", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ if(!this.myMedia.isCameraAcquired) return;
|
|
|
+ this.myCamera.setEnabled(false);
|
|
|
+ this.myMedia.isCameraOn = false;
|
|
|
+ this.socketClient.send("/app/myCameraIsOff", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
editMyName: function (_myNewName) {
|
|
|
- self.socketClient.send("/app/editMyName", {},
|
|
|
+ this.socketClient.send("/app/editMyName", {},
|
|
|
JSON.stringify({
|
|
|
sessionKey: '{{$performer->session_key}}',
|
|
|
myNewName: _myNewName
|
|
@@ -490,7 +559,7 @@
|
|
|
);
|
|
|
},
|
|
|
setMyAwayMessage: function (_message) {
|
|
|
- self.socketClient.send("/app/setMyAwayMessage", {},
|
|
|
+ this.socketClient.send("/app/setMyAwayMessage", {},
|
|
|
JSON.stringify({
|
|
|
sessionKey: '{{$performer->session_key}}',
|
|
|
message: _message
|
|
@@ -498,49 +567,21 @@
|
|
|
);
|
|
|
},
|
|
|
removeMyAwayMessage: function () {
|
|
|
- self.socketClient.send("/app/removeMyAwayMessage", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ this.socketClient.send("/app/removeMyAwayMessage", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
},
|
|
|
leaveClientRoom: function () {
|
|
|
- self.socketClient.send("/app/leaveClientRoom", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
+ this.socketClient.send("/app/leaveClientRoom", {},
|
|
|
+ JSON.stringify({sessionKey: '{{$performer->session_key}}'})
|
|
|
);
|
|
|
+ window.location.href = '/pro/meet';
|
|
|
},
|
|
|
// end: actions that notify participants via socket
|
|
|
|
|
|
},
|
|
|
mounted: function () {
|
|
|
-
|
|
|
- // enter the room
|
|
|
- this.enterClientRoomAsPro(function() {
|
|
|
-
|
|
|
- // get meeting info
|
|
|
- this.getMeetingInfo(function() {
|
|
|
-
|
|
|
- // register socket
|
|
|
- this.registerSocket();
|
|
|
-
|
|
|
- });
|
|
|
-
|
|
|
- });
|
|
|
-
|
|
|
- {{--var self = this;--}}
|
|
|
-
|
|
|
- {{--$(document).on('click', '.thumbs>div[data-stream]', function () {--}}
|
|
|
- {{-- self.activateParty($(this).attr('data-stream'));--}}
|
|
|
- {{-- return false;--}}
|
|
|
- {{--});--}}
|
|
|
-
|
|
|
- {{--@if(isset($client))--}}
|
|
|
- {{-- self.client = true;--}}
|
|
|
- {{-- self.clientUid = '{{ $client->uid }}';--}}
|
|
|
- {{-- self.videoActive = false;--}}
|
|
|
- {{--@endif--}}
|
|
|
+ this.enterClientRoomAsPro();
|
|
|
}
|
|
|
});
|
|
|
new Vue({
|
|
@@ -583,29 +624,5 @@
|
|
|
})
|
|
|
})();
|
|
|
</script>
|
|
|
- <!--
|
|
|
- <script>
|
|
|
- // connect to WS
|
|
|
- self.socket = new SockJS('{{ config('app.backend_ws_url') }}');
|
|
|
- self.socketClient = Stomp.over(self.socket);
|
|
|
- self.socketClient.connect({}, function(frame) {
|
|
|
- console.log('Connected: ' + frame);
|
|
|
-
|
|
|
- self.socketClient.subscribe("/user/topic/registration", function(message) {
|
|
|
- console.log("Receiving message")
|
|
|
- console.log("registration result:", message.body);
|
|
|
- });
|
|
|
-
|
|
|
- // join self
|
|
|
- console.log("Sending message")
|
|
|
- self.socketClient.send("/app/register", {},
|
|
|
- JSON.stringify({
|
|
|
- sessionKey: '{{$performer->session_key}}'
|
|
|
- })
|
|
|
- );
|
|
|
-
|
|
|
- });
|
|
|
- </script>
|
|
|
- -->
|
|
|
</body>
|
|
|
</html>
|