Merge pull request #165 from openreplay/dev

hotfix v1.3.0
This commit is contained in:
Kraiem Taha Yassine 2021-08-25 12:43:04 +01:00 committed by GitHub
commit 079e3add5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 142 additions and 210 deletions

View file

@ -22,13 +22,6 @@ app = Blueprint(__name__)
_overrides.chalice_app(app)
@app.route('/signedups', methods=['GET'], authorizer=None)
def signed_ups():
return {
'data': tenants.get_tenants()
}
@app.route('/login', methods=['POST'], authorizer=None)
def login():
data = app.current_request.json_body
@ -52,7 +45,7 @@ def login():
c = tenants.get_by_tenant_id(tenant_id)
c.pop("createdAt")
c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True,
stack_integrations=True)
stack_integrations=True, version=True)
c["smtp"] = helper.has_smtp()
return {
'jwt': r.pop('jwt'),
@ -83,7 +76,7 @@ def get_account(context):
@app.route('/projects', methods=['GET'])
def get_projects(context):
return {"data": projects.get_projects(tenant_id=context["tenantId"], recording_state=True, gdpr=True, recorded=True,
stack_integrations=True)}
stack_integrations=True, version=True)}
@app.route('/projects', methods=['POST', 'PUT'])
@ -127,7 +120,7 @@ def get_client(context):
if r is not None:
r.pop("createdAt")
r["projects"] = projects.get_projects(tenant_id=context['tenantId'], recording_state=True, recorded=True,
stack_integrations=True)
stack_integrations=True, version=True)
return {
'data': r
}
@ -148,7 +141,7 @@ def put_client(context):
@app.route('/signup', methods=['GET'], authorizer=None)
def get_all_signup():
return {"data": signup.get_signed_ups()}
return {"data": tenants.tenants_exists()}
@app.route('/signup', methods=['POST', 'PUT'], authorizer=None)

View file

@ -41,7 +41,7 @@ def __create(tenant_id, name):
@dev.timed
def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False):
def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False,version=False):
with pg_client.PostgresClient() as cur:
cur.execute(f"""\
SELECT
@ -49,6 +49,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st
{',s.gdpr' if gdpr else ''}
{',COALESCE((SELECT TRUE FROM public.sessions WHERE sessions.project_id = s.project_id LIMIT 1), FALSE) AS recorded' if recorded else ''}
{',stack_integrations.count>0 AS stack_integrations' if stack_integrations else ''}
{',(SELECT tracker_version FROM public.sessions WHERE sessions.project_id = s.project_id ORDER BY start_ts DESC LIMIT 1) AS tracker_version' if version else ''}
FROM public.projects AS s
{'LEFT JOIN LATERAL (SELECT COUNT(*) AS count FROM public.integrations WHERE s.project_id = integrations.project_id LIMIT 1) AS stack_integrations ON TRUE' if stack_integrations else ''}
where s.deleted_at IS NULL

View file

@ -1,39 +1,31 @@
from chalicelib.utils import helper
from chalicelib.utils import pg_client
from chalicelib.core import users, telemetry
from chalicelib.core import users, telemetry, tenants
from chalicelib.utils import captcha
import json
from chalicelib.utils.TimeUTC import TimeUTC
from chalicelib.utils.helper import environ
def get_signed_ups():
with pg_client.PostgresClient() as cur:
cur.execute("SELECT tenant_id, name FROM public.tenants;")
rows = cur.fetchall()
return helper.list_to_camel_case(rows)
def create_step1(data):
print(f"===================== SIGNUP STEP 1 AT {TimeUTC.to_human_readable(TimeUTC.now())} UTC")
errors = []
if tenants.tenants_exists():
return {"errors": ["tenants already registered"]}
email = data.get("email")
print(f"=====================> {email}")
password = data.get("password")
print("Verifying email validity")
email_exists = False
if email is None or len(email) < 5 or not helper.is_valid_email(email):
errors.append("Invalid email address.")
else:
print("Verifying email existance")
if users.email_exists(email):
# errors.append("Email address already in use.")
email_exists = True
errors.append("Email address already in use.")
if users.get_deleted_user_by_email(email) is not None:
# errors.append("Email address previously deleted.")
email_exists = True
errors.append("Email address previously deleted.")
print("Verifying captcha")
if helper.allow_captcha() and not captcha.is_valid(data["g-recaptcha-response"]):
@ -57,12 +49,6 @@ def create_step1(data):
project_name = data.get("projectName")
if project_name is None or len(project_name) < 1:
project_name = "my first project"
signed_ups = get_signed_ups()
if len(signed_ups) > 0 and data.get("tenantId") is None:
errors.append("Tenant already exists, please select it from dropdown")
elif len(signed_ups) == 0 and data.get("tenantId") is not None \
or len(signed_ups) > 0 and data.get("tenantId") not in [t['tenantId'] for t in signed_ups]:
errors.append("Tenant not found")
if len(errors) > 0:
print("==> error")
@ -77,41 +63,7 @@ def create_step1(data):
"organizationName": company_name,
"versionNumber": environ["version_number"]
}
if data.get("tenantId") is not None:
update_user = """
u AS (
UPDATE public.users
SET name = %(fullname)s, deleted_at=NULL
WHERE email=%(email)s
RETURNING user_id,email, role, name
)
UPDATE public.basic_authentication
SET password= crypt(%(password)s, gen_salt('bf', 12))
WHERE user_id = (SELECT user_id FROM u)"""
insert_user = """
a AS (
UPDATE public.users
SET role='admin'
WHERE role ='owner'
),
u AS (
INSERT INTO public.users (email, role, name, data)
VALUES (%(email)s, 'owner', %(fullname)s,%(data)s)
RETURNING user_id,email,role,name
)
INSERT INTO public.basic_authentication (user_id, password, generated_password)
VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE)"""
query = f"""\
WITH t AS (
UPDATE public.tenants
SET name = %(organizationName)s,
version_number = %(versionNumber)s
RETURNING api_key
),
{update_user if email_exists else insert_user}
RETURNING (SELECT api_key FROM t) AS api_key,(SELECT project_id FROM projects LIMIT 1) AS project_id;"""
else:
query = f"""\
query = f"""\
WITH t AS (
INSERT INTO public.tenants (name, version_number, edition)
VALUES (%(organizationName)s, %(versionNumber)s, 'fos')

View file

@ -77,7 +77,7 @@ def update(tenant_id, user_id, data):
return edit_client(tenant_id=tenant_id, changes=changes)
def get_tenants():
def tenants_exists():
with pg_client.PostgresClient() as cur:
cur.execute(f"SELECT name FROM public.tenants")
return helper.list_to_camel_case(cur.fetchall())
cur.execute(f"SELECT EXISTS(SELECT 1 FROM public.tenants)")
return cur.fetchone()["exists"]

View file

@ -22,13 +22,6 @@ app = Blueprint(__name__)
_overrides.chalice_app(app)
@app.route('/signedups', methods=['GET'], authorizer=None)
def signed_ups():
return {
'data': tenants.get_tenants()
}
@app.route('/login', methods=['POST'], authorizer=None)
def login():
data = app.current_request.json_body
@ -54,7 +47,7 @@ def login():
c = tenants.get_by_tenant_id(tenant_id)
c.pop("createdAt")
c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True,
stack_integrations=True)
stack_integrations=True, version=True)
return {
'jwt': r.pop('jwt'),
'data': {
@ -85,7 +78,7 @@ def get_account(context):
@app.route('/projects', methods=['GET'])
def get_projects(context):
return {"data": projects.get_projects(tenant_id=context["tenantId"], recording_state=True, gdpr=True, recorded=True,
stack_integrations=True)}
stack_integrations=True, version=True)}
@app.route('/projects', methods=['POST', 'PUT'])
@ -129,7 +122,7 @@ def get_client(context):
if r is not None:
r.pop("createdAt")
r["projects"] = projects.get_projects(tenant_id=context['tenantId'], recording_state=True, recorded=True,
stack_integrations=True)
stack_integrations=True, version=True)
return {
'data': r
}
@ -148,10 +141,9 @@ def put_client(context):
return tenants.update(tenant_id=context["tenantId"], user_id=context["userId"], data=data)
# TODO: delete this for production; it is used for dev only
@app.route('/signup', methods=['GET'], authorizer=None)
def get_all_signup():
return {"data": signup.get_signed_ups()}
return {"data": tenants.tenants_exists()}
@app.route('/signup', methods=['POST', 'PUT'], authorizer=None)
@ -391,7 +383,8 @@ def change_password_by_invitation():
if user["expiredChange"]:
return {"errors": ["expired change, please re-use the invitation link"]}
return users.set_password_invitation(new_password=data["password"], user_id=user["userId"])
return users.set_password_invitation(new_password=data["password"], user_id=user["userId"],
tenant_id=user["tenantId"])
@app.route('/client/members/{memberId}', methods=['PUT', 'POST'])

View file

@ -40,7 +40,7 @@ def __create(tenant_id, name):
return get_project(tenant_id=tenant_id, project_id=project_id, include_gdpr=True)
def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False):
def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False, version=False):
with pg_client.PostgresClient() as cur:
cur.execute(
cur.mogrify(f"""\
@ -49,6 +49,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st
{',s.gdpr' if gdpr else ''}
{',COALESCE((SELECT TRUE FROM public.sessions WHERE sessions.project_id = s.project_id LIMIT 1), FALSE) AS recorded' if recorded else ''}
{',stack_integrations.count>0 AS stack_integrations' if stack_integrations else ''}
{',(SELECT tracker_version FROM public.sessions WHERE sessions.project_id = s.project_id ORDER BY start_ts DESC LIMIT 1) AS tracker_version' if version else ''}
FROM public.projects AS s
{'LEFT JOIN LATERAL (SELECT COUNT(*) AS count FROM public.integrations WHERE s.project_id = integrations.project_id LIMIT 1) AS stack_integrations ON TRUE' if stack_integrations else ''}
where s.tenant_id =%(tenant_id)s

View file

@ -1,22 +1,17 @@
from chalicelib.utils import helper
from chalicelib.utils import pg_client
from chalicelib.core import users, telemetry
from chalicelib.core import users, telemetry, tenants
from chalicelib.utils import captcha
import json
from chalicelib.utils.TimeUTC import TimeUTC
from chalicelib.utils.helper import environ
def get_signed_ups():
with pg_client.PostgresClient() as cur:
cur.execute("SELECT tenant_id, name FROM public.tenants;")
rows = cur.fetchall()
return helper.list_to_camel_case(rows)
def create_step1(data):
print(f"===================== SIGNUP STEP 1 AT {TimeUTC.to_human_readable(TimeUTC.now())} UTC")
errors = []
if tenants.tenants_exists():
return {"errors":["tenants already registered"]}
email = data.get("email")
print(f"=====================> {email}")
@ -54,64 +49,37 @@ def create_step1(data):
project_name = data.get("projectName")
if project_name is None or len(project_name) < 1:
project_name = "my first project"
signed_ups = get_signed_ups()
if len(signed_ups) == 0 and data.get("tenantId") is not None \
or len(signed_ups) > 0 and data.get("tenantId") is not None\
and data.get("tenantId") not in [t['tenantId'] for t in signed_ups]:
errors.append("Tenant not found")
if len(errors) > 0:
print("==> error")
print(errors)
return {"errors": errors}
print("No errors detected")
print("Decomposed infos")
tenant_id = data.get("tenantId")
params = {"email": email, "password": password,
"fullname": fullname, "companyName": company_name,
"projectName": project_name,
"versionNumber": environ["version_number"],
"data": json.dumps({"lastAnnouncementView": TimeUTC.now()})}
if tenant_id is not None:
query = """\
WITH t AS (
UPDATE public.tenants
SET name = %(companyName)s,
version_number = %(versionNumber)s
WHERE tenant_id=%(tenant_id)s
query = """\
WITH t AS (
INSERT INTO public.tenants (name, version_number, edition)
VALUES (%(companyName)s, %(versionNumber)s, 'ee')
RETURNING tenant_id, api_key
),
u AS (
UPDATE public.users
SET email = %(email)s,
name = %(fullname)s,
WHERE role ='owner' AND tenant_id=%(tenant_id)s
RETURNING user_id,email, role, name
)
UPDATE public.basic_authentication
SET password= crypt(%(password)s, gen_salt('bf', 12))
WHERE user_id = (SELECT user_id FROM u)
RETURNING %(tenant_id)s AS tenant_id"""
else:
query = """\
WITH t AS (
INSERT INTO public.tenants (name, version_number, edition)
VALUES (%(companyName)s, %(versionNumber)s, 'ee')
RETURNING tenant_id, api_key
),
u AS (
INSERT INTO public.users (tenant_id, email, role, name, data)
VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s,%(data)s)
RETURNING user_id,email,role,name
),
au AS (
INSERT INTO public.basic_authentication (user_id, password, generated_password)
VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE)
)
INSERT INTO public.projects (tenant_id, name, active)
VALUES ((SELECT t.tenant_id FROM t), %(projectName)s, TRUE)
RETURNING tenant_id,project_id, (SELECT api_key FROM t) AS api_key;"""
),
u AS (
INSERT INTO public.users (tenant_id, email, role, name, data)
VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s,%(data)s)
RETURNING user_id,email,role,name
),
au AS (
INSERT INTO public.basic_authentication (user_id, password, generated_password)
VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE)
)
INSERT INTO public.projects (tenant_id, name, active)
VALUES ((SELECT t.tenant_id FROM t), %(projectName)s, TRUE)
RETURNING tenant_id,project_id, (SELECT api_key FROM t) AS api_key;"""
with pg_client.PostgresClient() as cur:
cur.execute(cur.mogrify(query, params))

View file

@ -101,7 +101,7 @@ def update(tenant_id, user_id, data):
return edit_client(tenant_id=tenant_id, changes=changes)
def get_tenants():
def tenants_exists():
with pg_client.PostgresClient() as cur:
cur.execute(f"SELECT name FROM public.tenants")
return helper.list_to_camel_case(cur.fetchall())
cur.execute(f"SELECT EXISTS(SELECT 1 FROM public.tenants)")
return cur.fetchone()["exists"]

View file

@ -440,11 +440,11 @@ def change_password(tenant_id, user_id, email, old_password, new_password):
"jwt": authenticate(email, new_password)["jwt"]}
def set_password_invitation(user_id, new_password):
def set_password_invitation(tenant_id, user_id, new_password):
changes = {"password": new_password, "generatedPassword": False,
"invitationToken": None, "invitedAt": None,
"changePwdExpireAt": None, "changePwdToken": None}
user = update(tenant_id=-1, user_id=user_id, changes=changes)
user = update(tenant_id=tenant_id, user_id=user_id, changes=changes)
r = authenticate(user['email'], new_password)
tenant_id = r.pop("tenantId")

View file

@ -53,21 +53,16 @@ export default class PlayerBlockHeader extends React.PureComponent {
);
backHandler = () => {
const { funnelRef, history, siteId } = this.props;
const { history, siteId } = this.props;
if (history.action !== 'POP')
history.goBack();
else
history.push(withSiteId(SESSIONS_ROUTE), siteId);
// if (funnelRef) {
// history.push(withSiteId(funnelIssueRoute(funnelRef.funnelId, funnelRef.issueId), funnelRef.siteId));
// } else {
// history.push(withSiteId(SESSIONS_ROUTE), siteId);
// }
history.push(withSiteId(SESSIONS_ROUTE), siteId);
}
toggleFavorite = () => {
const { session } = this.props;
this.props.toggleFavorite(session);
this.props.toggleFavorite(session.sessionId);
}
render() {
@ -123,8 +118,7 @@ export default class PlayerBlockHeader extends React.PureComponent {
tooltip="Bookmark"
onClick={ this.toggleFavorite }
loading={ loading }
icon={ favorite ? 'star-solid' : 'star' }
// label={ favorite ? 'Favourited' : 'Favourite' }
icon={ favorite ? 'star-solid' : 'star' }
plain
/>
<SharePopup
@ -135,8 +129,7 @@ export default class PlayerBlockHeader extends React.PureComponent {
className="mr-2"
tooltip="Share Session"
disabled={ disabled }
icon={ 'share-alt' }
//label="Share"
icon={ 'share-alt' }
plain
/>
}

View file

@ -0,0 +1,26 @@
import React, { useState } from 'react'
import stl from './bookmark.css'
import { Icon } from 'UI'
import { toggleFavorite } from 'Duck/sessions'
import { connect } from 'react-redux'
// import Session from 'Types/session';
interface Props {
toggleFavorite: (session) => void,
favorite: Boolean,
sessionId: any
}
function Bookmark({ toggleFavorite, sessionId, favorite } : Props ) {
return (
<div
className={ stl.favoriteWrapper }
onClick={ () => toggleFavorite(sessionId) }
data-favourite={ favorite }
>
<Icon name={ favorite ? 'star-solid' : 'star' } size="20" />
</div>
)
}
export default connect(null, { toggleFavorite })(Bookmark)

View file

@ -0,0 +1,14 @@
.favoriteWrapper {
cursor: pointer;
display: flex;
align-items: center;
/* opacity: 0; */
margin: 0 15px;
&[data-favourite=true] {
opacity: 1;
& svg {
fill: $teal;
}
}
}

View file

@ -0,0 +1 @@
export { default } from './Bookmark'

View file

@ -15,7 +15,7 @@ import { session as sessionRoute } from 'App/routes';
import { durationFormatted, formatTimeOrDate } from 'App/date';
import stl from './sessionItem.css';
import LiveTag from 'Shared/LiveTag';
import { session } from '../../../routes';
import Bookmark from 'Shared/Bookmark';
import Counter from './Counter'
const Label = ({ label = '', color = 'color-gray-medium'}) => (
@ -25,15 +25,6 @@ const Label = ({ label = '', color = 'color-gray-medium'}) => (
timezone: state.getIn(['sessions', 'timezone'])
}), { toggleFavorite })
export default class SessionItem extends React.PureComponent {
state = { favouriting: false };
toggleFavorite = () => {
this.setState({ favouriting: true });
this.props.toggleFavorite(this.props.session).then(() => {
this.setState({ favouriting: false });
});
}
// eslint-disable-next-line complexity
render() {
const {
@ -114,14 +105,8 @@ export default class SessionItem extends React.PureComponent {
{ live && <LiveTag isLive={true} /> }
<div className={ cn(stl.iconDetails, 'px-4') }>
<div
className={ stl.favoriteWrapper }
onClick={ this.toggleFavorite }
data-favourite={ favorite }
>
<Icon name={ favorite ? 'star-solid' : 'star' } size="20" />
</div>
<div className={ cn(stl.iconDetails, stl.favorite, 'px-4') } data-favourite={favorite} >
<Bookmark sessionId={sessionId} favorite={favorite} />
</div>
<div className={ stl.playLink } id="play-button" data-viewed={ viewed }>

View file

@ -20,13 +20,20 @@
align-items: center;
border: solid thin #EEEEEE;
& .favorite {
opacity: 0;
&[data-favourite=true] {
opacity: 1;
}
}
&:hover {
& .playLink {
transition: all 0.4s;
opacity: 1;
}
& .favoriteWrapper {
& .favorite {
transition: all 0.4s;
opacity: 1;
}
@ -81,21 +88,6 @@
}
}
.favoriteWrapper {
cursor: pointer;
display: flex;
align-items: center;
opacity: 0;
margin: 0 15px;
&[data-favourite=true] {
opacity: 1;
& svg {
fill: $teal;
}
}
}
.playLink {
display: flex;
align-items: center;

View file

@ -120,9 +120,10 @@ const reducer = (state = initialState, action = {}) => {
return state
.set('list', list)
.set('sessionIds', list.map(({ sessionId }) => sessionId ).toJS())
.set('favoriteList', list.filter(({ favorite }) => favorite))
.set('total', total)
.set('keyMap', keyMap)
.set('wdTypeCount', wdTypeCount);
.set('wdTypeCount', wdTypeCount);
case SET_AUTOPLAY_VALUES: {
const sessionIds = state.get('sessionIds')
const currentSessionId = state.get('current').sessionId
@ -179,13 +180,15 @@ const reducer = (state = initialState, action = {}) => {
.set('visitedEvents', visitedEvents)
.set('host', visitedEvents[0] && visitedEvents[0].host);
}
case FETCH_FAVORITE_LIST.SUCCESS:
case FETCH_FAVORITE_LIST.SUCCESS:
return state
.set('favoriteList', List(action.data).map(Session));
case TOGGLE_FAVORITE.SUCCESS: {
const id = action.session.sessionId;
const id = action.sessionId;
const session = state.get('list').find(({sessionId}) => sessionId === id)
const wasInFavorite = state
.get('favoriteList').findIndex(({ sessionId }) => sessionId === id) > -1;
return state
.update('list', list => list
.map(session => (session.sessionId === id
@ -193,7 +196,7 @@ const reducer = (state = initialState, action = {}) => {
: session)))
.update('favoriteList', list => (wasInFavorite
? list.filter(({ sessionId }) => sessionId !== id)
: list.push(action.session.set('favorite', true))))
: list.push(session.set('favorite', true))))
.update('current', session => (session.sessionId === id
? session.set('favorite', !wasInFavorite)
: session));
@ -283,11 +286,11 @@ export const fetch = (sessionId) => (dispatch, getState) => {
});
}
export function toggleFavorite(session) {
export function toggleFavorite(sessionId) {
return {
types: TOGGLE_FAVORITE.toArray(),
call: client => client.get(`/sessions2/${ session.sessionId }/favorite`),
session,
call: client => client.get(`/sessions2/${ sessionId }/favorite`),
sessionId,
};
}

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker-assist",
"description": "Tracker plugin for screen assistance through the WebRTC",
"version": "3.0.2",
"version": "3.0.3",
"keywords": [
"WebRTC",
"assistance",

View file

@ -101,15 +101,22 @@ export default class CallWindow {
}
private aRemote: HTMLAudioElement | null = null;
private localStream: MediaStream | null = null;
private remoteStream: MediaStream | null = null;
private setLocalVideoStream: (MediaStream) => void = () => {};
private videoRequested: boolean = true; // TODO: green camera light
private _trySetStreams() {
if (this.vRemote && this.remoteStream) {
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)
this.aRemote = document.createElement("audio");
this.aRemote.autoplay = true;
this.aRemote.style.display = "none"
this.aRemote.srcObject = this.remoteStream;
document.body.appendChild(this.aRemote)
}
if (this.vLocal && this.localStream) {
if (this.vLocal && !this.vLocal.srcObject && this.localStream) {
this.vLocal.srcObject = this.localStream;
}
}
@ -195,6 +202,9 @@ export default class CallWindow {
if (this.iframe.parentElement) {
document.body.removeChild(this.iframe);
}
if (this.aRemote && this.aRemote.parentElement) {
document.body.removeChild(this.aRemote);
}
}
}

View file

@ -164,20 +164,20 @@ export default function(opts: Partial<Options> = {}) {
});
call.on('stream', function(rStream) {
callUI.setRemoteStream(rStream);
dataConn.on('data', (data: any) => {
if (data === "call_end") {
//console.log('receiving callend on call')
onCallEnd();
return;
}
if (data && typeof data.name === 'string') {
//console.log("name",data)
callUI.setAssistentName(data.name);
}
if (data && typeof data.x === 'number' && typeof data.y === 'number') {
mouse.move(data);
}
});
});
dataConn.on('data', (data: any) => {
if (data === "call_end") {
//console.log('receiving callend on call')
onCallEnd();
return;
}
if (data && typeof data.name === 'string') {
//console.log("name",data)
callUI.setAssistentName(data.name);
}
if (data && typeof data.x === 'number' && typeof data.y === 'number') {
mouse.move(data);
}
});
}