diff --git a/tracker/tracker-assist/README.md b/tracker/tracker-assist/README.md index 0c7bfe00f..4897477b3 100644 --- a/tracker/tracker-assist/README.md +++ b/tracker/tracker-assist/README.md @@ -1,86 +1,131 @@ -# OpenReplay Tracker Assist plugin +# OpenReplay Assist -Tracker plugin for WebRTC video support at your site. +OpenReplay Assist Plugin allows you to support your users by seeing their live screen and instantly hopping on call (WebRTC) with them without requiring any 3rd-party screen sharing software. ## Installation ```bash npm i @openreplay/tracker-assist ``` +OR +```bash +yarn add @openreplay/tracker-assist +``` ## Usage -Initialize the `@openreplay/tracker` package as usual and load the plugin into it. +### With NPM + +Initialize the tracker then load the `@openreplay/tracker-assist` plugin. + +#### If your website is a Single Page Application (SPA) ```js import Tracker from '@openreplay/tracker'; import trackerAssist from '@openreplay/tracker-assist'; const tracker = new Tracker({ - projectKey: YOUR_PROJECT_KEY, + projectKey: PROJECT_KEY, }); +tracker.use(trackerAssist(options)); // check the list of available options below + tracker.start(); -tracker.use(trackerAssist()); ``` -Options: +#### If your web app is Server-Side-Rendered (SSR) -```ts -{ - confirmText: string, - confirmStyle: Object, - config: RTCConfiguration, - onAgentConnect: () => (()=>void | void), - onCallStart: () => (()=>void | void), +Follow the below example if your app is SSR. Ensure `tracker.start()` is called once the app is started (in `useEffect` or `componentDidMount`). + +```js +import OpenReplay from '@openreplay/tracker/cjs'; +import trackerFetch from '@openreplay/tracker-assist/cjs'; + +const tracker = new OpenReplay({ + projectKey: PROJECT_KEY +}); +tracker.use(trackerAssist(options)); // check the list of available options below + +//... +function MyApp() { + useEffect(() => { // use componentDidMount in case of React Class Component + tracker.start(); + }, []) +//... } ``` -Use `confirmText` option to specify a text in the call confirmation popup. -You can specify its styles as well with `confirmStyle` style object. -```ts -{ - background: "#555" - color: "orange" -} +#### Options +```js +trackerAssist({ + callConfirm?: string|ConfirmOptions; + controlConfirm?: string|ConfirmOptions; + config?: object; + onAgentConnect?: () => (()=>void | void); + onCallStart?: () => (()=>void | void); + onRemoteControlStart?: () => (()=>void | void); +}) ``` -It is possible to pass `config` RTCConfiguration object in order to configure TURN server or other parameters. -```ts -config: { - iceServers: [{ - urls: "stun:stun.services.mozilla.com", - username: "louis@mozilla.com", - credential: "webrtcdemo" - }, { - urls: ["stun:stun.example.com", "stun:stun-1.example.com"] - }] +```js +type ConfirmOptions = { + text?:string, + style?: StyleObject, // style object (i.e {color: 'red', borderRadius: '10px'}) + confirmBtn?: ButtonOptions, + declineBtn?: ButtonOptions } +type ButtonOptions = HTMLButtonElement | string | { + innerHTML?: string, // to pass an svg string or text + style?: StyleObject, // style object (i.e {color: 'red', borderRadius: '10px'}) +} ``` -You can pass `onAgentConnect` callback. It will be called when someone from OpenReplay UI connects to the current live session. It can return another function. In this case, returned callback will be called when the same agent connection gets closed. -```ts -onAgentConnect: () => { - console.log("Hello!") - const onAgentDisconnect = () => console.log("Bye!") +- `callConfirm`: Customize the text and/or layout of the call request popup. +- `controlConfirm`: Customize the text and/or layout of the remote control request popup. +- `config`: Contains any custom ICE/TURN server configuration. Defaults to `{ 'iceServers': [{ 'urls': 'stun:stun.l.google.com:19302' }], 'sdpSemantics': 'unified-plan' }`. +- `onAgentConnect: () => (()=>void | void)`: This callback function is fired when someone from OpenReplay UI connects to the current live session. It can return another function. In this case, returned callback will be called when the same agent connection gets closed. + +```js +onAgentConnect = () => { + console.log("Live session started") + const onAgentDisconnect = () => console.log("Live session stopped") return onAgentDisconnect } - ``` -Warning: it is possible for the same agent to be connected/disconnected several times during one session due to a bad network. Several agents may connect simultaneously. +- `onCallStart: () => (()=>void | void)`: This callback function is fired as soon as a call (webRTC) starts. It can also return `onCallEnd` which will be called when the call ends. In case of an unstable connection, this may be called several times. Below is an example: -A callback `onCallStart` will be fired when the end-user accepts the call. It can return another callback that will be called on the call end. -```ts +```js onCallStart: () => { - console.log("Allo!") - const onCallEnd = () => console.log("short beeps...") + console.log("Call started") + const onCallEnd = () => console.log("Call ended") return onCallEnd } - ``` +- `onRemoteControlStart: () => (()=>void | void)`: This callback function is fired as soon as a remote control session starts. It can also return `onRemoteControlEnd` which will be called when the remote control permissions are revoked. Below is an example: +```js +onCallStart: () => { + console.log("Remote control started") + const onCallEnd = () => console.log("Remote control ended") + return onCallEnd +} +``` +## Troubleshooting + +### Critical dependency: the request of a dependency is an expression + +Please apply this [workaround](https://github.com/peers/peerjs/issues/630#issuecomment-910028230) if you face the below error when compiling: + +```log +Failed to compile. + +./node_modules/peerjs/dist/peerjs.min.js +Critical dependency: the request of a dependency is an expression +``` + +If you encounter any other issue, please connect to our [Slack](https://slack.openreplay.com) and get help from our community. diff --git a/tracker/tracker-assist/package.json b/tracker/tracker-assist/package.json index 437a86499..980082681 100644 --- a/tracker/tracker-assist/package.json +++ b/tracker/tracker-assist/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-assist", "description": "Tracker plugin for screen assistance through the WebRTC", - "version": "3.5.9", + "version": "3.5.10 ", "keywords": [ "WebRTC", "assistance", diff --git a/tracker/tracker-assist/src/Assist.ts b/tracker/tracker-assist/src/Assist.ts index ab64ab9b2..f4dff48c5 100644 --- a/tracker/tracker-assist/src/Assist.ts +++ b/tracker/tracker-assist/src/Assist.ts @@ -8,8 +8,9 @@ import RequestLocalStream from './LocalStream.js'; import RemoteControl from './RemoteControl.js'; import CallWindow from './CallWindow.js'; import AnnotationCanvas from './AnnotationCanvas.js'; -import ConfirmWindow, { callConfirmDefault, controlConfirmDefault } from './ConfirmWindow.js'; -import type { Options as ConfirmOptions } from './ConfirmWindow.js'; +import ConfirmWindow from './ConfirmWindow/ConfirmWindow.js'; +import { callConfirmDefault } from './ConfirmWindow/defaults.js'; +import type { Options as ConfirmOptions } from './ConfirmWindow/defaults.js'; // TODO: fully specified strict check (everywhere) @@ -231,7 +232,7 @@ export default class Assist { peerOptions['config'] = this.options.config } const peer = this.peer = new Peer(peerID, peerOptions); - app.debug.log('Peer created: ', peer) + // app.debug.log('Peer created: ', peer) peer.on('error', e => app.debug.warn("Peer error: ", e.type, e)) peer.on('disconnect', () => peer.reconnect()) peer.on('call', (call) => { diff --git a/tracker/tracker-assist/src/ConfirmWindow.ts b/tracker/tracker-assist/src/ConfirmWindow/ConfirmWindow.ts similarity index 68% rename from tracker/tracker-assist/src/ConfirmWindow.ts rename to tracker/tracker-assist/src/ConfirmWindow/ConfirmWindow.ts index 6cfabbca9..02d9dd9c6 100644 --- a/tracker/tracker-assist/src/ConfirmWindow.ts +++ b/tracker/tracker-assist/src/ConfirmWindow/ConfirmWindow.ts @@ -1,12 +1,6 @@ import type { Properties } from 'csstype'; -import { declineCall, acceptCall, cross, remoteControl } from './icons.js' - -const TEXT_GRANT_REMORTE_ACCESS = "Grant Remote Access"; -const TEXT_REJECT = "Reject"; -const TEXT_ANSWER_CALL = `${acceptCall}   Answer`; - -type ButtonOptions = +export type ButtonOptions = | HTMLButtonElement | string | { @@ -15,48 +9,15 @@ type ButtonOptions = }; // TODO: common strategy for InputOptions/defaultOptions merging -interface ConfirmWindowOptions { +export interface ConfirmWindowOptions { text: string; style?: Properties; confirmBtn: ButtonOptions; declineBtn: ButtonOptions; } -export type Options = string | Partial; -function confirmDefault( - opts: Options, - confirmBtn: ButtonOptions, - declineBtn: ButtonOptions, - text: string -): ConfirmWindowOptions { - const isStr = typeof opts === "string"; - return Object.assign( - { - text: isStr ? opts : text, - confirmBtn, - declineBtn - }, - isStr ? undefined : opts - ); -} - -export const callConfirmDefault = (opts: Options) => - confirmDefault( - opts, - TEXT_ANSWER_CALL, - TEXT_REJECT, - "You have an incoming call. Do you want to answer?" - ); -export const controlConfirmDefault = (opts: Options) => - confirmDefault( - opts, - TEXT_GRANT_REMORTE_ACCESS, - TEXT_REJECT, - "Allow remote control?" - ); - -function makeButton(options: ButtonOptions): HTMLButtonElement { +function makeButton(options: ButtonOptions, defaultStyle?: Properties): HTMLButtonElement { if (options instanceof HTMLButtonElement) { return options; } @@ -71,7 +32,7 @@ function makeButton(options: ButtonOptions): HTMLButtonElement { alignItems: "center", textTransform: "uppercase", marginRight: "10px" - }); + }, defaultStyle); if (typeof options === "string") { btn.innerHTML = options; } else { @@ -90,22 +51,19 @@ export default class ConfirmWindow { const p = document.createElement("p"); p.innerText = options.text; const buttons = document.createElement("div"); - const confirmBtn = makeButton(options.confirmBtn); - const declineBtn = makeButton(options.declineBtn); - buttons.appendChild(confirmBtn); - buttons.appendChild(declineBtn); - popup.appendChild(p); - popup.appendChild(buttons); - - Object.assign(confirmBtn.style, { + const confirmBtn = makeButton(options.confirmBtn, { background: "rgba(0, 167, 47, 1)", color: "white" - }); - - Object.assign(declineBtn.style, { + }) + const declineBtn = makeButton(options.declineBtn, { background: "#FFE9E9", color: "#CC0000" - }); + }) + buttons.appendChild(confirmBtn) + buttons.appendChild(declineBtn) + popup.appendChild(p) + popup.appendChild(buttons) + Object.assign(buttons.style, { marginTop: "10px", diff --git a/tracker/tracker-assist/src/ConfirmWindow/defaults.ts b/tracker/tracker-assist/src/ConfirmWindow/defaults.ts new file mode 100644 index 000000000..8f84cbe89 --- /dev/null +++ b/tracker/tracker-assist/src/ConfirmWindow/defaults.ts @@ -0,0 +1,42 @@ +import { declineCall, acceptCall, cross, remoteControl } from '../icons.js' +import type { ButtonOptions, ConfirmWindowOptions } from './ConfirmWindow.js' + + +const TEXT_GRANT_REMORTE_ACCESS = "Grant Remote Control"; +const TEXT_REJECT = "Reject"; +const TEXT_ANSWER_CALL = `${acceptCall}   Answer`; + +export type Options = string | Partial; + +function confirmDefault( + opts: Options, + confirmBtn: ButtonOptions, + declineBtn: ButtonOptions, + text: string +): ConfirmWindowOptions { + const isStr = typeof opts === "string"; + return Object.assign( + { + text: isStr ? opts : text, + confirmBtn, + declineBtn + }, + isStr ? undefined : opts + ); +} + +export const callConfirmDefault = (opts: Options) => + confirmDefault( + opts, + TEXT_ANSWER_CALL, + TEXT_REJECT, + "You have an incoming call. Do you want to answer?" + ) + +export const controlConfirmDefault = (opts: Options) => + confirmDefault( + opts, + TEXT_GRANT_REMORTE_ACCESS, + TEXT_REJECT, + "Agent requested remote control. Allow?" + ) diff --git a/tracker/tracker-assist/src/RemoteControl.ts b/tracker/tracker-assist/src/RemoteControl.ts index 02137d99f..d2903f9c4 100644 --- a/tracker/tracker-assist/src/RemoteControl.ts +++ b/tracker/tracker-assist/src/RemoteControl.ts @@ -1,6 +1,7 @@ import Mouse from './Mouse.js'; -import ConfirmWindow, { controlConfirmDefault } from './ConfirmWindow.js'; -import type { Options as AssistOptions } from './Assist' +import ConfirmWindow from './ConfirmWindow/ConfirmWindow.js'; +import { controlConfirmDefault } from './ConfirmWindow/defaults.js'; +import type { Options as AssistOptions } from './Assist'; enum RCStatus { Disabled,