change(tracker): change remote control func and window
This commit is contained in:
parent
e02b9276b0
commit
67669cafe4
4 changed files with 358 additions and 645 deletions
|
|
@ -1,26 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>OpenReplay | Assist</title>
|
||||
|
||||
<!--CSS -->
|
||||
<!-- <link href="css/styles.css" rel="stylesheet"> -->
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.text-uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.connecting-message {
|
||||
/* margin-top: 50%; */
|
||||
margin-top: 50%;
|
||||
font-size: 20px;
|
||||
color: #aaa;
|
||||
text-align: center;
|
||||
|
|
@ -29,71 +20,18 @@
|
|||
}
|
||||
|
||||
.status-connecting .connecting-message {
|
||||
/* display: block; */
|
||||
display: block;
|
||||
}
|
||||
|
||||
.status-connecting .card {
|
||||
/* display: none; */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
font: 14px 'Roboto', sans-serif;
|
||||
/* min-width: 324px; */
|
||||
width: 300px;
|
||||
/* max-width: 800px; */
|
||||
/* border: solid thin #ccc; */
|
||||
/* box-shadow: 0 0 10px #aaa; */
|
||||
border: solid 4px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.card-footers {
|
||||
display: flex;
|
||||
border-bottom: solid thin #ccc;
|
||||
padding: 5px 5px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-footers .assist-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #cc0000 !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: #ff0000 !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 5px 8px;
|
||||
font-size: 14px;
|
||||
border-radius: 3px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.card .card-header {
|
||||
min-width: 324px;
|
||||
width: 350px;
|
||||
max-width: 800px;
|
||||
cursor: move;
|
||||
padding: 14px 18px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: solid thin #ccc;
|
||||
}
|
||||
|
||||
#agent-name,
|
||||
|
|
@ -101,23 +39,9 @@
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
#video-container {
|
||||
background-color: rgb(90, 90, 90);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
/* width: 300px; */
|
||||
}
|
||||
|
||||
#video-container video {
|
||||
width: 100% !important;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#local-stream,
|
||||
#remote-stream {
|
||||
/* display:none; */
|
||||
/* TODO uncomment this line */
|
||||
display: none;
|
||||
}
|
||||
|
||||
#video-container.remote #remote-stream {
|
||||
|
|
@ -134,33 +58,21 @@
|
|||
|
||||
#local-stream {
|
||||
width: 35%;
|
||||
/* top: 50%; */
|
||||
/* left: 70%; */
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
border: thin solid rgba(255, 255, 255, 0.3);
|
||||
overflow: hidden;
|
||||
border: thin solid rgba(255, 255, 255, .3);
|
||||
}
|
||||
|
||||
#audio-btn {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#audio-btn .bi-mic {
|
||||
fill: #cc0000;
|
||||
}
|
||||
|
||||
#audio-btn .bi-mic-mute {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#audio-btn:after {
|
||||
/* text-transform: capitalize; */
|
||||
color: #cc0000;
|
||||
content: 'Mute';
|
||||
padding-left: 5px;
|
||||
text-transform: capitalize;
|
||||
content: 'Mute'
|
||||
}
|
||||
|
||||
#audio-btn.muted .bi-mic-mute {
|
||||
|
|
@ -172,28 +84,21 @@
|
|||
}
|
||||
|
||||
#audio-btn.muted:after {
|
||||
content: 'Unmute';
|
||||
padding-left: 5px;
|
||||
content: 'Unmute'
|
||||
}
|
||||
|
||||
#video-btn .bi-camera-video {
|
||||
fill: #cc0000;
|
||||
}
|
||||
|
||||
#video-btn .bi-camera-video-off {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#video-btn:after {
|
||||
/* text-transform: capitalize; */
|
||||
color: #cc0000;
|
||||
content: 'Stop Video';
|
||||
padding-left: 5px;
|
||||
text-transform: capitalize;
|
||||
content: 'Stop Video'
|
||||
}
|
||||
|
||||
#video-btn.off:after {
|
||||
content: 'Start Video';
|
||||
padding-left: 5px;
|
||||
content: 'Start Video'
|
||||
}
|
||||
|
||||
#video-btn.off .bi-camera-video-off {
|
||||
|
|
@ -204,233 +109,25 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
/* CHART */
|
||||
#chat-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 14px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#chat-card .chat-messages {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#chat-card .chat-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#chat-card .chat-header .arrow-state {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
#chat-card.active .chat-messages {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#chat-card.active .chat-input {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#chat-card.active .chat-header .arrow-state {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
#chat-card .chat-header {
|
||||
border-bottom: solid thin #ccc;
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#chat-card .chat-header .chat-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#chat-card .chat-header .chat-title span {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
#chat-card .chat-messages {
|
||||
padding: 8px 16px;
|
||||
overflow-y: auto;
|
||||
height: 250px;
|
||||
overflow-y: auto;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#chat-card .message-text {
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
color: #666666;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
#chat-card .message .message-text {
|
||||
/* max-width: 70%; */
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#chat-card .message {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#chat-card .chat-messages .message.left .message-text {
|
||||
text-align: left;
|
||||
background: #d7e2e2;
|
||||
border-radius: 0px 30px 30px 30px;
|
||||
}
|
||||
|
||||
#chat-card .message .message-user {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
#chat-card .message .message-time {
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
#chat-card .chat-messages .message.right {
|
||||
margin-left: auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#chat-card .chat-messages .message.right .message-text {
|
||||
background: #e4e4e4;
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 30px 30px 0px 30px;
|
||||
}
|
||||
|
||||
#chat-card .chat-input {
|
||||
margin: 10px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.15);
|
||||
background-color: #dddddd;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#chat-card .chat-input .input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
padding: 8px 16px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
background-color: #aaa;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.send-btn:hover {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.send-btn svg {
|
||||
fill: #dddddd;
|
||||
}
|
||||
|
||||
.confirm-window .title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.confirm-window {
|
||||
font: 14px 'Roboto', sans-serif;
|
||||
padding: 20px;
|
||||
background-color: #f3f3f3;
|
||||
border-radius: 3px;
|
||||
/* position: absolute; */
|
||||
width: fit-content;
|
||||
color: #666666;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.confirm-window .actions {
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
box-shadow: 0px 0px 3.99778px 1.99889px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
font-size: 14px;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: rgba(0, 167, 47, 1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* .btn-error:hover,
|
||||
.btn-success:hover {
|
||||
filter: brightness(0.9);
|
||||
} */
|
||||
|
||||
.btn-error {
|
||||
background: #ffe9e9;
|
||||
/* border-color: #d43f3a; */
|
||||
color: #cc0000;
|
||||
}
|
||||
|
||||
.remote-control {
|
||||
display: hidden;
|
||||
display: none;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
flex-direction: row;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body data-openreplay-hidden>
|
||||
<div id="remote-control-confirm" class="confirm-window">
|
||||
<div class="title">The agent is requesting remote control</div>
|
||||
<div class="actions">
|
||||
<button class="text-uppercase btn btn-lg btn-success" style="margin-right: 10px">
|
||||
Grant remote access
|
||||
</button>
|
||||
<button class="text-uppercase btn btn-lg btn-error">Reject</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="call-confirm" class="confirm-window">
|
||||
<div class="title">Answer the call so the agent can assist.</div>
|
||||
<div class="actions">
|
||||
<button class="text-uppercase btn btn-lg btn-success" style="margin-right: 10px">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-telephone"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M3.654 1.328a.678.678 0 0 0-1.015-.063L1.605 2.3c-.483.484-.661 1.169-.45 1.77a17.568 17.568 0 0 0 4.168 6.608 17.569 17.569 0 0 0 6.608 4.168c.601.211 1.286.033 1.77-.45l1.034-1.034a.678.678 0 0 0-.063-1.015l-2.307-1.794a.678.678 0 0 0-.58-.122l-2.19.547a1.745 1.745 0 0 1-1.657-.459L5.482 8.062a1.745 1.745 0 0 1-.46-1.657l.548-2.19a.678.678 0 0 0-.122-.58L3.654 1.328zM1.884.511a1.745 1.745 0 0 1 2.612.163L6.29 2.98c.329.423.445.974.315 1.494l-.547 2.19a.678.678 0 0 0 .178.643l2.457 2.457a.678.678 0 0 0 .644.178l2.189-.547a1.745 1.745 0 0 1 1.494.315l2.306 1.794c.829.645.905 1.87.163 2.611l-1.034 1.034c-.74.74-1.846 1.065-2.877.702a18.634 18.634 0 0 1-7.01-4.42 18.634 18.634 0 0 1-4.42-7.009c-.362-1.03-.037-2.137.703-2.877L1.885.511z" />
|
||||
</svg>
|
||||
<span>Answer</span>
|
||||
</button>
|
||||
<button class="text-uppercase btn btn-lg btn-error">Reject</button>
|
||||
</div>
|
||||
</div>
|
||||
<body>
|
||||
<section id="or-assist" class="status-connecting">
|
||||
<div class="connecting-message">Connecting...</div>
|
||||
<div class="card shadow">
|
||||
<div class="drag-area card-header d-flex justify-content-between">
|
||||
<div class="card border-dark shadow drag-area">
|
||||
<div class="connecting-message"> Connecting... </div>
|
||||
<div id="controls">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="user-info">
|
||||
<span>Call with</span>
|
||||
<!-- User Name -->
|
||||
|
|
@ -444,38 +141,36 @@
|
|||
</div>
|
||||
<div id="video-container" class="card-body bg-dark p-0 d-flex align-items-center position-relative">
|
||||
<div id="local-stream" class="ratio ratio-4x3 rounded m-0 p-0 shadow">
|
||||
<!-- <p class="text-white m-auto text-center">Starting video...</p> -->
|
||||
<p class="text-white m-auto text-center">Starting video...</p>
|
||||
<video id="video-local" autoplay muted></video>
|
||||
</div>
|
||||
|
||||
<div id="remote-stream" class="ratio ratio-4x3 m-0 p-0">
|
||||
<!-- <p id="remote-stream-placeholder" class="text-white m-auto text-center">Starting video...</p> -->
|
||||
<p id="remote-stream-placeholder" class="text-white m-auto text-center">Starting video...</p>
|
||||
<video id="video-remote" autoplay></video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footers">
|
||||
<div class="card-footer bg-transparent d-flex justify-content-between">
|
||||
<div class="assist-controls">
|
||||
<!-- Add class .muted to #audio-btn when user mutes audio -->
|
||||
<button href="#" id="audio-btn" class="btn btn-light btn-sm text-uppercase me-2">
|
||||
<i>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-mic" viewBox="0 0 16 16">
|
||||
<a href="#" id="audio-btn" class="btn btn-light btn-sm text-uppercase me-2"><i>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-mic"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5z" />
|
||||
<path d="M10 8a2 2 0 1 1-4 0V3a2 2 0 1 1 4 0v5zM8 0a3 3 0 0 0-3 3v5a3 3 0 0 0 6 0V3a3 3 0 0 0-3-3z" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-mic-mute" viewBox="0 0 16 16">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-mic-mute"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M13 8c0 .564-.094 1.107-.266 1.613l-.814-.814A4.02 4.02 0 0 0 12 8V7a.5.5 0 0 1 1 0v1zm-5 4c.818 0 1.578-.245 2.212-.667l.718.719a4.973 4.973 0 0 1-2.43.923V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 1 0v1a4 4 0 0 0 4 4zm3-9v4.879l-1-1V3a2 2 0 0 0-3.997-.118l-.845-.845A3.001 3.001 0 0 1 11 3z" />
|
||||
<path
|
||||
d="m9.486 10.607-.748-.748A2 2 0 0 1 6 8v-.878l-1-1V8a3 3 0 0 0 4.486 2.607zm-7.84-9.253 12 12 .708-.708-12-12-.708.708z" />
|
||||
</svg>
|
||||
</i>
|
||||
</button>
|
||||
|
||||
<!--Add class .off to #video-btn when user stops video -->
|
||||
<button href="#" id="video-btn" class="btn btn-light btn-sm text-uppercase ms-2">
|
||||
<i>
|
||||
</i></a>
|
||||
<!-- Add class .mute to #audio-btn when user mutes audio -->
|
||||
<a href="#" id="video-btn" class="off btn btn-light btn-sm text-uppercase ms-2"><i>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-camera-video" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
|
|
@ -486,70 +181,22 @@
|
|||
<path fill-rule="evenodd"
|
||||
d="M10.961 12.365a1.99 1.99 0 0 0 .522-1.103l3.11 1.382A1 1 0 0 0 16 11.731V4.269a1 1 0 0 0-1.406-.913l-3.111 1.382A2 2 0 0 0 9.5 3H4.272l.714 1H9.5a1 1 0 0 1 1 1v6a1 1 0 0 1-.144.518l.605.847zM1.428 4.18A.999.999 0 0 0 1 5v6a1 1 0 0 0 1 1h5.014l.714 1H2a2 2 0 0 1-2-2V5c0-.675.334-1.272.847-1.634l.58.814zM15 11.73l-3.5-1.555v-4.35L15 4.269v7.462zm-4.407 3.56-10-14 .814-.58 10 14-.814.58z" />
|
||||
</svg>
|
||||
</i>
|
||||
</button>
|
||||
|
||||
</i></a>
|
||||
<!--Add class .off to #video-btn when user stops video -->
|
||||
</div>
|
||||
|
||||
<button id="end-call-btn" href="#" class="btn btn-danger btn-sm text-uppercase" style="margin-right: 8px">
|
||||
End
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- CHAT - add .active class to show the messages and input -->
|
||||
<div id="chat-card" class="active">
|
||||
<div class="chat-header">
|
||||
<div class="chat-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-chat"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M2.678 11.894a1 1 0 0 1 .287.801 10.97 10.97 0 0 1-.398 2c1.395-.323 2.247-.697 2.634-.893a1 1 0 0 1 .71-.074A8.06 8.06 0 0 0 8 14c3.996 0 7-2.807 7-6 0-3.192-3.004-6-7-6S1 4.808 1 8c0 1.468.617 2.83 1.678 3.894zm-.493 3.905a21.682 21.682 0 0 1-.713.129c-.2.032-.352-.176-.273-.362a9.68 9.68 0 0 0 .244-.637l.003-.01c.248-.72.45-1.548.524-2.319C.743 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7-3.582 7-8 7a9.06 9.06 0 0 1-2.347-.306c-.52.263-1.639.742-3.468 1.105z" />
|
||||
</svg>
|
||||
<span>Chat</span>
|
||||
</div>
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" class="bi bi-chevron-up arrow-state"
|
||||
viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-messages">
|
||||
<div class="message left">
|
||||
<div class="message-text">Hey, did you get the key?</div>
|
||||
<div>
|
||||
<span class="message-user">Username</span>
|
||||
<span class="message-time"> 00:00 </span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message right">
|
||||
<div class="message-text">Oui, merci!</div>
|
||||
<div>
|
||||
<span class="message-user">Username</span>
|
||||
<span class="message-time">00:00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<input type="text" class="input" placeholder="Type a message..." />
|
||||
<div class="send-btn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="bi bi-arrow-right-short"
|
||||
viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M4 8a.5.5 0 0 1 .5-.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5A.5.5 0 0 1 4 8z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="assist-end">
|
||||
<a id="end-call-btn" style="min-width:55px;" href="#" class="btn btn-danger btn-sm text-uppercase">End</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="remote-control-row" class="remote-control">
|
||||
<div>Remote control active</div>
|
||||
<button id="end-control-btn" href="#" class="btn btn-danger btn-sm text-uppercase">
|
||||
End
|
||||
<button style="min-width:55px;" id="end-control-btn" href="#" class="btn btn-danger btn-sm text-uppercase">
|
||||
Stop
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
|
|
|
|||
|
|
@ -158,8 +158,9 @@ export default class Assist {
|
|||
annot = null
|
||||
}
|
||||
callUI?.hideRemoteControl()
|
||||
if (!CallingState.True) {
|
||||
if (this.callingState !== CallingState.True) {
|
||||
callUI?.remove()
|
||||
callUI = null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,13 +170,18 @@ export default class Assist {
|
|||
if (!callUI) {
|
||||
callUI = new CallWindow(app.debug.error)
|
||||
}
|
||||
callUI.showRemoteControl()
|
||||
callUI.setRemoteControlEnd(() => releaseControlCb(id))
|
||||
setTimeout(() => {
|
||||
if (this.callingState === CallingState.False) {
|
||||
callUI?.showRemoteOnly()
|
||||
} else {
|
||||
callUI?.showRemoteControl()
|
||||
}
|
||||
}, 150)
|
||||
this.agents[id].onControlReleased = this.options.onRemoteControlStart()
|
||||
this.emit('control_granted', id)
|
||||
annot = new AnnotationCanvas()
|
||||
annot.mount()
|
||||
return callingAgents.get(id)
|
||||
return { agentName: callingAgents.get(id), callUI, }
|
||||
},
|
||||
releaseControlCb,
|
||||
)
|
||||
|
|
@ -317,6 +323,7 @@ export default class Assist {
|
|||
|
||||
// UI
|
||||
closeCallConfirmWindow()
|
||||
remoteControl.releaseControl()
|
||||
callUI?.remove()
|
||||
annot?.remove()
|
||||
callUI = null
|
||||
|
|
@ -377,6 +384,8 @@ export default class Assist {
|
|||
callUI = new CallWindow(app.debug.error)
|
||||
}
|
||||
callUI.setCallEndAction(initiateCallEnd)
|
||||
callUI.showControls()
|
||||
|
||||
if (!annot) {
|
||||
annot = new AnnotationCanvas()
|
||||
annot.mount()
|
||||
|
|
|
|||
|
|
@ -4,274 +4,321 @@ import attachDND from './dnd.js'
|
|||
const SS_START_TS_KEY = '__openreplay_assist_call_start_ts'
|
||||
|
||||
export default class CallWindow {
|
||||
private readonly iframe: HTMLIFrameElement
|
||||
private vRemote: HTMLVideoElement | null = null
|
||||
private vLocal: HTMLVideoElement | null = null
|
||||
private audioBtn: HTMLElement | null = null
|
||||
private videoBtn: HTMLElement | null = null
|
||||
private endCallBtn: HTMLElement | null = null
|
||||
private agentNameElem: HTMLElement | null = null
|
||||
private videoContainer: HTMLElement | null = null
|
||||
private vPlaceholder: HTMLElement | null = null
|
||||
private remoteControlContainer: HTMLElement | null = null
|
||||
private remoteControlEndBtn: HTMLElement | null = null
|
||||
private readonly iframe: HTMLIFrameElement
|
||||
private vRemote: HTMLVideoElement | null = null
|
||||
private vLocal: HTMLVideoElement | null = null
|
||||
private audioBtn: HTMLElement | null = null
|
||||
private videoBtn: HTMLElement | null = null
|
||||
private endCallBtn: HTMLElement | null = null
|
||||
private agentNameElem: HTMLElement | null = null
|
||||
private videoContainer: HTMLElement | null = null
|
||||
private vPlaceholder: HTMLElement | null = null
|
||||
private remoteControlContainer: HTMLElement | null = null
|
||||
private remoteControlEndBtn: HTMLElement | null = null
|
||||
private controlsContainer: HTMLElement | null = null
|
||||
|
||||
private tsInterval: ReturnType<typeof setInterval>
|
||||
private tsInterval: ReturnType<typeof setInterval>
|
||||
|
||||
private readonly load: Promise<void>
|
||||
private readonly load: Promise<void>
|
||||
|
||||
constructor(private readonly logError: (...args: any[]) => void) {
|
||||
const iframe = this.iframe = document.createElement('iframe')
|
||||
Object.assign(iframe.style, {
|
||||
position: 'fixed',
|
||||
zIndex: 2147483647 - 1,
|
||||
border: 'none',
|
||||
bottom: '10px',
|
||||
right: '10px',
|
||||
height: '200px',
|
||||
width: '200px',
|
||||
})
|
||||
// TODO: find the best attribute name for the ignoring iframes
|
||||
iframe.setAttribute('data-openreplay-obscured', '')
|
||||
iframe.setAttribute('data-openreplay-hidden', '')
|
||||
iframe.setAttribute('data-openreplay-ignore', '')
|
||||
document.body.appendChild(iframe)
|
||||
constructor(private readonly logError: (...args: any[]) => void) {
|
||||
const iframe = (this.iframe = document.createElement('iframe'))
|
||||
Object.assign(iframe.style, {
|
||||
position: 'fixed',
|
||||
zIndex: 2147483647 - 1,
|
||||
border: 'none',
|
||||
bottom: '10px',
|
||||
right: '10px',
|
||||
height: '200px',
|
||||
width: '200px',
|
||||
})
|
||||
// TODO: find the best attribute name for the ignoring iframes
|
||||
iframe.setAttribute('data-openreplay-obscured', '')
|
||||
iframe.setAttribute('data-openreplay-hidden', '')
|
||||
iframe.setAttribute('data-openreplay-ignore', '')
|
||||
document.body.appendChild(iframe)
|
||||
|
||||
const doc = iframe.contentDocument
|
||||
if (!doc) {
|
||||
console.error('OpenReplay: CallWindow iframe document is not reachable.')
|
||||
return
|
||||
}
|
||||
const doc = iframe.contentDocument
|
||||
if (!doc) {
|
||||
console.error('OpenReplay: CallWindow iframe document is not reachable.')
|
||||
return
|
||||
}
|
||||
|
||||
//const baseHref = "https://static.openreplay.com/tracker-assist/test"
|
||||
const baseHref = 'https://static.openreplay.com/tracker-assist/4.0.0'
|
||||
this.load = fetch(baseHref + '/index.html')
|
||||
.then((r) => r.text())
|
||||
.then((text) => {
|
||||
iframe.onload = () => {
|
||||
const assistSection = doc.getElementById('or-assist')
|
||||
assistSection?.classList.remove('status-connecting')
|
||||
//iframe.style.height = doc.body.scrollHeight + 'px';
|
||||
//iframe.style.width = doc.body.scrollWidth + 'px';
|
||||
this.adjustIframeSize()
|
||||
iframe.onload = null
|
||||
}
|
||||
|
||||
//const baseHref = "https://static.openreplay.com/tracker-assist/test"
|
||||
const baseHref = 'https://static.openreplay.com/tracker-assist/4.0.0'
|
||||
this.load = fetch(baseHref + '/index.html')
|
||||
.then(r => r.text())
|
||||
.then((text) => {
|
||||
iframe.onload = () => {
|
||||
const assistSection = doc.getElementById('or-assist')
|
||||
assistSection?.classList.remove('status-connecting')
|
||||
//iframe.style.height = doc.body.scrollHeight + 'px';
|
||||
//iframe.style.width = doc.body.scrollWidth + 'px';
|
||||
this.adjustIframeSize()
|
||||
iframe.onload = null
|
||||
}
|
||||
// ?
|
||||
text = text.replace(/href="css/g, `href="${baseHref}/css`)
|
||||
doc.open()
|
||||
doc.write(text)
|
||||
doc.close()
|
||||
|
||||
// ?
|
||||
text = text.replace(/href="css/g, `href="${baseHref}/css`)
|
||||
doc.open()
|
||||
doc.write(text)
|
||||
doc.close()
|
||||
this.vLocal = doc.getElementById('video-local') as HTMLVideoElement | null
|
||||
this.vRemote = doc.getElementById('video-remote') as HTMLVideoElement | null
|
||||
this.videoContainer = doc.getElementById('video-container')
|
||||
|
||||
this.audioBtn = doc.getElementById('audio-btn')
|
||||
if (this.audioBtn) {
|
||||
this.audioBtn.onclick = () => this.toggleAudio()
|
||||
}
|
||||
this.videoBtn = doc.getElementById('video-btn')
|
||||
if (this.videoBtn) {
|
||||
this.videoBtn.onclick = () => this.toggleVideo()
|
||||
}
|
||||
this.endCallBtn = doc.getElementById('end-call-btn')
|
||||
|
||||
this.vLocal = doc.getElementById('video-local') as (HTMLVideoElement | null)
|
||||
this.vRemote = doc.getElementById('video-remote') as (HTMLVideoElement | null)
|
||||
this.videoContainer = doc.getElementById('video-container')
|
||||
this.agentNameElem = doc.getElementById('agent-name')
|
||||
this.vPlaceholder = doc.querySelector('#remote-stream p')
|
||||
|
||||
this.audioBtn = doc.getElementById('audio-btn')
|
||||
if (this.audioBtn) {
|
||||
this.audioBtn.onclick = () => this.toggleAudio()
|
||||
}
|
||||
this.videoBtn = doc.getElementById('video-btn')
|
||||
if (this.videoBtn) {
|
||||
this.videoBtn.onclick = () => this.toggleVideo()
|
||||
}
|
||||
this.endCallBtn = doc.getElementById('end-call-btn')
|
||||
this.remoteControlContainer = doc.getElementById('remote-control-row')
|
||||
this.remoteControlEndBtn = doc.getElementById('end-control-btn')
|
||||
this.controlsContainer = doc.getElementById('controls')
|
||||
|
||||
this.agentNameElem = doc.getElementById('agent-name')
|
||||
this.vPlaceholder = doc.querySelector('#remote-stream p')
|
||||
const tsElem = doc.getElementById('duration')
|
||||
if (tsElem) {
|
||||
const startTs =
|
||||
Number(sessionStorage.getItem(SS_START_TS_KEY)) || Date.now()
|
||||
sessionStorage.setItem(SS_START_TS_KEY, startTs.toString())
|
||||
this.tsInterval = setInterval(() => {
|
||||
const ellapsed = Date.now() - startTs
|
||||
const secsFull = ~~(ellapsed / 1000)
|
||||
const mins = ~~(secsFull / 60)
|
||||
const secs = secsFull - mins * 60
|
||||
tsElem.innerText = `${mins}:${secs < 10 ? 0 : ''}${secs}`
|
||||
}, 500)
|
||||
}
|
||||
|
||||
this.remoteControlContainer = doc.getElementById('remote-control-row')
|
||||
this.remoteControlEndBtn = doc.getElementById('end-control-btn')
|
||||
const dragArea = doc.querySelector('.drag-area')
|
||||
if (dragArea) {
|
||||
// TODO: save coordinates on the new page
|
||||
attachDND(iframe, dragArea, doc.documentElement)
|
||||
}
|
||||
})
|
||||
|
||||
const tsElem = doc.getElementById('duration')
|
||||
if (tsElem) {
|
||||
const startTs = Number(sessionStorage.getItem(SS_START_TS_KEY)) || Date.now()
|
||||
sessionStorage.setItem(SS_START_TS_KEY, startTs.toString())
|
||||
this.tsInterval = setInterval(() => {
|
||||
const ellapsed = Date.now() - startTs
|
||||
const secsFull = ~~(ellapsed / 1000)
|
||||
const mins = ~~(secsFull / 60)
|
||||
const secs = secsFull - mins * 60
|
||||
tsElem.innerText = `${mins}:${secs < 10 ? 0 : ''}${secs}`
|
||||
}, 500)
|
||||
}
|
||||
//this.toggleVideoUI(false)
|
||||
//this.toggleRemoteVideoUI(false)
|
||||
}
|
||||
|
||||
const dragArea = doc.querySelector('.drag-area')
|
||||
if (dragArea) {
|
||||
// TODO: save coordinates on the new page
|
||||
attachDND(iframe, dragArea, doc.documentElement)
|
||||
}
|
||||
})
|
||||
private adjustIframeSize() {
|
||||
const doc = this.iframe.contentDocument
|
||||
if (!doc) {
|
||||
return
|
||||
}
|
||||
this.iframe.style.height = `${doc.body.scrollHeight}px`
|
||||
this.iframe.style.width = `${doc.body.scrollWidth}px`
|
||||
}
|
||||
|
||||
//this.toggleVideoUI(false)
|
||||
//this.toggleRemoteVideoUI(false)
|
||||
}
|
||||
setCallEndAction(endCall: () => void) {
|
||||
this.load
|
||||
.then(() => {
|
||||
if (this.endCallBtn) {
|
||||
this.endCallBtn.onclick = endCall
|
||||
}
|
||||
})
|
||||
.catch((e) => this.logError(e))
|
||||
}
|
||||
|
||||
private adjustIframeSize() {
|
||||
const doc = this.iframe.contentDocument
|
||||
if (!doc) { return }
|
||||
this.iframe.style.height = `${doc.body.scrollHeight}px`
|
||||
this.iframe.style.width = `${doc.body.scrollWidth}px`
|
||||
}
|
||||
setRemoteControlEnd(endControl: () => void) {
|
||||
this.load
|
||||
.then(() => {
|
||||
if (this.remoteControlEndBtn) {
|
||||
this.remoteControlEndBtn.onclick = endControl
|
||||
}
|
||||
})
|
||||
.catch((e) => this.logError(e))
|
||||
}
|
||||
|
||||
setCallEndAction(endCall: () => void) {
|
||||
this.load.then(() => {
|
||||
if (this.endCallBtn) {
|
||||
this.endCallBtn.onclick = endCall
|
||||
}
|
||||
}).catch(e => this.logError(e))
|
||||
}
|
||||
private checkRemoteVideoInterval: ReturnType<typeof setInterval>
|
||||
private audioContainer: HTMLDivElement | null = null
|
||||
addRemoteStream(rStream: MediaStream) {
|
||||
this.load
|
||||
.then(() => {
|
||||
// Video
|
||||
if (this.vRemote && !this.vRemote.srcObject) {
|
||||
this.vRemote.srcObject = rStream
|
||||
if (this.vPlaceholder) {
|
||||
this.vPlaceholder.innerText =
|
||||
'Video has been paused. Click anywhere to resume.'
|
||||
}
|
||||
// Hack to determine if the remote video is enabled
|
||||
// TODO: pass this info through socket
|
||||
if (this.checkRemoteVideoInterval) {
|
||||
clearInterval(this.checkRemoteVideoInterval)
|
||||
} // just in case
|
||||
let enabled = false
|
||||
this.checkRemoteVideoInterval = setInterval(() => {
|
||||
const settings = rStream.getVideoTracks()[0]?.getSettings()
|
||||
const isDummyVideoTrack =
|
||||
!!settings && (settings.width === 2 || settings.frameRate === 0)
|
||||
const shouldBeEnabled = !isDummyVideoTrack
|
||||
if (enabled !== shouldBeEnabled) {
|
||||
this.toggleRemoteVideoUI((enabled = shouldBeEnabled))
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
setRemoteControlEnd(endControl: () => void) {
|
||||
this.load.then(() => {
|
||||
if (this.remoteControlEndBtn) {
|
||||
this.remoteControlEndBtn.onclick = endControl
|
||||
}
|
||||
}).catch(e => this.logError(e))
|
||||
}
|
||||
// Audio
|
||||
if (!this.audioContainer) {
|
||||
this.audioContainer = document.createElement('div')
|
||||
document.body.appendChild(this.audioContainer)
|
||||
}
|
||||
// Hack for audio. Doesen't work inside the iframe
|
||||
// because of some magical reasons (check if it is connected to autoplay?)
|
||||
const audioEl = document.createElement('audio')
|
||||
audioEl.autoplay = true
|
||||
audioEl.style.display = 'none'
|
||||
audioEl.srcObject = rStream
|
||||
this.audioContainer.appendChild(audioEl)
|
||||
})
|
||||
.catch((e) => this.logError(e))
|
||||
}
|
||||
|
||||
private checkRemoteVideoInterval: ReturnType<typeof setInterval>
|
||||
private audioContainer: HTMLDivElement | null = null
|
||||
addRemoteStream(rStream: MediaStream) {
|
||||
this.load.then(() => {
|
||||
// Video
|
||||
if (this.vRemote && !this.vRemote.srcObject) {
|
||||
this.vRemote.srcObject = rStream
|
||||
if (this.vPlaceholder) {
|
||||
this.vPlaceholder.innerText = 'Video has been paused. Click anywhere to resume.'
|
||||
}
|
||||
// Hack to determine if the remote video is enabled
|
||||
// TODO: pass this info through socket
|
||||
if (this.checkRemoteVideoInterval) { clearInterval(this.checkRemoteVideoInterval) } // just in case
|
||||
let enabled = false
|
||||
this.checkRemoteVideoInterval = setInterval(() => {
|
||||
const settings = rStream.getVideoTracks()[0]?.getSettings()
|
||||
const isDummyVideoTrack = !!settings && (settings.width === 2 || settings.frameRate === 0)
|
||||
const shouldBeEnabled = !isDummyVideoTrack
|
||||
if (enabled !== shouldBeEnabled) {
|
||||
this.toggleRemoteVideoUI(enabled=shouldBeEnabled)
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
toggleRemoteVideoUI(enable: boolean) {
|
||||
this.load
|
||||
.then(() => {
|
||||
if (this.videoContainer) {
|
||||
if (enable) {
|
||||
this.videoContainer.classList.add('remote')
|
||||
} else {
|
||||
this.videoContainer.classList.remove('remote')
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
})
|
||||
.catch((e) => this.logError(e))
|
||||
}
|
||||
|
||||
// Audio
|
||||
if (!this.audioContainer) {
|
||||
this.audioContainer = document.createElement('div')
|
||||
document.body.appendChild(this.audioContainer)
|
||||
}
|
||||
// Hack for audio. Doesen't work inside the iframe
|
||||
// because of some magical reasons (check if it is connected to autoplay?)
|
||||
const audioEl = document.createElement('audio')
|
||||
audioEl.autoplay = true
|
||||
audioEl.style.display = 'none'
|
||||
audioEl.srcObject = rStream
|
||||
this.audioContainer.appendChild(audioEl)
|
||||
}).catch(e => this.logError(e))
|
||||
}
|
||||
private localStreams: LocalStream[] = []
|
||||
// !TODO: separate streams manipulation from ui
|
||||
setLocalStreams(streams: LocalStream[]) {
|
||||
this.localStreams = streams
|
||||
}
|
||||
|
||||
toggleRemoteVideoUI(enable: boolean) {
|
||||
this.load.then(() => {
|
||||
if (this.videoContainer) {
|
||||
if (enable) {
|
||||
this.videoContainer.classList.add('remote')
|
||||
} else {
|
||||
this.videoContainer.classList.remove('remote')
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
}).catch(e => this.logError(e))
|
||||
}
|
||||
playRemote() {
|
||||
this.vRemote && this.vRemote.play()
|
||||
}
|
||||
|
||||
private localStreams: LocalStream[] = []
|
||||
// !TODO: separate streams manipulation from ui
|
||||
setLocalStreams(streams: LocalStream[]) {
|
||||
this.localStreams = streams
|
||||
}
|
||||
setAssistentName(callingAgents: Map<string, string>) {
|
||||
this.load
|
||||
.then(() => {
|
||||
if (this.agentNameElem) {
|
||||
const nameString = Array.from(callingAgents.values()).join(', ')
|
||||
const safeNames =
|
||||
nameString.length > 20 ? nameString.substring(0, 20) + '...' : nameString
|
||||
this.agentNameElem.innerText = safeNames
|
||||
}
|
||||
})
|
||||
.catch((e) => this.logError(e))
|
||||
}
|
||||
|
||||
playRemote() {
|
||||
this.vRemote && this.vRemote.play()
|
||||
}
|
||||
private toggleAudioUI(enabled: boolean) {
|
||||
if (!this.audioBtn) {
|
||||
return
|
||||
}
|
||||
if (enabled) {
|
||||
this.audioBtn.classList.remove('muted')
|
||||
} else {
|
||||
this.audioBtn.classList.add('muted')
|
||||
}
|
||||
}
|
||||
|
||||
setAssistentName(callingAgents: Map<string, string>) {
|
||||
this.load.then(() => {
|
||||
if (this.agentNameElem) {
|
||||
const nameString = Array.from(callingAgents.values()).join(', ')
|
||||
const safeNames = nameString.length > 20 ? nameString.substring(0, 20) + '...' : nameString
|
||||
this.agentNameElem.innerText = safeNames
|
||||
}
|
||||
}).catch(e => this.logError(e))
|
||||
}
|
||||
private toggleAudio() {
|
||||
let enabled = false
|
||||
this.localStreams.forEach((stream) => {
|
||||
enabled = stream.toggleAudio() || false
|
||||
})
|
||||
this.toggleAudioUI(enabled)
|
||||
}
|
||||
|
||||
private toggleVideoUI(enabled: boolean) {
|
||||
if (!this.videoBtn || !this.videoContainer) {
|
||||
return
|
||||
}
|
||||
if (enabled) {
|
||||
this.videoContainer.classList.add('local')
|
||||
this.videoBtn.classList.remove('off')
|
||||
} else {
|
||||
this.videoContainer.classList.remove('local')
|
||||
this.videoBtn.classList.add('off')
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
|
||||
private toggleAudioUI(enabled: boolean) {
|
||||
if (!this.audioBtn) { return }
|
||||
if (enabled) {
|
||||
this.audioBtn.classList.remove('muted')
|
||||
} else {
|
||||
this.audioBtn.classList.add('muted')
|
||||
}
|
||||
}
|
||||
private toggleVideo() {
|
||||
this.localStreams.forEach((stream) => {
|
||||
stream
|
||||
.toggleVideo()
|
||||
.then((enabled) => {
|
||||
this.toggleVideoUI(enabled)
|
||||
this.load
|
||||
.then(() => {
|
||||
if (this.vLocal && stream && !this.vLocal.srcObject) {
|
||||
this.vLocal.srcObject = stream.stream
|
||||
}
|
||||
})
|
||||
.catch((e) => this.logError(e))
|
||||
})
|
||||
.catch((e) => this.logError(e))
|
||||
})
|
||||
}
|
||||
|
||||
private toggleAudio() {
|
||||
let enabled = false
|
||||
this.localStreams.forEach(stream => {
|
||||
enabled = stream.toggleAudio() || false
|
||||
})
|
||||
this.toggleAudioUI(enabled)
|
||||
}
|
||||
public showRemoteControl() {
|
||||
if (this.remoteControlContainer) {
|
||||
this.remoteControlContainer.style.display = 'flex'
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
|
||||
private toggleVideoUI(enabled: boolean) {
|
||||
if (!this.videoBtn || !this.videoContainer) { return }
|
||||
if (enabled) {
|
||||
this.videoContainer.classList.add('local')
|
||||
this.videoBtn.classList.remove('off')
|
||||
} else {
|
||||
this.videoContainer.classList.remove('local')
|
||||
this.videoBtn.classList.add('off')
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
public showRemoteOnly() {
|
||||
if (this.controlsContainer) {
|
||||
this.controlsContainer.style.display = 'none'
|
||||
}
|
||||
this.showRemoteControl()
|
||||
}
|
||||
|
||||
private toggleVideo() {
|
||||
this.localStreams.forEach(stream => {
|
||||
stream.toggleVideo()
|
||||
.then(enabled => {
|
||||
this.toggleVideoUI(enabled)
|
||||
this.load.then(() => {
|
||||
if (this.vLocal && stream && !this.vLocal.srcObject) {
|
||||
this.vLocal.srcObject = stream.stream
|
||||
}
|
||||
}).catch(e => this.logError(e))
|
||||
}).catch(e => this.logError(e))
|
||||
})
|
||||
}
|
||||
public showControls() {
|
||||
if (this.controlsContainer) {
|
||||
this.controlsContainer.style.display = 'unset'
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
|
||||
public showRemoteControl() {
|
||||
if (this.remoteControlContainer) {
|
||||
this.remoteControlContainer.style.display = 'flex'
|
||||
}
|
||||
}
|
||||
public hideControls() {
|
||||
if (this.controlsContainer) {
|
||||
this.controlsContainer.style.display = 'none'
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
|
||||
public hideRemoteControl() {
|
||||
if (this.remoteControlContainer) {
|
||||
this.remoteControlContainer.style.display = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
clearInterval(this.tsInterval)
|
||||
clearInterval(this.checkRemoteVideoInterval)
|
||||
if (this.audioContainer && this.audioContainer.parentElement) {
|
||||
this.audioContainer.parentElement.removeChild(this.audioContainer)
|
||||
this.audioContainer = null
|
||||
}
|
||||
if (this.iframe.parentElement) {
|
||||
this.iframe.parentElement.removeChild(this.iframe)
|
||||
}
|
||||
sessionStorage.removeItem(SS_START_TS_KEY)
|
||||
this.localStreams = []
|
||||
}
|
||||
public hideRemoteControl() {
|
||||
if (this.remoteControlContainer) {
|
||||
this.remoteControlContainer.style.display = 'none'
|
||||
}
|
||||
this.adjustIframeSize()
|
||||
}
|
||||
|
||||
remove() {
|
||||
clearInterval(this.tsInterval)
|
||||
clearInterval(this.checkRemoteVideoInterval)
|
||||
if (this.audioContainer && this.audioContainer.parentElement) {
|
||||
this.audioContainer.parentElement.removeChild(this.audioContainer)
|
||||
this.audioContainer = null
|
||||
}
|
||||
if (this.iframe.parentElement) {
|
||||
this.iframe.parentElement.removeChild(this.iframe)
|
||||
}
|
||||
sessionStorage.removeItem(SS_START_TS_KEY)
|
||||
this.localStreams = []
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export default class RemoteControl {
|
|||
|
||||
constructor(
|
||||
private readonly options: AssistOptions,
|
||||
private readonly onGrand: (string?) => string | undefined,
|
||||
private readonly onGrand: (string?) => { agentName: string | undefined, callUI: any },
|
||||
private readonly onRelease: (string?) => void) {}
|
||||
|
||||
reconnect(ids: string[]) {
|
||||
|
|
@ -71,21 +71,31 @@ export default class RemoteControl {
|
|||
this.agentID = id
|
||||
this.status = RCStatus.Enabled
|
||||
sessionStorage.setItem(this.options.session_control_peer_key, id)
|
||||
const agentName = this.onGrand(id)
|
||||
const { agentName, callUI, } = this.onGrand(id)
|
||||
if (callUI) {
|
||||
callUI?.setRemoteControlEnd(this.releaseControl)
|
||||
}
|
||||
if (this.mouse) {
|
||||
this.resetMouse()
|
||||
}
|
||||
this.mouse = new Mouse(agentName)
|
||||
this.mouse.mount()
|
||||
}
|
||||
|
||||
releaseControl = () => {
|
||||
if (!this.agentID) { return }
|
||||
this.mouse?.remove()
|
||||
this.mouse = null
|
||||
this.resetMouse()
|
||||
this.status = RCStatus.Disabled
|
||||
sessionStorage.removeItem(this.options.session_control_peer_key)
|
||||
this.onRelease(this.agentID)
|
||||
this.agentID = null
|
||||
}
|
||||
|
||||
resetMouse = () => {
|
||||
this.mouse?.remove()
|
||||
this.mouse = null
|
||||
}
|
||||
|
||||
scroll = (id, d) => { id === this.agentID && this.mouse?.scroll(d) }
|
||||
move = (id, xy) => { id === this.agentID && this.mouse?.move(xy) }
|
||||
private focused: HTMLElement | null = null
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue