feat(tracker-assist):3.2.0: maintaining call resume on page reload
This commit is contained in:
parent
ba7ae009c4
commit
afaab71bf4
6 changed files with 153 additions and 105 deletions
2
tracker/tracker-assist/package-lock.json
generated
2
tracker/tracker-assist/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openreplay/tracker-assist",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@openreplay/tracker-assist",
|
||||
"description": "Tracker plugin for screen assistance through the WebRTC",
|
||||
"version": "3.1.1",
|
||||
"version": "3.2.0",
|
||||
"keywords": [
|
||||
"WebRTC",
|
||||
"assistance",
|
||||
|
|
@ -24,10 +24,10 @@
|
|||
"peerjs": "^1.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@openreplay/tracker": "^3.3.0"
|
||||
"@openreplay/tracker": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openreplay/tracker": "^3.3.0",
|
||||
"@openreplay/tracker": "^3.4.0",
|
||||
"prettier": "^1.18.2",
|
||||
"replace-in-files-cli": "^1.0.0",
|
||||
"typescript": "^3.6.4"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export default class CallWindow {
|
|||
private audioBtn: HTMLAnchorElement | null = null;
|
||||
private videoBtn: HTMLAnchorElement | null = null;
|
||||
private userNameSpan: HTMLSpanElement | null = null;
|
||||
private vPlaceholder: HTMLParagraphElement | null = null;
|
||||
|
||||
private tsInterval: ReturnType<typeof setInterval>;
|
||||
constructor(endCall: () => void) {
|
||||
|
|
@ -23,84 +24,84 @@ export default class CallWindow {
|
|||
height: "200px",
|
||||
width: "200px",
|
||||
});
|
||||
//iframe.src = "//static.openreplay.com/tracker-assist/index.html";
|
||||
iframe.onload = () => {
|
||||
const doc = iframe.contentDocument;
|
||||
if (!doc) {
|
||||
console.error("OpenReplay: CallWindow iframe document is not reachable.")
|
||||
return;
|
||||
}
|
||||
fetch("https://static.openreplay.com/tracker-assist/index.html")
|
||||
//fetch("file:///Users/shikhu/work/asayer-tester/dist/assist/index.html")
|
||||
.then(r => r.text())
|
||||
.then((text) => {
|
||||
iframe.onload = () => {
|
||||
doc.body.removeChild(doc.body.children[0]); //?!!>R#
|
||||
const assistSection = doc.getElementById("or-assist")
|
||||
assistSection && assistSection.removeAttribute("style");
|
||||
iframe.style.height = doc.body.scrollHeight + 'px';
|
||||
iframe.style.width = doc.body.scrollWidth + 'px';
|
||||
iframe.onload = null;
|
||||
}
|
||||
|
||||
text = text.replace(/href="css/g, "href=\"https://static.openreplay.com/tracker-assist/css")
|
||||
doc.open();
|
||||
doc.write(text);
|
||||
doc.close();
|
||||
|
||||
|
||||
this.vLocal = doc.getElementById("video-local") as HTMLVideoElement;
|
||||
this.vRemote = doc.getElementById("video-remote") as HTMLVideoElement;
|
||||
this._trySetStreams();
|
||||
//
|
||||
this.vLocal.parentElement && this.vLocal.parentElement.classList.add("d-none");
|
||||
|
||||
this.audioBtn = doc.getElementById("audio-btn") as HTMLAnchorElement;
|
||||
this.audioBtn.onclick = () => this.toggleAudio();
|
||||
this.videoBtn = doc.getElementById("video-btn") as HTMLAnchorElement;
|
||||
this.videoBtn.onclick = () => this.toggleVideo();
|
||||
|
||||
this.userNameSpan = doc.getElementById("username") as HTMLSpanElement;
|
||||
this._trySetAssistentName();
|
||||
|
||||
const endCallBtn = doc.getElementById("end-call-btn") as HTMLAnchorElement;
|
||||
endCallBtn.onclick = endCall;
|
||||
|
||||
const tsText = doc.getElementById("time-stamp");
|
||||
const startTs = Date.now();
|
||||
if (tsText) {
|
||||
this.tsInterval = setInterval(() => {
|
||||
const ellapsed = Date.now() - startTs;
|
||||
const secsFull = ~~(ellapsed / 1000);
|
||||
const mins = ~~(secsFull / 60);
|
||||
const secs = secsFull - mins * 60
|
||||
tsText.innerText = `${mins}:${secs < 10 ? 0 : ''}${secs}`;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// TODO: better D'n'D
|
||||
doc.body.setAttribute("draggable", "true");
|
||||
doc.body.ondragstart = (e) => {
|
||||
if (!e.dataTransfer || !e.target) { return; }
|
||||
//@ts-ignore
|
||||
if (!e.target.classList || !e.target.classList.contains("card-header")) { return; }
|
||||
e.dataTransfer.setDragImage(doc.body, e.clientX, e.clientY);
|
||||
};
|
||||
doc.body.ondragend = e => {
|
||||
Object.assign(iframe.style, {
|
||||
left: `${e.clientX}px`,
|
||||
top: `${e.clientY}px`,
|
||||
bottom: 'auto',
|
||||
right: 'auto',
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
const doc = iframe.contentDocument;
|
||||
if (!doc) {
|
||||
console.error("OpenReplay: CallWindow iframe document is not reachable.")
|
||||
return;
|
||||
}
|
||||
fetch("https://static.openreplay.com/tracker-assist/index.html")
|
||||
//fetch("file:///Users/shikhu/work/asayer-tester/dist/assist/index.html")
|
||||
.then(r => r.text())
|
||||
.then((text) => {
|
||||
iframe.onload = () => {
|
||||
doc.body.removeChild(doc.body.children[0]); //?!!>R#
|
||||
const assistSection = doc.getElementById("or-assist")
|
||||
assistSection && assistSection.removeAttribute("style");
|
||||
iframe.style.height = doc.body.scrollHeight + 'px';
|
||||
iframe.style.width = doc.body.scrollWidth + 'px';
|
||||
iframe.onload = null;
|
||||
}
|
||||
|
||||
text = text.replace(/href="css/g, "href=\"https://static.openreplay.com/tracker-assist/css")
|
||||
doc.open();
|
||||
doc.write(text);
|
||||
doc.close();
|
||||
|
||||
|
||||
this.vLocal = doc.getElementById("video-local") as HTMLVideoElement;
|
||||
this.vRemote = doc.getElementById("video-remote") as HTMLVideoElement;
|
||||
|
||||
//
|
||||
this.vLocal.parentElement && this.vLocal.parentElement.classList.add("d-none");
|
||||
|
||||
this.audioBtn = doc.getElementById("audio-btn") as HTMLAnchorElement;
|
||||
this.audioBtn.onclick = () => this.toggleAudio();
|
||||
this.videoBtn = doc.getElementById("video-btn") as HTMLAnchorElement;
|
||||
this.videoBtn.onclick = () => this.toggleVideo();
|
||||
|
||||
this.userNameSpan = doc.getElementById("username") as HTMLSpanElement;
|
||||
this.vPlaceholder = doc.querySelector("#remote-stream p")
|
||||
this._trySetAssistentName();
|
||||
this._trySetStreams();
|
||||
|
||||
const endCallBtn = doc.getElementById("end-call-btn") as HTMLAnchorElement;
|
||||
endCallBtn.onclick = endCall;
|
||||
|
||||
const tsText = doc.getElementById("time-stamp");
|
||||
const startTs = Date.now();
|
||||
if (tsText) {
|
||||
this.tsInterval = setInterval(() => {
|
||||
const ellapsed = Date.now() - startTs;
|
||||
const secsFull = ~~(ellapsed / 1000);
|
||||
const mins = ~~(secsFull / 60);
|
||||
const secs = secsFull - mins * 60
|
||||
tsText.innerText = `${mins}:${secs < 10 ? 0 : ''}${secs}`;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// TODO: better D'n'D
|
||||
doc.body.setAttribute("draggable", "true");
|
||||
doc.body.ondragstart = (e) => {
|
||||
if (!e.dataTransfer || !e.target) { return; }
|
||||
//@ts-ignore
|
||||
if (!e.target.classList || !e.target.classList.contains("card-header")) { return; }
|
||||
e.dataTransfer.setDragImage(doc.body, e.clientX, e.clientY);
|
||||
};
|
||||
doc.body.ondragend = e => {
|
||||
Object.assign(iframe.style, {
|
||||
left: `${e.clientX}px`, // TODO: fix in case e is inside the iframe
|
||||
top: `${e.clientY}px`,
|
||||
bottom: 'auto',
|
||||
right: 'auto',
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: load(): Promise
|
||||
|
||||
private aRemote: HTMLAudioElement | null = null;
|
||||
private localStream: MediaStream | null = null;
|
||||
private remoteStream: MediaStream | null = null;
|
||||
|
|
@ -109,7 +110,11 @@ export default class CallWindow {
|
|||
private _trySetStreams() {
|
||||
if (this.vRemote && !this.vRemote.srcObject && this.remoteStream) {
|
||||
this.vRemote.srcObject = this.remoteStream;
|
||||
// Hack for audio (doesen't work in iframe because of some magical reasons)
|
||||
|
||||
if (this.vPlaceholder) {
|
||||
this.vPlaceholder.innerText = "Video has been paused. Click anywhere to resume.";
|
||||
}
|
||||
// Hack for audio (doesen't work in iframe because of some magical reasons (check if it is connected to autoplay?))
|
||||
this.aRemote = document.createElement("audio");
|
||||
this.aRemote.autoplay = true;
|
||||
this.aRemote.style.display = "none"
|
||||
|
|
@ -133,6 +138,10 @@ export default class CallWindow {
|
|||
this._trySetStreams();
|
||||
}
|
||||
|
||||
playRemote() {
|
||||
this.vRemote && this.vRemote.play()
|
||||
}
|
||||
|
||||
|
||||
// TODO: determined workflow
|
||||
_trySetAssistentName() {
|
||||
|
|
|
|||
|
|
@ -67,26 +67,32 @@ export default class ConfirmWindow {
|
|||
this.wrapper = wrapper;
|
||||
|
||||
answerBtn.onclick = () => {
|
||||
this.remove();
|
||||
this.callback(true);
|
||||
this._remove();
|
||||
this.resolve(true);
|
||||
}
|
||||
declineBtn.onclick = () => {
|
||||
this.remove();
|
||||
this.callback(false);
|
||||
this._remove();
|
||||
this.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
mount() {
|
||||
private resolve: (result: boolean) => void = ()=>{};
|
||||
private reject: ()=>void = ()=>{};
|
||||
|
||||
mount(): Promise<boolean> {
|
||||
document.body.appendChild(this.wrapper);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
});
|
||||
}
|
||||
|
||||
private callback: (result: boolean) => void = ()=>{};
|
||||
onAnswer(callback: (result: boolean) => void) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
remove() {
|
||||
private _remove() {
|
||||
if (!this.wrapper.parentElement) { return; }
|
||||
document.body.removeChild(this.wrapper);
|
||||
}
|
||||
remove() {
|
||||
this._remove();
|
||||
this.reject();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
8
tracker/tracker-assist/src/_slim.ts
Normal file
8
tracker/tracker-assist/src/_slim.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
/**
|
||||
* Hach for the issue of peerjs compilation on angular
|
||||
* Mor info here: https://github.com/peers/peerjs/issues/552
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
window.parcelRequire = window.parcelRequire || undefined;
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import './_slim';
|
||||
import Peer, { MediaConnection } from 'peerjs';
|
||||
import type { DataConnection } from 'peerjs';
|
||||
import { App, Messages } from '@openreplay/tracker';
|
||||
|
|
@ -11,6 +12,7 @@ import ConfirmWindow from './ConfirmWindow';
|
|||
export interface Options {
|
||||
confirmText: string,
|
||||
confirmStyle: Object, // Styles object
|
||||
session_calling_peer_key: string,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -25,6 +27,7 @@ export default function(opts: Partial<Options> = {}) {
|
|||
{
|
||||
confirmText: "You have a call. Do you want to answer?",
|
||||
confirmStyle: {},
|
||||
session_calling_peer_key: "__openreplay_calling_peer",
|
||||
},
|
||||
opts,
|
||||
);
|
||||
|
|
@ -104,25 +107,42 @@ export default function(opts: Partial<Options> = {}) {
|
|||
return;
|
||||
}
|
||||
|
||||
function setCallingState(newState: CallingState) {
|
||||
if (newState === CallingState.True) {
|
||||
sessionStorage.setItem(options.session_calling_peer_key, call.peer);
|
||||
} else if (newState === CallingState.False) {
|
||||
sessionStorage.removeItem(options.session_calling_peer_key);
|
||||
}
|
||||
callingState = newState;
|
||||
}
|
||||
|
||||
const notifyCallEnd = () => {
|
||||
dataConn.open && dataConn.send("call_end");
|
||||
}
|
||||
|
||||
callingState = CallingState.Requesting;
|
||||
const confirm = new ConfirmWindow(options.confirmText, options.confirmStyle);
|
||||
dataConn.on('data', (data) => { // if call closed by a caller before confirm
|
||||
if (data === "call_end") {
|
||||
//console.log('OpenReplay tracker-assist: receiving callend onconfirm')
|
||||
callingState = CallingState.False;
|
||||
confirm.remove();
|
||||
}
|
||||
});
|
||||
confirm.mount();
|
||||
confirm.onAnswer(agreed => {
|
||||
|
||||
let confirmAnswer: Promise<boolean>
|
||||
const peerOnCall = sessionStorage.getItem(options.session_calling_peer_key)
|
||||
if (peerOnCall === call.peer) {
|
||||
confirmAnswer = Promise.resolve(true)
|
||||
} else {
|
||||
setCallingState(CallingState.Requesting);
|
||||
const confirm = new ConfirmWindow(options.confirmText, options.confirmStyle);
|
||||
confirmAnswer = confirm.mount();
|
||||
dataConn.on('data', (data) => { // if call closed by a caller before confirm
|
||||
if (data === "call_end") {
|
||||
//console.log('OpenReplay tracker-assist: receiving callend onconfirm')
|
||||
setCallingState(CallingState.False);
|
||||
confirm.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
confirmAnswer.then(agreed => {
|
||||
if (!agreed || !dataConn.open) {
|
||||
call.close();
|
||||
notifyCallEnd();
|
||||
callingState = CallingState.False;
|
||||
setCallingState(CallingState.False);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -131,11 +151,10 @@ export default function(opts: Partial<Options> = {}) {
|
|||
|
||||
const onCallConnect = lStream => {
|
||||
const onCallEnd = () => {
|
||||
//console.log("on callend", call.open)
|
||||
mouse.remove();
|
||||
callUI?.remove();
|
||||
lStream.getTracks().forEach(t => t.stop());
|
||||
callingState = CallingState.False;
|
||||
setCallingState(CallingState.False);
|
||||
}
|
||||
const initiateCallEnd = () => {
|
||||
//console.log("callend initiated")
|
||||
|
|
@ -145,6 +164,7 @@ export default function(opts: Partial<Options> = {}) {
|
|||
}
|
||||
|
||||
call.answer(lStream);
|
||||
setCallingState(CallingState.True)
|
||||
|
||||
dataConn.on("close", onCallEnd);
|
||||
|
||||
|
|
@ -176,6 +196,11 @@ export default function(opts: Partial<Options> = {}) {
|
|||
});
|
||||
call.on('stream', function(rStream) {
|
||||
callUI.setRemoteStream(rStream);
|
||||
const onInteraction = () => {
|
||||
callUI.playRemote()
|
||||
document.removeEventListener("click", onInteraction)
|
||||
}
|
||||
document.addEventListener("click", onInteraction)
|
||||
});
|
||||
dataConn.on('data', (data: any) => {
|
||||
if (data === "call_end") {
|
||||
|
|
@ -200,7 +225,7 @@ export default function(opts: Partial<Options> = {}) {
|
|||
.then(onCallConnect)
|
||||
.catch(e => console.log("OpenReplay tracker-assist: cant reach media devices. ", e));
|
||||
});
|
||||
});
|
||||
}).catch(); // in case of Confirm.remove() without any confirmation
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue