start and stop recording
This commit is contained in:
		
							parent
							
								
									e388c18e03
								
							
						
					
					
						commit
						3129d90318
					
				| @ -31,6 +31,7 @@ class VideoCall { | ||||
|     this.onCreateLocalVideoStream = options.onCreateLocalVideoStream; | ||||
|     this.remoteParticipantStateChanged = options.remoteParticipantStateChanged; | ||||
|     this.remoteVideoIsAvailableChanged = options.remoteVideoIsAvailableChanged; | ||||
|     this.onGetServerCallID = options.onGetServerCallID; | ||||
| 
 | ||||
|     const tokenCredential = new AzureCommunicationTokenCredential(userAccessToken); | ||||
| 
 | ||||
| @ -58,15 +59,10 @@ class VideoCall { | ||||
|         const localVideoStream = await this.createLocalVideoStream(); | ||||
|         const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined; | ||||
| 
 | ||||
|         const roomCallLocator = { roomId: roomId }; | ||||
|         let call = this.callAgent.join(roomCallLocator, { videoOptions }); | ||||
|         this.subscribeToCall(call); | ||||
| 
 | ||||
|         this.callAgent.on('callsUpdated', e => { | ||||
|           e.added.forEach((addedCall) => { | ||||
|             addedCall.on('stateChanged', () => { | ||||
|               if (addedCall.state === 'Connected') { | ||||
|                 debugger; | ||||
|                 addedCall.info.getServerCallId().then(result => { | ||||
|                   this.onGetServerCallID?.(result); | ||||
|                 }).catch(err => { | ||||
| @ -76,6 +72,10 @@ class VideoCall { | ||||
|             }); | ||||
|           }); | ||||
|         }); | ||||
| 
 | ||||
|         const roomCallLocator = { roomId: roomId }; | ||||
|         let call = this.callAgent.join(roomCallLocator, { videoOptions }); | ||||
|         this.subscribeToCall(call); | ||||
|       } catch (error) { | ||||
|         console.error(error); | ||||
|       } | ||||
|  | ||||
| @ -14,13 +14,15 @@ namespace EnotaryoPH.Web.Common.Services | ||||
|         private readonly CommunicationIdentityClient _communicationIdentityClient; | ||||
|         private readonly RoomsClient _roomsClient; | ||||
|         private readonly CallAutomationClient _callAutomationClient; | ||||
|         private readonly IConfiguration _configuration; | ||||
| 
 | ||||
|         public ConferenceSheduleService(NotaryoDBContext dbContext, CommunicationIdentityClient communicationIdentityClient, RoomsClient roomsClient, CallAutomationClient callAutomationClient) | ||||
|         public ConferenceSheduleService(NotaryoDBContext dbContext, CommunicationIdentityClient communicationIdentityClient, RoomsClient roomsClient, CallAutomationClient callAutomationClient, IConfiguration configuration) | ||||
|         { | ||||
|             _dbContext = dbContext; | ||||
|             _communicationIdentityClient = communicationIdentityClient; | ||||
|             _roomsClient = roomsClient; | ||||
|             _callAutomationClient = callAutomationClient; | ||||
|             _configuration = configuration; | ||||
|         } | ||||
| 
 | ||||
|         public async Task<Guid> GetOrCreateScheduleIDAsync(Guid transaction_UID) | ||||
| @ -105,19 +107,20 @@ namespace EnotaryoPH.Web.Common.Services | ||||
|                         _dbContext.Update(schedule); | ||||
|                     } | ||||
|                     _dbContext.SaveChanges(); | ||||
|                     schedule_UID = schedule.LawyerVideoConferenceSchedule_UID; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return schedule_UID; | ||||
|         } | ||||
| 
 | ||||
|         public async Task StartRecordingAsync(Guid transaction_UID) | ||||
|         public async Task StartRecordingAsync(Guid transaction_UID, string serverCallID) | ||||
|         { | ||||
|             var transactionEntity = _dbContext.Transactions | ||||
|                 .FirstOrDefault(t => t.Transaction_UID == transaction_UID) ?? throw new ArgumentException("Transaction not found."); | ||||
| 
 | ||||
|             var existingSchedule = _dbContext.LawyerVideoConferenceSchedules.FirstOrDefault(sched => sched.TransactionID == transactionEntity.TransactionID) ?? throw new ArgumentException("Schedule not found."); | ||||
|             if (string.IsNullOrEmpty(existingSchedule.ServerCallID)) | ||||
|             if (string.IsNullOrEmpty(existingSchedule.ServerCallID) && string.IsNullOrEmpty(serverCallID)) | ||||
|             { | ||||
|                 throw new ArgumentException("ServerCallID is not set for this transaction."); | ||||
|             } | ||||
| @ -127,8 +130,18 @@ namespace EnotaryoPH.Web.Common.Services | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (!string.IsNullOrEmpty(serverCallID)) | ||||
|             { | ||||
|                 existingSchedule.ServerCallID = serverCallID; | ||||
|             } | ||||
| 
 | ||||
|             CallLocator callLocator = new ServerCallLocator(existingSchedule.ServerCallID); | ||||
|             var recordingResult = await _callAutomationClient.GetCallRecording().StartAsync(new StartRecordingOptions(callLocator) { RecordingStorage = RecordingStorage.CreateAzureBlobContainerRecordingStorage(new Uri("")) }); | ||||
|             var uri = _configuration.GetValue<string>("UriRecordingBloblContainer") ?? string.Empty; | ||||
|             var recordingResult = await _callAutomationClient | ||||
|                 .GetCallRecording().StartAsync(new StartRecordingOptions(callLocator) | ||||
|                 { | ||||
|                     RecordingStorage = RecordingStorage.CreateAzureBlobContainerRecordingStorage(new Uri(uri)) | ||||
|                 }); | ||||
|             existingSchedule.RecordingID = recordingResult.Value.RecordingId; | ||||
|             _dbContext.Update(existingSchedule); | ||||
|             _dbContext.SaveChanges(); | ||||
| @ -150,7 +163,6 @@ namespace EnotaryoPH.Web.Common.Services | ||||
|                 throw new ArgumentException("Recording ID is not set for this transaction."); | ||||
|             } | ||||
| 
 | ||||
|             CallLocator callLocator = new ServerCallLocator(existingSchedule.ServerCallID); | ||||
|             await _callAutomationClient.GetCallRecording().StopAsync(existingSchedule.RecordingID); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
|     { | ||||
|         Task<Guid> GetOrCreateScheduleIDAsync(Guid transaction_UID); | ||||
| 
 | ||||
|         Task StartRecordingAsync(Guid transaction_UID); | ||||
|         Task StartRecordingAsync(Guid transaction_UID, string serverCallID); | ||||
| 
 | ||||
|         Task StopRecordingAsync(Guid transaction_UID); | ||||
|     } | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| @using System.Text.Json | ||||
| @model EnotaryoPH.Web.Pages.Participant.VideoCall.RoomModel | ||||
| @{ | ||||
|     Layout = "_Blank"; | ||||
| } | ||||
| 
 | ||||
| @section Head { | ||||
| @ -64,7 +65,19 @@ | ||||
| } | ||||
| 
 | ||||
| <div class="container-fluid py-3" id="videoGrid-container"> | ||||
|     <div class="row g-2" id="videoGrid"> | ||||
|     <div class="d-flex mb-1"> | ||||
|         <div> | ||||
|             <span>32:04</span> | ||||
|         </div> | ||||
|         <div class="flex-fill"></div> | ||||
|         <div> | ||||
|             <a href="#" id="ViewDocument" class="btn btn-sm btn-secondary">View Document</a> | ||||
|             <a href="#" id="Approve" class="btn btn-sm btn-success">Approve</a> | ||||
|             <a href="#" id="Reject" class="btn btn-sm btn-danger">Reject</a> | ||||
|         </div> | ||||
| 
 | ||||
|     </div> | ||||
|     <div class="row g-2" id="VideoGrid"> | ||||
|     </div> | ||||
| </div> | ||||
| 
 | ||||
| @ -74,100 +87,17 @@ | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <input type="hidden" value="@JsonSerializer.Serialize(Model.Participants)" /> | ||||
| <input type="hidden" id="Participants" value="@(System.Web.HttpUtility.JavaScriptStringEncode(JsonSerializer.Serialize(Model.Participants)).Replace("\\", ""))" /> | ||||
| <input type="hidden" id="CommunicationUserToken" value="@Model.CommunicationUserToken" /> | ||||
| <input type="hidden" id="CommunicationUserId" value="@Model.CommunicationUserId" /> | ||||
| <input type="hidden" id="CommunicationRoomId" value="@Model.CommunicationRoomId" /> | ||||
| 
 | ||||
| <form method="post" asp-page-handler="StartRecording"> | ||||
|     <input type="hidden" asp-for="ServerCallID" /> | ||||
|     <input type="hidden" asp-for="Transaction_UID" /> | ||||
| </form> | ||||
| 
 | ||||
| @section Scripts { | ||||
|     <script type="text/javascript" src="/dist/_jfa.js"></script> | ||||
|     <script> | ||||
| 
 | ||||
|         let participants = JSON.parse('@Html.Raw(JsonSerializer.Serialize(Model.Participants))'); | ||||
|         function updateGrid() { | ||||
|             const videoGrid = document.getElementById('videoGrid'); | ||||
|             videoGrid.innerHTML = ''; | ||||
| 
 | ||||
|             participants.forEach((participant, index) => { | ||||
|                 const col = document.createElement('div'); | ||||
|                 col.className = 'participant-col'; | ||||
|                 var tmpl = document.getElementById("templateVideo").cloneNode(true).content; | ||||
|                 let vidcontainer = tmpl.querySelector(".video-container") | ||||
|                 if (vidcontainer) { | ||||
|                     vidcontainer.id = participant.Id; | ||||
|                     vidcontainer.classList.add(participant.Id); | ||||
|                     vidcontainer.classList.add(participant.Type == 'Notary' ? 'local-video-container' : 'remote-video-container'); | ||||
|                 } | ||||
|                 let participantName = tmpl.querySelector(".participant-name") | ||||
|                 if (participantName) { | ||||
|                     participantName.textContent = participant.DisplayName; | ||||
|                 } | ||||
|                 col.appendChild(tmpl); | ||||
|                 videoGrid.appendChild(col); | ||||
|             }); | ||||
| 
 | ||||
|             // Dynamically adjust grid columns based on participant count | ||||
|             const count = participants.length; | ||||
|             const cols = count <= 2 ? 'col-12 col-sm-12 offset-md-1 col-md-10 offset-lg-0 col-lg-6' : | ||||
|                         count <= 4 ? 'col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6' : | ||||
|                         count <= 8 ? 'col-6 col-md-6 col-lg-4 col-xl-4' : | ||||
|                         count <= 9 ? 'col-4' : | ||||
|                         'col-6 col-sm-4 col-lg-3'; | ||||
| 
 | ||||
|             document.querySelectorAll('.participant-col').forEach(el => { | ||||
|                 el.className = `participant-col ${cols}`; | ||||
|             }); | ||||
| 
 | ||||
|             const fluid = count <= 2 ? 'container-fluid' : | ||||
|                 count <= 8 ? 'container-xxl' : | ||||
|                 'container-xl' | ||||
|             document.getElementById('videoGrid-container').className = `${fluid} py-3`; | ||||
|         } | ||||
| 
 | ||||
|         async function initVideoCall() { | ||||
|             let userAccessToken = '@Model.CommunicationUserToken'; | ||||
|             const videoCall = jfa.communication.videocall; | ||||
|             let options = { | ||||
|                 onCreateLocalVideoStream: function(el) { | ||||
|                     if (el) { | ||||
|                         el.style['transform'] = ''; | ||||
|                         let notary = participants.find(p => p.RoomUserID == '@Model.CommunicationUserId'); | ||||
|                         document.getElementById(notary.Id).appendChild(el); | ||||
|                     } | ||||
|                 }, | ||||
|                 remoteVideoIsAvailableChanged: function(e) { | ||||
|                     let participant = participants.find(p => p.RoomUserID == e.participantId); | ||||
|                     if (participant) { | ||||
|                         e.el.querySelector('video').style['object-fit'] = 'cover'; | ||||
|                         document.getElementById(participant.Id).appendChild(e.el); | ||||
|                     } | ||||
|                 }, | ||||
|                 onGetServerCallID: function(e) { | ||||
|                     debugger; | ||||
|                     let url = jfa.utilities.routing.getCurrentURLWithHandler("StartRecording"); | ||||
|                     debugger; | ||||
|                     url.searchParams.append("ServerCallID", e.serverCallId || ""); | ||||
|                     jfa.utilities.request.post(url, {}) | ||||
|                       .then(resp => { | ||||
|                         if (resp.ok === true) { | ||||
|                           jfa.page.reload(); | ||||
|                         } | ||||
|                       }) | ||||
|                       .catch(err => console.error(err)); | ||||
|                 } | ||||
|             }; | ||||
|             await videoCall.init(userAccessToken, options); | ||||
|             videoCall.joinRoom('@Model.CommunicationRoomId'); | ||||
|         } | ||||
| 
 | ||||
|         if (document.readyState !== 'loading') { | ||||
|             init(); | ||||
|         } else { | ||||
|             document.addEventListener('DOMContentLoaded', init); | ||||
|         } | ||||
| 
 | ||||
|         async function init() { | ||||
|             updateGrid(); | ||||
|             window.addEventListener('resize', updateGrid); | ||||
| 
 | ||||
|             await initVideoCall(); | ||||
|         } | ||||
|     </script> | ||||
|     <script src="~/Pages/Participant/VideoCall/Room.cshtml.js" asp-append-version="true"></script> | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,7 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall | ||||
| { | ||||
|     public class RoomModel : PageModel | ||||
|     { | ||||
|         private const int VideoConferenceExpirationInHours = 2; | ||||
|         private readonly IConferenceSheduleService _conferenceSheduleService; | ||||
|         private readonly ICurrentUserService _currentUserService; | ||||
|         private readonly NotaryoDBContext _dbContext; | ||||
| @ -22,10 +23,7 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall | ||||
| 
 | ||||
|         public async Task<IActionResult> OnGetAsync() | ||||
|         { | ||||
|             _Transaction = _dbContext.Transactions | ||||
|                 .Include(t => t.TransactionSignatories) | ||||
|                 .Include(t => t.Principal) | ||||
|                 .FirstOrDefault(t => t.Transaction_UID == Transaction_UID); | ||||
|             LoadTransaction(); | ||||
|             if (_Transaction == null) | ||||
|             { | ||||
|                 return NotFound(); | ||||
| @ -48,12 +46,20 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall | ||||
|                 return Redirect("/"); | ||||
|             } | ||||
| 
 | ||||
|             _LawyerVideoConferenceSchedule = _dbContext.LawyerVideoConferenceSchedules | ||||
|                 .Include(sched => sched.LawyerVideoConferenceParticipants) | ||||
|                 .ThenInclude(p => p.Participant) | ||||
|                 .Include(sched => sched.Lawyer) | ||||
|                 .ThenInclude(l => l.User) | ||||
|                 .FirstOrDefault(sched => sched.LawyerVideoConferenceSchedule_UID == schedule_UID); | ||||
|             LoadVideoConferenceSchedule(schedule_UID); | ||||
| 
 | ||||
|             if ((DateTime.UtcNow - _LawyerVideoConferenceSchedule.MeetingDate).TotalHours > VideoConferenceExpirationInHours) | ||||
|             { | ||||
|                 if (!_LawyerVideoConferenceSchedule.Status.IsInList(VideoConferenceStatus.Abandoned, VideoConferenceStatus.Completed)) | ||||
|                 { | ||||
|                     _LawyerVideoConferenceSchedule.Status = nameof(VideoConferenceStatus.Expired); | ||||
|                     _dbContext.Update(_LawyerVideoConferenceSchedule); | ||||
|                     _dbContext.SaveChanges(); | ||||
|                 } | ||||
| 
 | ||||
|                 TempData["Warning"] = "The video conference has expired."; | ||||
|                 return Redirect("/"); | ||||
|             } | ||||
| 
 | ||||
|             CommunicationUserToken = currentUser.Role == nameof(UserType.Notary) | ||||
|                 ? _LawyerVideoConferenceSchedule.MeetingRoomTokenID | ||||
| @ -66,7 +72,33 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall | ||||
|             return Page(); | ||||
|         } | ||||
| 
 | ||||
|         public async Task OnPostStartRecordingAsync() => await _conferenceSheduleService.StartRecordingAsync(Transaction_UID); | ||||
|         public async Task OnPostStartRecordingAsync() | ||||
|         { | ||||
|             LoadTransaction(); | ||||
|             var schedule_UID = await _conferenceSheduleService.GetOrCreateScheduleIDAsync(Transaction_UID); | ||||
|             LoadVideoConferenceSchedule(schedule_UID); | ||||
|             await _conferenceSheduleService.StartRecordingAsync(Transaction_UID, ServerCallID); | ||||
|         } | ||||
| 
 | ||||
|         public async Task OnPostStopRecordingAsync() | ||||
|         { | ||||
|             LoadTransaction(); | ||||
|             var schedule_UID = await _conferenceSheduleService.GetOrCreateScheduleIDAsync(Transaction_UID); | ||||
|             LoadVideoConferenceSchedule(schedule_UID); | ||||
|             await _conferenceSheduleService.StopRecordingAsync(Transaction_UID); | ||||
|         } | ||||
| 
 | ||||
|         private void LoadTransaction() => _Transaction = _dbContext.Transactions | ||||
|                                         .Include(t => t.TransactionSignatories) | ||||
|                         .Include(t => t.Principal) | ||||
|                         .FirstOrDefault(t => t.Transaction_UID == Transaction_UID); | ||||
| 
 | ||||
|         private void LoadVideoConferenceSchedule(Guid schedule_UID) => _LawyerVideoConferenceSchedule = _dbContext.LawyerVideoConferenceSchedules | ||||
|                         .Include(sched => sched.LawyerVideoConferenceParticipants) | ||||
|                         .ThenInclude(p => p.Participant) | ||||
|                         .Include(sched => sched.Lawyer) | ||||
|                         .ThenInclude(l => l.User) | ||||
|                         .FirstOrDefault(sched => sched.LawyerVideoConferenceSchedule_UID == schedule_UID); | ||||
| 
 | ||||
|         public string CommunicationRoomId { get; private set; } | ||||
| 
 | ||||
| @ -101,7 +133,7 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall | ||||
|         } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public string ServerCallingID { get; set; } | ||||
|         public string ServerCallID { get; set; } | ||||
| 
 | ||||
|         [BindProperty(SupportsGet = true)] | ||||
|         public Guid Transaction_UID { get; set; } | ||||
|  | ||||
| @ -0,0 +1,119 @@ | ||||
| "use strict"; | ||||
| (async function () { | ||||
|   let | ||||
|     control_approve = document.getElementById("Approve"), | ||||
|     control_communicationRoomId = document.getElementById("CommunicationRoomId"), | ||||
|     control_communicationUserId = document.getElementById("CommunicationUserId"), | ||||
|     control_communicationUserToken = document.getElementById("CommunicationUserToken"), | ||||
|     control_participants = document.getElementById("Participants"), | ||||
|     control_reject = document.getElementById("Reject"), | ||||
|     control_templateVideo = document.getElementById("templateVideo"), | ||||
|     control_videoGrid = document.getElementById("VideoGrid"), | ||||
|     control_videoGridContainer = document.getElementById("videoGrid-container"), | ||||
|     control_viewDocument = document.getElementById("ViewDocument"), | ||||
|     control_serverCallIID = document.getElementById("ServerCallID"), | ||||
|     x = 1; | ||||
| 
 | ||||
|   let participants = JSON.parse(control_participants.value); | ||||
| 
 | ||||
|   async function _initVideoCall() { | ||||
|     let userAccessToken = control_communicationUserToken.value; | ||||
|     const videoCall = jfa.communication.videocall; | ||||
|     let options = { | ||||
|       onCreateLocalVideoStream: function (el) { | ||||
|         if (el) { | ||||
|           el.style['transform'] = ''; | ||||
|           let notary = participants.find(p => p.RoomUserID == control_communicationUserId.value); | ||||
|           document.getElementById(notary.Id).appendChild(el); | ||||
|         } | ||||
|       }, | ||||
|       remoteVideoIsAvailableChanged: function (e) { | ||||
|         let participant = participants.find(p => p.RoomUserID == e.participantId); | ||||
|         if (participant) { | ||||
|           e.el.querySelector('video').style['object-fit'] = 'cover'; | ||||
|           document.getElementById(participant.Id).appendChild(e.el); | ||||
|         } | ||||
|       }, | ||||
|       onGetServerCallID: function (serverCallId) { | ||||
|         let url = jfa.utilities.routing.getCurrentURLWithHandler("StartRecording"); | ||||
|         url.searchParams.append("ServerCallID", serverCallId); | ||||
|         jfa.utilities.request.post(url, {}) | ||||
|           .then(resp => { | ||||
|             if (resp.ok === true) { | ||||
|               console.log("started recording"); | ||||
|             } | ||||
|           }) | ||||
|           .catch(err => console.error(err)); | ||||
|       } | ||||
|     }; | ||||
|     await videoCall.init(userAccessToken, options); | ||||
|     videoCall.joinRoom(control_communicationRoomId.value); | ||||
|   } | ||||
| 
 | ||||
|   function _updateGrid() { | ||||
|     control_videoGrid.innerHTML = ''; | ||||
| 
 | ||||
|     participants.forEach((participant, index) => { | ||||
|       const col = document.createElement('div'); | ||||
|       col.className = 'participant-col'; | ||||
|       const tmpl = control_templateVideo.cloneNode(true).content; | ||||
|       const vidcontainer = tmpl.querySelector(".video-container") | ||||
|       if (vidcontainer) { | ||||
|         vidcontainer.id = participant.Id; | ||||
|         vidcontainer.classList.add(participant.Id); | ||||
|         vidcontainer.classList.add(participant.Type == 'Notary' ? 'local-video-container' : 'remote-video-container'); | ||||
|       } | ||||
|       let participantName = tmpl.querySelector(".participant-name") | ||||
|       if (participantName) { | ||||
|         participantName.textContent = participant.DisplayName; | ||||
|       } | ||||
|       col.appendChild(tmpl); | ||||
|       control_videoGrid.appendChild(col); | ||||
|     }); | ||||
| 
 | ||||
|     // Dynamically adjust grid columns based on participant count
 | ||||
|     const count = participants.length; | ||||
|     const cols = count <= 2 ? 'col-12 col-sm-12 offset-md-1 col-md-10 offset-lg-0 col-lg-6' : | ||||
|       count <= 4 ? 'col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6' : | ||||
|         count <= 8 ? 'col-6 col-md-6 col-lg-4 col-xl-4' : | ||||
|           count <= 9 ? 'col-4' : | ||||
|             'col-6 col-sm-4 col-lg-3'; | ||||
| 
 | ||||
|     document.querySelectorAll('.participant-col').forEach(el => { | ||||
|       el.className = `participant-col ${cols}`; | ||||
|     }); | ||||
| 
 | ||||
|     const fluid = count <= 2 ? 'container-fluid' : | ||||
|       count <= 8 ? 'container-xxl' : | ||||
|         'container-xl' | ||||
|     control_videoGridContainer.className = `${fluid} py-3`; | ||||
|   } | ||||
| 
 | ||||
|   function _bindEvents() { | ||||
|     window.addEventListener('resize', _updateGrid); | ||||
|     control_viewDocument.addEventListener("click", function () { | ||||
|       alert('not yet implemented'); | ||||
|     }); | ||||
|     control_approve.addEventListener("click", function () { | ||||
|       let url = jfa.utilities.routing.getCurrentURLWithHandler("StopRecording"); | ||||
|       jfa.utilities.request.post(url, {}) | ||||
|         .then(resp => { | ||||
|           if (resp.ok === true) { | ||||
|             console.log("stopped recording"); | ||||
|           } | ||||
|         }) | ||||
|         .catch(err => console.error(err)); | ||||
|     }); | ||||
|     control_reject.addEventListener("click", function () { | ||||
|       alert('not yet implemented'); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async function _init() { | ||||
|     _bindEvents(); | ||||
|     _updateGrid(); | ||||
|     await _initVideoCall(); | ||||
|   } | ||||
| 
 | ||||
|   await _init(); | ||||
| })(); | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user