feat (frontend): re-call peer on remote page reload

This commit is contained in:
ShiKhu 2021-09-23 18:04:26 +02:00
parent 7b5d21a220
commit 2ebdd3d67f
3 changed files with 80 additions and 154 deletions

View file

@ -10,6 +10,19 @@ import { CallingState, ConnectionStatus } from 'Player/MessageDistributor/manage
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import stl from './AassistActions.css' import stl from './AassistActions.css'
function onClose(stream) {
stream.getTracks().forEach(t=>t.stop());
}
function onReject() {
toast.info(`Call was rejected.`);
}
function onError(e) {
toast.error(e);
}
interface Props { interface Props {
userId: String, userId: String,
toggleChatWindow: (state) => void, toggleChatWindow: (state) => void,
@ -32,18 +45,6 @@ function AssistActions({ toggleChatWindow, userId, calling, peerConnectionStatus
} }
}, [peerConnectionStatus]) }, [peerConnectionStatus])
function onClose(stream) {
stream.getTracks().forEach(t=>t.stop());
}
function onReject() {
toast.info(`Call was rejected.`);
}
function onError(e) {
toast.error(e);
}
function onCallConnect(lStream) { function onCallConnect(lStream) {
setLocalStream(lStream); setLocalStream(lStream);
setEndCall(() => callPeer( setEndCall(() => callPeer(

View file

@ -10,6 +10,7 @@ import { update, getState } from '../../store';
export enum CallingState { export enum CallingState {
Reconnecting,
Requesting, Requesting,
True, True,
False, False,
@ -38,7 +39,7 @@ export function getStatusText(status: ConnectionStatus): string {
case ConnectionStatus.Error: case ConnectionStatus.Error:
return "Something went wrong. Try to reload the page."; return "Something went wrong. Try to reload the page.";
case ConnectionStatus.WaitingMessages: case ConnectionStatus.WaitingMessages:
return "Connected. Waiting for the data..." return "Connected. Waiting for the data... (The tab might be inactive)"
} }
} }
@ -187,6 +188,13 @@ export default class AssistManager {
const conn = this.peer.connect(id, { serialization: 'json', reliable: true}); const conn = this.peer.connect(id, { serialization: 'json', reliable: true});
conn.on('open', () => { conn.on('open', () => {
window.addEventListener("beforeunload", ()=>conn.open &&conn.send("unload")); window.addEventListener("beforeunload", ()=>conn.open &&conn.send("unload"));
//console.log("peer connected")
if (getState().calling === CallingState.Reconnecting) {
this._call()
}
let i = 0; let i = 0;
let firstMessage = true; let firstMessage = true;
@ -195,7 +203,7 @@ export default class AssistManager {
conn.on('data', (data) => { conn.on('data', (data) => {
if (!Array.isArray(data)) { return this.handleCommand(data); } if (!Array.isArray(data)) { return this.handleCommand(data); }
this.mesagesRecieved = true; this.disconnectTimeout && clearTimeout(this.disconnectTimeout);
if (firstMessage) { if (firstMessage) {
firstMessage = false; firstMessage = false;
this.setStatus(ConnectionStatus.Connected) this.setStatus(ConnectionStatus.Connected)
@ -246,8 +254,8 @@ export default class AssistManager {
const onDataClose = () => { const onDataClose = () => {
this.initiateCallEnd(); this.onCallDisconnect()
this.setStatus(ConnectionStatus.Connecting); //console.log('closed peer conn. Reconnecting...')
this.connectToPeer(); this.connectToPeer();
} }
@ -276,8 +284,6 @@ export default class AssistManager {
} }
private onCallEnd: null | (()=>void) = null;
private onReject: null | (()=>void) = null;
private forceCallEnd() { private forceCallEnd() {
this.callConnection?.close(); this.callConnection?.close();
} }
@ -290,33 +296,37 @@ export default class AssistManager {
private initiateCallEnd = () => { private initiateCallEnd = () => {
this.forceCallEnd(); this.forceCallEnd();
this.notifyCallEnd(); this.notifyCallEnd();
this.onCallEnd?.(); this.localCallData && this.localCallData.onCallEnd();
} }
private onTrackerCallEnd = () => { private onTrackerCallEnd = () => {
console.log('onTrackerCallEnd')
this.forceCallEnd(); this.forceCallEnd();
if (getState().calling === CallingState.Requesting) { if (getState().calling === CallingState.Requesting) {
this.onReject?.(); this.localCallData && this.localCallData.onReject();
}
this.localCallData && this.localCallData.onCallEnd();
}
private onCallDisconnect = () => {
if (getState().calling === CallingState.True) {
update({ calling: CallingState.Reconnecting });
} }
this.onCallEnd?.();
} }
private disconnectTimeout: ReturnType<typeof setTimeout> | undefined;
private mesagesRecieved: boolean = false;
private handleCommand(command: string) { private handleCommand(command: string) {
console.log("Data command", command)
switch (command) { switch (command) {
case "unload": case "unload":
this.onTrackerCallEnd(); //this.onTrackerCallEnd();
this.mesagesRecieved = false; this.onCallDisconnect()
setTimeout(() => { this.dataConnection?.close();
if (this.mesagesRecieved) { this.disconnectTimeout = setTimeout(() => {
return; this.onTrackerCallEnd();
}
// @ts-ignore
this.dataConnection?.close();
this.setStatus(ConnectionStatus.Disconnected); this.setStatus(ConnectionStatus.Disconnected);
}, 8000); // TODO: more convenient way }, 15000); // TODO: more convenient way
//this.dataConnection?.close(); //this.dataConnection?.close();
return; return;
case "call_end": case "call_end":
@ -337,60 +347,67 @@ export default class AssistManager {
conn.send({ x: Math.round(data.x), y: Math.round(data.y) }); conn.send({ x: Math.round(data.x), y: Math.round(data.y) });
} }
private localCallData: {
localStream: MediaStream,
onStream: (s: MediaStream)=>void,
onCallEnd: () => void,
onReject: () => void,
onError?: ()=> void
} | null = null
call(localStream: MediaStream, onStream: (s: MediaStream)=>void, onCallEnd: () => void, onReject: () => void, onError?: ()=> void): null | Function { call(localStream: MediaStream, onStream: (s: MediaStream)=>void, onCallEnd: () => void, onReject: () => void, onError?: ()=> void): null | Function {
if (!this.peer || getState().calling !== CallingState.False) { return null; } this.localCallData = {
localStream,
onStream,
onCallEnd: () => {
onCallEnd();
this.md.overlay.removeEventListener("mousemove", this.onMouseMove);
update({ calling: CallingState.False });
this.localCallData = null;
},
onReject,
onError,
}
this._call()
return this.initiateCallEnd;
}
private _call() {
if (!this.peer || !this.localCallData || ![CallingState.False, CallingState.Reconnecting].includes(getState().calling)) { return null; }
update({ calling: CallingState.Requesting }); update({ calling: CallingState.Requesting });
const call = this.peer.call(this.peerID, localStream);
call.on('stream', stream => {
//call.peerConnection.ontrack = (t)=> console.log('ontrack', t)
//console.log('calling...', this.localCallData.localStream)
const call = this.peer.call(this.peerID, this.localCallData.localStream);
call.on('stream', stream => {
update({ calling: CallingState.True }); update({ calling: CallingState.True });
onStream(stream); this.localCallData && this.localCallData.onStream(stream);
this.send({ this.send({
name: store.getState().getIn([ 'user', 'account', 'name']), name: store.getState().getIn([ 'user', 'account', 'name']),
}); });
// @ts-ignore ??
this.md.overlay.addEventListener("mousemove", this.onMouseMove) this.md.overlay.addEventListener("mousemove", this.onMouseMove)
}); });
this.onCallEnd = () => { call.on("close", this.localCallData.onCallEnd);
onCallEnd();
// @ts-ignore ??
this.md.overlay.removeEventListener("mousemove", this.onMouseMove);
update({ calling: CallingState.False });
this.onCallEnd = null;
}
call.on("close", this.onCallEnd);
call.on("error", (e) => { call.on("error", (e) => {
console.error("PeerJS error (on call):", e) console.error("PeerJS error (on call):", e)
this.initiateCallEnd?.(); this.initiateCallEnd();
onError?.(); this.localCallData && this.localCallData.onError && this.localCallData.onError();
}); });
// const intervalID = setInterval(() => {
// if (!call.open && getState().calling === CallingState.True) {
// this.onCallEnd?.();
// clearInterval(intervalID);
// }
// }, 5000);
window.addEventListener("beforeunload", this.initiateCallEnd) window.addEventListener("beforeunload", this.initiateCallEnd)
return this.initiateCallEnd;
} }
clear() { clear() {
this.initiateCallEnd(); this.initiateCallEnd();
this.dataCheckIntervalID && clearInterval(this.dataCheckIntervalID); this.dataCheckIntervalID && clearInterval(this.dataCheckIntervalID);
if (this.peer) { if (this.peer) {
this.peer.connections[this.peerID]?.forEach(c => c.open && c.close()); //console.log("destroying peer...")
this.peer.disconnect(); const peer = this.peer; // otherwise it calls reconnection on data chan close
this.peer.destroy();
this.peer = null; this.peer = null;
peer.destroy();
} }
} }
} }

View file

@ -1,92 +0,0 @@
// @flow
import type StatedScreen from '../StatedScreen';
import type { CssInsertRule, CssDeleteRule } from '../messages';
import type { Timed } from '../Timed';
type CSSRuleMessage = CssInsertRule | CssDeleteRule;
type TimedCSSRuleMessage = Timed & CSSRuleMessage;
import logger from 'App/logger';
import ListWalker from './ListWalker';
export default class StylesManager extends ListWalker<TimedCSSRuleMessage> {
#screen: StatedScreen;
_linkLoadingCount: number = 0;
_linkLoadPromises: Array<Promise<void>> = [];
_skipCSSLinks: Array<string> = []; // should be common for all pages
constructor(screen: StatedScreen) {
super();
this.#screen = screen;
}
reset():void {
super.reset();
this._linkLoadingCount = 0;
this._linkLoadPromises = [];
//cancel all promises? tothinkaboutit
}
setStyleHandlers(node: HTMLLinkElement, value: string): void {
let timeoutId;
const promise = new Promise((resolve) => {
if (this._skipCSSLinks.includes(value)) resolve();
this._linkLoadingCount++;
this.#screen.setCSSLoading(true);
const setSkipAndResolve = () => {
this._skipCSSLinks.push(value); // watch out
resolve();
}
timeoutId = setTimeout(setSkipAndResolve, 4000);
node.onload = resolve;
node.onerror = setSkipAndResolve;
}).then(() => {
node.onload = null;
node.onerror = null;
clearTimeout(timeoutId);
this._linkLoadingCount--;
if (this._linkLoadingCount === 0) {
this.#screen.setCSSLoading(false);
}
});
this._linkLoadPromises.push(promise);
}
#manageRule = (msg: CSSRuleMessage):void => {
// if (msg.tp === "css_insert_rule") {
// let styleSheet = this.#screen.document.styleSheets[ msg.stylesheetID ];
// if (!styleSheet) {
// logger.log("No stylesheet with corresponding ID found: ", msg)
// styleSheet = this.#screen.document.styleSheets[0];
// if (!styleSheet) {
// return;
// }
// }
// try {
// styleSheet.insertRule(msg.rule, msg.index);
// } catch (e) {
// logger.log(e, msg)
// //const index = Math.min(msg.index, styleSheet.cssRules.length);
// styleSheet.insertRule(msg.rule, styleSheet.cssRules.length);
// //styleSheet.ownerNode.innerHTML += msg.rule;
// }
// }
// if (msg.tp === "css_delete_rule") {
// // console.warn('Warning: STYLESHEET_DELETE_RULE msg')
// const styleSheet = this.#screen.document.styleSheets[msg.stylesheetID];
// if (!styleSheet) {
// logger.log("No stylesheet with corresponding ID found: ", msg)
// return;
// }
// styleSheet.deleteRule(msg.index);
// }
}
moveReady(t: number): Promise<void> {
return Promise.all(this._linkLoadPromises)
.then(() => this.moveApply(t, this.#manageRule));
}
}