callix-dialer-widget 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/WIDGET-README.md +34 -0
- package/dist-widget/callix-dialer.css +1 -1
- package/dist-widget/callix-dialer.es.js +1059 -43
- package/dist-widget/callix-dialer.es.js.map +1 -1
- package/dist-widget/callix-dialer.umd.js +4 -4
- package/dist-widget/callix-dialer.umd.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
|
|
|
6
6
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
7
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
8
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
-
import React, {
|
|
9
|
+
import React, { createContext, useMemo, useSyncExternalStore, useRef, useCallback, useContext, useEffect, useState, forwardRef, useLayoutEffect, useId, useImperativeHandle, StrictMode } from "react";
|
|
10
10
|
import require$$0$2, { createPortal } from "react-dom";
|
|
11
11
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
12
12
|
function getAugmentedNamespace(n) {
|
|
@@ -43725,6 +43725,790 @@ function requireClientSdkReact() {
|
|
|
43725
43725
|
return clientSdkReact;
|
|
43726
43726
|
}
|
|
43727
43727
|
var clientSdkReactExports = requireClientSdkReact();
|
|
43728
|
+
class EngineSignal {
|
|
43729
|
+
constructor(value) {
|
|
43730
|
+
this.value = value;
|
|
43731
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
43732
|
+
}
|
|
43733
|
+
get() {
|
|
43734
|
+
return this.value;
|
|
43735
|
+
}
|
|
43736
|
+
set(value) {
|
|
43737
|
+
if (JSON.stringify(value) === JSON.stringify(this.value)) return;
|
|
43738
|
+
this.value = value;
|
|
43739
|
+
for (const listener of this.listeners) listener(value);
|
|
43740
|
+
}
|
|
43741
|
+
subscribe(fn) {
|
|
43742
|
+
this.listeners.add(fn);
|
|
43743
|
+
return () => this.listeners.delete(fn);
|
|
43744
|
+
}
|
|
43745
|
+
}
|
|
43746
|
+
const INITIAL_TRANSCRIPT = { segments: [], status: null, insight: null, interims: 0 };
|
|
43747
|
+
const MAX_SEGMENTS = 300;
|
|
43748
|
+
const RECONNECT_MIN_MS = 500;
|
|
43749
|
+
const RECONNECT_MAX_MS = 5e3;
|
|
43750
|
+
function engineWsUrl(baseUrl) {
|
|
43751
|
+
const url = new URL(baseUrl, typeof window !== "undefined" ? window.location.href : "http://localhost");
|
|
43752
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
43753
|
+
url.pathname = `${url.pathname.replace(/\/+$/, "")}/ws`;
|
|
43754
|
+
url.search = "";
|
|
43755
|
+
url.hash = "";
|
|
43756
|
+
return url.toString();
|
|
43757
|
+
}
|
|
43758
|
+
class EngineClient {
|
|
43759
|
+
constructor(options) {
|
|
43760
|
+
this.options = options;
|
|
43761
|
+
this.userSignal = new EngineSignal(void 0);
|
|
43762
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
43763
|
+
this.sessionLostListeners = /* @__PURE__ */ new Set();
|
|
43764
|
+
this.viewWaiters = [];
|
|
43765
|
+
this.ws = null;
|
|
43766
|
+
this.reconnectDelay = RECONNECT_MIN_MS;
|
|
43767
|
+
this.reconnectTimer = null;
|
|
43768
|
+
this.closed = false;
|
|
43769
|
+
this.iceServers = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
43770
|
+
this.pc = null;
|
|
43771
|
+
this.connectionId = null;
|
|
43772
|
+
this.remoteSet = false;
|
|
43773
|
+
this.pendingCandidates = [];
|
|
43774
|
+
this.micStream = null;
|
|
43775
|
+
this.audioSeq = 0;
|
|
43776
|
+
this.unlockListener = null;
|
|
43777
|
+
this.agentId = options.agentId;
|
|
43778
|
+
this.session = options.session ?? null;
|
|
43779
|
+
this.log = options.log ?? (() => void 0);
|
|
43780
|
+
this.audio = options.audioElement !== void 0 ? options.audioElement : createAudioElement();
|
|
43781
|
+
this.state = {
|
|
43782
|
+
wsState: "idle",
|
|
43783
|
+
tabId: null,
|
|
43784
|
+
view: null,
|
|
43785
|
+
engaged: false,
|
|
43786
|
+
pcState: "idle",
|
|
43787
|
+
needsUnlock: false,
|
|
43788
|
+
micError: null,
|
|
43789
|
+
lastError: null,
|
|
43790
|
+
inputMuted: false,
|
|
43791
|
+
outputMuted: false,
|
|
43792
|
+
outputVolume: 100,
|
|
43793
|
+
transcript: INITIAL_TRANSCRIPT,
|
|
43794
|
+
sessionLost: null
|
|
43795
|
+
};
|
|
43796
|
+
}
|
|
43797
|
+
// ------------------------------------------------------------------ store
|
|
43798
|
+
getState() {
|
|
43799
|
+
return this.state;
|
|
43800
|
+
}
|
|
43801
|
+
subscribe(listener) {
|
|
43802
|
+
this.listeners.add(listener);
|
|
43803
|
+
return () => {
|
|
43804
|
+
this.listeners.delete(listener);
|
|
43805
|
+
};
|
|
43806
|
+
}
|
|
43807
|
+
setState(patch) {
|
|
43808
|
+
this.state = { ...this.state, ...patch };
|
|
43809
|
+
for (const listener of this.listeners) listener();
|
|
43810
|
+
}
|
|
43811
|
+
get currentCall() {
|
|
43812
|
+
var _a2, _b;
|
|
43813
|
+
return ((_b = (_a2 = this.state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.call) ?? null;
|
|
43814
|
+
}
|
|
43815
|
+
// ------------------------------------------------------------------ conexão
|
|
43816
|
+
connect() {
|
|
43817
|
+
if (this.closed) this.closed = false;
|
|
43818
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) return;
|
|
43819
|
+
const Impl = this.options.WebSocketImpl ?? WebSocket;
|
|
43820
|
+
this.setState({ wsState: "connecting" });
|
|
43821
|
+
const ws = new Impl(engineWsUrl(this.options.url));
|
|
43822
|
+
this.ws = ws;
|
|
43823
|
+
ws.onopen = () => {
|
|
43824
|
+
if (this.ws !== ws) return;
|
|
43825
|
+
this.reconnectDelay = RECONNECT_MIN_MS;
|
|
43826
|
+
this.setState({ wsState: "open" });
|
|
43827
|
+
const hello = { type: "hello", role: "tab", agentId: this.agentId, token: this.options.token, label: this.options.label ?? "Discador", ...this.session ? { session: this.session } : {} };
|
|
43828
|
+
this.send(hello);
|
|
43829
|
+
this.log("conectado ao host do motor");
|
|
43830
|
+
};
|
|
43831
|
+
ws.onmessage = (event) => {
|
|
43832
|
+
if (this.ws !== ws) return;
|
|
43833
|
+
let message;
|
|
43834
|
+
try {
|
|
43835
|
+
message = JSON.parse(String(event.data));
|
|
43836
|
+
} catch {
|
|
43837
|
+
return;
|
|
43838
|
+
}
|
|
43839
|
+
this.handle(message);
|
|
43840
|
+
};
|
|
43841
|
+
ws.onclose = () => {
|
|
43842
|
+
if (this.ws !== ws) return;
|
|
43843
|
+
this.ws = null;
|
|
43844
|
+
const wasEngaged = this.state.engaged;
|
|
43845
|
+
this.setState({ wsState: "closed", tabId: null, engaged: false });
|
|
43846
|
+
if (wasEngaged) this.stopAudio();
|
|
43847
|
+
if (this.closed) return;
|
|
43848
|
+
this.log(`conexão com o motor caiu; nova tentativa em ${this.reconnectDelay} ms`);
|
|
43849
|
+
this.reconnectTimer = setTimeout(() => {
|
|
43850
|
+
this.reconnectTimer = null;
|
|
43851
|
+
this.connect();
|
|
43852
|
+
}, this.reconnectDelay);
|
|
43853
|
+
this.reconnectDelay = Math.min(this.reconnectDelay * 2, RECONNECT_MAX_MS);
|
|
43854
|
+
};
|
|
43855
|
+
ws.onerror = () => void 0;
|
|
43856
|
+
}
|
|
43857
|
+
/** fecha de vez (sem reconectar) e solta o áudio */
|
|
43858
|
+
close() {
|
|
43859
|
+
this.closed = true;
|
|
43860
|
+
if (this.reconnectTimer) {
|
|
43861
|
+
clearTimeout(this.reconnectTimer);
|
|
43862
|
+
this.reconnectTimer = null;
|
|
43863
|
+
}
|
|
43864
|
+
this.stopAudio();
|
|
43865
|
+
const ws = this.ws;
|
|
43866
|
+
this.ws = null;
|
|
43867
|
+
if (ws) {
|
|
43868
|
+
ws.onclose = null;
|
|
43869
|
+
ws.close();
|
|
43870
|
+
}
|
|
43871
|
+
for (const waiter of this.viewWaiters.splice(0)) clearTimeout(waiter.timer);
|
|
43872
|
+
this.setState({ wsState: "closed", tabId: null, engaged: false });
|
|
43873
|
+
}
|
|
43874
|
+
/** a view atual, ou a próxima que o host mandar (welcome/view) */
|
|
43875
|
+
waitForView(timeoutMs = 15e3) {
|
|
43876
|
+
if (this.state.view && this.state.tabId) return Promise.resolve(this.state.view);
|
|
43877
|
+
return new Promise((resolve, reject) => {
|
|
43878
|
+
const timer = setTimeout(() => {
|
|
43879
|
+
const index = this.viewWaiters.findIndex((w) => w.timer === timer);
|
|
43880
|
+
if (index >= 0) this.viewWaiters.splice(index, 1);
|
|
43881
|
+
reject(new Error("o motor não respondeu"));
|
|
43882
|
+
}, timeoutMs);
|
|
43883
|
+
this.viewWaiters.push({ resolve, timer });
|
|
43884
|
+
});
|
|
43885
|
+
}
|
|
43886
|
+
send(message) {
|
|
43887
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(message));
|
|
43888
|
+
}
|
|
43889
|
+
handle(message) {
|
|
43890
|
+
switch (message.type) {
|
|
43891
|
+
case "welcome": {
|
|
43892
|
+
if (Array.isArray(message.iceServers) && message.iceServers.length > 0) this.iceServers = message.iceServers;
|
|
43893
|
+
this.setState({ tabId: message.tabId });
|
|
43894
|
+
this.applyView(message.view);
|
|
43895
|
+
this.log(`sou a aba ${message.tabId}`);
|
|
43896
|
+
if (this.options.autoEngage !== false && message.view.engagedTabId !== message.tabId) this.engage();
|
|
43897
|
+
break;
|
|
43898
|
+
}
|
|
43899
|
+
case "view":
|
|
43900
|
+
this.applyView(message.view);
|
|
43901
|
+
break;
|
|
43902
|
+
case "engage":
|
|
43903
|
+
this.setState({ engaged: message.engaged });
|
|
43904
|
+
if (message.engaged) void this.startAudio();
|
|
43905
|
+
else this.stopAudio();
|
|
43906
|
+
break;
|
|
43907
|
+
case "signal":
|
|
43908
|
+
void this.onSignal(message.payload);
|
|
43909
|
+
break;
|
|
43910
|
+
case "error":
|
|
43911
|
+
this.setState({ lastError: message.message });
|
|
43912
|
+
this.log(`erro do host: ${message.message}`);
|
|
43913
|
+
break;
|
|
43914
|
+
case "transcript":
|
|
43915
|
+
this.pushSegment(message.segment);
|
|
43916
|
+
break;
|
|
43917
|
+
case "transcript-sync":
|
|
43918
|
+
this.setState({ transcript: { segments: message.segments ?? [], status: message.status ?? null, insight: message.insight ?? null, interims: 0 } });
|
|
43919
|
+
break;
|
|
43920
|
+
case "insight": {
|
|
43921
|
+
const current = this.state.transcript.insight;
|
|
43922
|
+
if (!current || message.insight.revision >= current.revision || message.insight.callId !== current.callId) {
|
|
43923
|
+
this.setState({ transcript: { ...this.state.transcript, insight: message.insight } });
|
|
43924
|
+
}
|
|
43925
|
+
break;
|
|
43926
|
+
}
|
|
43927
|
+
case "transcript-status":
|
|
43928
|
+
this.onTranscriptStatus(message.status);
|
|
43929
|
+
break;
|
|
43930
|
+
}
|
|
43931
|
+
}
|
|
43932
|
+
applyView(view) {
|
|
43933
|
+
var _a2, _b;
|
|
43934
|
+
const previous = this.state.view;
|
|
43935
|
+
const lost = view.sessionLost ?? null;
|
|
43936
|
+
const newlyLost = Boolean(lost) && !(previous == null ? void 0 : previous.sessionLost);
|
|
43937
|
+
if (newlyLost) {
|
|
43938
|
+
this.session = null;
|
|
43939
|
+
this.log(`o Callix derrubou a sessão: ${(lost == null ? void 0 : lost.message) ?? ""}`);
|
|
43940
|
+
}
|
|
43941
|
+
const user = (_a2 = view.snapshot) == null ? void 0 : _a2.leg.user;
|
|
43942
|
+
this.userSignal.set(user ? { ...user, sessionId: 0 } : void 0);
|
|
43943
|
+
const call = (_b = view.snapshot) == null ? void 0 : _b.leg.call;
|
|
43944
|
+
const patch = { view, sessionLost: lost ? { originator: lost.originator, message: lost.message } : null };
|
|
43945
|
+
if (call && typeof call.muted === "boolean" && call.muted !== this.state.inputMuted && call.state !== "ended") patch.inputMuted = call.muted;
|
|
43946
|
+
this.setState(patch);
|
|
43947
|
+
for (const waiter of this.viewWaiters.splice(0)) {
|
|
43948
|
+
clearTimeout(waiter.timer);
|
|
43949
|
+
waiter.resolve(view);
|
|
43950
|
+
}
|
|
43951
|
+
if (newlyLost && lost) for (const listener of this.sessionLostListeners) listener(lost);
|
|
43952
|
+
}
|
|
43953
|
+
onSessionLost(listener) {
|
|
43954
|
+
this.sessionLostListeners.add(listener);
|
|
43955
|
+
return () => {
|
|
43956
|
+
this.sessionLostListeners.delete(listener);
|
|
43957
|
+
};
|
|
43958
|
+
}
|
|
43959
|
+
// ------------------------------------------------------------------ sessão e comandos
|
|
43960
|
+
/** modo sdk: o widget fez login no back-end e entrega a sessão ao host */
|
|
43961
|
+
setSession(session) {
|
|
43962
|
+
this.session = session;
|
|
43963
|
+
this.send({ type: "session", session });
|
|
43964
|
+
}
|
|
43965
|
+
engage() {
|
|
43966
|
+
this.send({ type: "engage" });
|
|
43967
|
+
}
|
|
43968
|
+
release() {
|
|
43969
|
+
this.send({ type: "release" });
|
|
43970
|
+
}
|
|
43971
|
+
command(command) {
|
|
43972
|
+
this.send({ type: "command", command });
|
|
43973
|
+
this.log(`comando: ${command.name}${command.phone ? ` ${command.phone}` : ""}`);
|
|
43974
|
+
}
|
|
43975
|
+
// ------------------------------------------------------------------ áudio
|
|
43976
|
+
setInputMuted(muted) {
|
|
43977
|
+
if (this.micStream) for (const track of this.micStream.getAudioTracks()) track.enabled = !muted;
|
|
43978
|
+
this.setState({ inputMuted: muted });
|
|
43979
|
+
if (this.currentCall) this.command({ name: muted ? "mute" : "unmute" });
|
|
43980
|
+
}
|
|
43981
|
+
setOutputMuted(muted) {
|
|
43982
|
+
if (this.audio) this.audio.muted = muted;
|
|
43983
|
+
this.setState({ outputMuted: muted });
|
|
43984
|
+
}
|
|
43985
|
+
setOutputVolume(volume) {
|
|
43986
|
+
const clamped = Math.max(0, Math.min(100, Math.round(volume)));
|
|
43987
|
+
if (this.audio) this.audio.volume = clamped / 100;
|
|
43988
|
+
this.setState({ outputVolume: clamped });
|
|
43989
|
+
}
|
|
43990
|
+
async setOutputDevice(deviceId) {
|
|
43991
|
+
const audio = this.audio;
|
|
43992
|
+
if (!(audio == null ? void 0 : audio.setSinkId)) throw new Error("este navegador não permite escolher a saída de som");
|
|
43993
|
+
await audio.setSinkId(deviceId);
|
|
43994
|
+
}
|
|
43995
|
+
/** depois de um toque do agente: tenta tocar o som do motor que o navegador segurou */
|
|
43996
|
+
async unlockAudio() {
|
|
43997
|
+
if (!this.audio || !this.audio.srcObject) return;
|
|
43998
|
+
try {
|
|
43999
|
+
await this.audio.play();
|
|
44000
|
+
this.setState({ needsUnlock: false });
|
|
44001
|
+
} catch {
|
|
44002
|
+
this.setState({ needsUnlock: true });
|
|
44003
|
+
}
|
|
44004
|
+
}
|
|
44005
|
+
async startAudio() {
|
|
44006
|
+
const seq = ++this.audioSeq;
|
|
44007
|
+
this.stopAudio();
|
|
44008
|
+
const mediaDevices = this.options.mediaDevices === void 0 ? typeof navigator !== "undefined" ? navigator.mediaDevices : null : this.options.mediaDevices;
|
|
44009
|
+
if (!(mediaDevices == null ? void 0 : mediaDevices.getUserMedia)) {
|
|
44010
|
+
this.setState({ micError: "Este navegador não permite usar o microfone pela página." });
|
|
44011
|
+
return;
|
|
44012
|
+
}
|
|
44013
|
+
let stream;
|
|
44014
|
+
try {
|
|
44015
|
+
stream = await mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } });
|
|
44016
|
+
} catch (error2) {
|
|
44017
|
+
const message = `Microfone indisponível: ${error2.message}`;
|
|
44018
|
+
this.setState({ micError: message });
|
|
44019
|
+
this.log(message);
|
|
44020
|
+
return;
|
|
44021
|
+
}
|
|
44022
|
+
if (seq !== this.audioSeq || !this.state.engaged) {
|
|
44023
|
+
for (const track of stream.getTracks()) track.stop();
|
|
44024
|
+
return;
|
|
44025
|
+
}
|
|
44026
|
+
this.micStream = stream;
|
|
44027
|
+
for (const track of stream.getAudioTracks()) track.enabled = !this.state.inputMuted;
|
|
44028
|
+
this.setState({ micError: null });
|
|
44029
|
+
const Impl = this.options.PeerConnectionImpl ?? RTCPeerConnection;
|
|
44030
|
+
const connection = new Impl({ iceServers: this.iceServers });
|
|
44031
|
+
this.pc = connection;
|
|
44032
|
+
const connectionId = Math.random().toString(36).slice(2, 10);
|
|
44033
|
+
this.connectionId = connectionId;
|
|
44034
|
+
this.remoteSet = false;
|
|
44035
|
+
this.pendingCandidates = [];
|
|
44036
|
+
for (const track of stream.getAudioTracks()) connection.addTrack(track, stream);
|
|
44037
|
+
connection.onicecandidate = (event) => {
|
|
44038
|
+
this.send({ type: "signal", payload: { connectionId, candidate: event.candidate ? event.candidate.toJSON() : null } });
|
|
44039
|
+
};
|
|
44040
|
+
connection.ontrack = (event) => {
|
|
44041
|
+
const remote = event.streams[0] ?? new MediaStream([event.track]);
|
|
44042
|
+
if (this.audio) {
|
|
44043
|
+
this.audio.srcObject = remote;
|
|
44044
|
+
this.audio.muted = this.state.outputMuted;
|
|
44045
|
+
this.audio.volume = this.state.outputVolume / 100;
|
|
44046
|
+
this.audio.play().then(() => this.setState({ needsUnlock: false })).catch(() => {
|
|
44047
|
+
this.setState({ needsUnlock: true });
|
|
44048
|
+
this.armUnlock();
|
|
44049
|
+
});
|
|
44050
|
+
}
|
|
44051
|
+
this.log("áudio do motor chegou");
|
|
44052
|
+
};
|
|
44053
|
+
connection.onconnectionstatechange = () => {
|
|
44054
|
+
if (this.pc !== connection) return;
|
|
44055
|
+
this.setState({ pcState: connection.connectionState });
|
|
44056
|
+
this.log(`WebRTC: ${connection.connectionState}`);
|
|
44057
|
+
if (connection.connectionState === "failed" && this.state.engaged) {
|
|
44058
|
+
this.log("conexão de áudio com o motor falhou; renegociando");
|
|
44059
|
+
void this.startAudio();
|
|
44060
|
+
}
|
|
44061
|
+
};
|
|
44062
|
+
const offer = await connection.createOffer();
|
|
44063
|
+
if (this.pc !== connection) return;
|
|
44064
|
+
await connection.setLocalDescription(offer);
|
|
44065
|
+
this.send({ type: "signal", payload: { connectionId, description: { type: offer.type, sdp: offer.sdp } } });
|
|
44066
|
+
this.setState({ pcState: connection.connectionState });
|
|
44067
|
+
}
|
|
44068
|
+
stopAudio() {
|
|
44069
|
+
if (this.pc) {
|
|
44070
|
+
this.pc.onconnectionstatechange = null;
|
|
44071
|
+
this.pc.onicecandidate = null;
|
|
44072
|
+
this.pc.ontrack = null;
|
|
44073
|
+
this.pc.close();
|
|
44074
|
+
this.pc = null;
|
|
44075
|
+
}
|
|
44076
|
+
this.connectionId = null;
|
|
44077
|
+
if (this.micStream) {
|
|
44078
|
+
for (const track of this.micStream.getTracks()) track.stop();
|
|
44079
|
+
this.micStream = null;
|
|
44080
|
+
}
|
|
44081
|
+
if (this.audio) this.audio.srcObject = null;
|
|
44082
|
+
if (this.state.pcState !== "idle") this.setState({ pcState: "idle" });
|
|
44083
|
+
}
|
|
44084
|
+
async onSignal(payload) {
|
|
44085
|
+
const connection = this.pc;
|
|
44086
|
+
if (!connection) return;
|
|
44087
|
+
if (payload.connectionId && payload.connectionId !== this.connectionId) return;
|
|
44088
|
+
try {
|
|
44089
|
+
if (payload.description) {
|
|
44090
|
+
await connection.setRemoteDescription(payload.description);
|
|
44091
|
+
this.remoteSet = true;
|
|
44092
|
+
for (const candidate of this.pendingCandidates.splice(0)) await connection.addIceCandidate(candidate).catch(() => void 0);
|
|
44093
|
+
}
|
|
44094
|
+
if (payload.candidate !== void 0) {
|
|
44095
|
+
const candidate = payload.candidate ?? void 0;
|
|
44096
|
+
if (!this.remoteSet && candidate) this.pendingCandidates.push(candidate);
|
|
44097
|
+
else await connection.addIceCandidate(candidate).catch(() => void 0);
|
|
44098
|
+
}
|
|
44099
|
+
} catch (error2) {
|
|
44100
|
+
this.log(`sinalização: ${error2.message}`);
|
|
44101
|
+
}
|
|
44102
|
+
}
|
|
44103
|
+
/** o próximo toque/tecla na página libera o som (política de autoplay) */
|
|
44104
|
+
armUnlock() {
|
|
44105
|
+
if (this.unlockListener || typeof document === "undefined") return;
|
|
44106
|
+
const listener = () => {
|
|
44107
|
+
document.removeEventListener("pointerdown", listener, true);
|
|
44108
|
+
document.removeEventListener("keydown", listener, true);
|
|
44109
|
+
this.unlockListener = null;
|
|
44110
|
+
void this.unlockAudio();
|
|
44111
|
+
};
|
|
44112
|
+
this.unlockListener = listener;
|
|
44113
|
+
document.addEventListener("pointerdown", listener, true);
|
|
44114
|
+
document.addEventListener("keydown", listener, true);
|
|
44115
|
+
}
|
|
44116
|
+
// ------------------------------------------------------------------ transcrição
|
|
44117
|
+
pushSegment(segment) {
|
|
44118
|
+
const current = this.state.transcript;
|
|
44119
|
+
let segments = current.segments;
|
|
44120
|
+
let insight = current.insight;
|
|
44121
|
+
let interims = current.interims;
|
|
44122
|
+
if (segments.length && segments[0].callId !== segment.callId) {
|
|
44123
|
+
segments = [];
|
|
44124
|
+
insight = null;
|
|
44125
|
+
interims = 0;
|
|
44126
|
+
}
|
|
44127
|
+
const index = segments.findIndex((s) => s.id === segment.id);
|
|
44128
|
+
const next = index >= 0 ? segments.map((s, i2) => i2 === index ? segment : s) : [...segments, segment];
|
|
44129
|
+
if (next.length > MAX_SEGMENTS) next.shift();
|
|
44130
|
+
if (segment.interim) interims += 1;
|
|
44131
|
+
this.setState({ transcript: { ...current, segments: next, insight, interims } });
|
|
44132
|
+
}
|
|
44133
|
+
onTranscriptStatus(status) {
|
|
44134
|
+
const current = this.state.transcript;
|
|
44135
|
+
if (!current.status || current.status.recordingId === status.recordingId || status.live === "running") {
|
|
44136
|
+
const fresh = current.status && current.status.recordingId !== status.recordingId;
|
|
44137
|
+
this.setState({ transcript: { ...current, status, ...fresh ? { segments: [], insight: null, interims: 0 } : {} } });
|
|
44138
|
+
}
|
|
44139
|
+
}
|
|
44140
|
+
}
|
|
44141
|
+
function createAudioElement() {
|
|
44142
|
+
if (typeof document === "undefined") return null;
|
|
44143
|
+
const audio = document.createElement("audio");
|
|
44144
|
+
audio.autoplay = true;
|
|
44145
|
+
audio.setAttribute("playsinline", "true");
|
|
44146
|
+
audio.style.display = "none";
|
|
44147
|
+
audio.dataset.callixEngineAudio = "true";
|
|
44148
|
+
document.body.appendChild(audio);
|
|
44149
|
+
return audio;
|
|
44150
|
+
}
|
|
44151
|
+
const EngineClientContext = createContext(null);
|
|
44152
|
+
function EngineClientProvider({ client: client2, children: children2 }) {
|
|
44153
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(EngineClientContext.Provider, { value: client2, children: children2 });
|
|
44154
|
+
}
|
|
44155
|
+
function useEngineClient() {
|
|
44156
|
+
return useContext(EngineClientContext);
|
|
44157
|
+
}
|
|
44158
|
+
function useEngineState(client2) {
|
|
44159
|
+
return useSyncExternalStore(client2.subscribe.bind(client2), client2.getState.bind(client2), client2.getState.bind(client2));
|
|
44160
|
+
}
|
|
44161
|
+
const ENGINE_BREAKS = [{ id: 0, name: "Pausa" }];
|
|
44162
|
+
function toCallQualification(q) {
|
|
44163
|
+
return { id: q.id, name: q.name, isSystemQualification: false, systemQualificationType: 0 };
|
|
44164
|
+
}
|
|
44165
|
+
function qualificationsFromView(view, kind) {
|
|
44166
|
+
var _a2;
|
|
44167
|
+
return (((_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.qualifications) ?? []).filter((q) => q.kind === kind).map(toCallQualification);
|
|
44168
|
+
}
|
|
44169
|
+
class EngineCall {
|
|
44170
|
+
constructor(client2, snapshot) {
|
|
44171
|
+
this.client = client2;
|
|
44172
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
44173
|
+
this.snapshot = snapshot;
|
|
44174
|
+
}
|
|
44175
|
+
get id() {
|
|
44176
|
+
return this.snapshot.id;
|
|
44177
|
+
}
|
|
44178
|
+
get direction() {
|
|
44179
|
+
return this.snapshot.direction;
|
|
44180
|
+
}
|
|
44181
|
+
get data() {
|
|
44182
|
+
var _a2;
|
|
44183
|
+
return {
|
|
44184
|
+
destinationNumber: (_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone,
|
|
44185
|
+
campaignContactPhoneId: this.snapshot.campaignContactPhoneId,
|
|
44186
|
+
answeredAt: this.snapshot.answeredAt ? new Date(this.snapshot.answeredAt) : void 0
|
|
44187
|
+
};
|
|
44188
|
+
}
|
|
44189
|
+
get callerNumber() {
|
|
44190
|
+
var _a2;
|
|
44191
|
+
return (_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone;
|
|
44192
|
+
}
|
|
44193
|
+
get peerDisplayName() {
|
|
44194
|
+
var _a2;
|
|
44195
|
+
return (_a2 = this.snapshot.contact) == null ? void 0 : _a2.name;
|
|
44196
|
+
}
|
|
44197
|
+
get isEstablished() {
|
|
44198
|
+
return this.snapshot.state === "active";
|
|
44199
|
+
}
|
|
44200
|
+
get isEnded() {
|
|
44201
|
+
return this.snapshot.state === "ended";
|
|
44202
|
+
}
|
|
44203
|
+
/** segundos desde o atendimento (ou desde o início, enquanto toca) */
|
|
44204
|
+
get currentDurationSeconds() {
|
|
44205
|
+
const from = this.snapshot.answeredAt ?? this.snapshot.startedAt;
|
|
44206
|
+
return Math.max(0, Math.floor((Date.now() - from) / 1e3));
|
|
44207
|
+
}
|
|
44208
|
+
answer() {
|
|
44209
|
+
this.client.command({ name: "answer" });
|
|
44210
|
+
}
|
|
44211
|
+
reject() {
|
|
44212
|
+
this.client.command({ name: "reject" });
|
|
44213
|
+
}
|
|
44214
|
+
hangup() {
|
|
44215
|
+
this.client.command({ name: "hangup" });
|
|
44216
|
+
}
|
|
44217
|
+
isOnHold() {
|
|
44218
|
+
return { local: false, remote: false };
|
|
44219
|
+
}
|
|
44220
|
+
async hold() {
|
|
44221
|
+
}
|
|
44222
|
+
async unhold() {
|
|
44223
|
+
}
|
|
44224
|
+
isMuted() {
|
|
44225
|
+
return this.client.getState().inputMuted;
|
|
44226
|
+
}
|
|
44227
|
+
mute() {
|
|
44228
|
+
this.client.setInputMuted(true);
|
|
44229
|
+
}
|
|
44230
|
+
unmute() {
|
|
44231
|
+
this.client.setInputMuted(false);
|
|
44232
|
+
}
|
|
44233
|
+
sendDtmf(tones) {
|
|
44234
|
+
this.client.command({ name: "dtmf", tones });
|
|
44235
|
+
}
|
|
44236
|
+
on(event, listener) {
|
|
44237
|
+
let set = this.listeners.get(event);
|
|
44238
|
+
if (!set) {
|
|
44239
|
+
set = /* @__PURE__ */ new Set();
|
|
44240
|
+
this.listeners.set(event, set);
|
|
44241
|
+
}
|
|
44242
|
+
set.add(listener);
|
|
44243
|
+
return this;
|
|
44244
|
+
}
|
|
44245
|
+
once(event, listener) {
|
|
44246
|
+
const wrapped = (...args) => {
|
|
44247
|
+
this.off(event, wrapped);
|
|
44248
|
+
listener(...args);
|
|
44249
|
+
};
|
|
44250
|
+
return this.on(event, wrapped);
|
|
44251
|
+
}
|
|
44252
|
+
off(event, listener) {
|
|
44253
|
+
var _a2;
|
|
44254
|
+
(_a2 = this.listeners.get(event)) == null ? void 0 : _a2.delete(listener);
|
|
44255
|
+
return this;
|
|
44256
|
+
}
|
|
44257
|
+
toCallData() {
|
|
44258
|
+
var _a2;
|
|
44259
|
+
return {
|
|
44260
|
+
id: this.snapshot.id,
|
|
44261
|
+
direction: this.snapshot.direction,
|
|
44262
|
+
destinationNumber: ((_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone) ?? "",
|
|
44263
|
+
answeredAt: this.snapshot.answeredAt ? new Date(this.snapshot.answeredAt).toISOString() : void 0,
|
|
44264
|
+
campaignContactPhoneId: this.snapshot.campaignContactPhoneId,
|
|
44265
|
+
isMuted: this.snapshot.muted
|
|
44266
|
+
};
|
|
44267
|
+
}
|
|
44268
|
+
}
|
|
44269
|
+
function operatorStateFromView(view, call) {
|
|
44270
|
+
var _a2, _b, _c;
|
|
44271
|
+
if (!view) return void 0;
|
|
44272
|
+
if (((_a2 = view.sessionLost) == null ? void 0 : _a2.originator) === "anotherDevice") return { state: "startupError", error: "userIsAlreadyConnectedOnAnotherDevice" };
|
|
44273
|
+
const leg = (_b = view.snapshot) == null ? void 0 : _b.leg;
|
|
44274
|
+
if (!leg) {
|
|
44275
|
+
if (view.engine === "starting") return { state: "starting" };
|
|
44276
|
+
if (view.engine === "running") return { state: "starting" };
|
|
44277
|
+
return { state: "offline" };
|
|
44278
|
+
}
|
|
44279
|
+
const asCall = call ?? void 0;
|
|
44280
|
+
switch (leg.status) {
|
|
44281
|
+
case "starting":
|
|
44282
|
+
return { state: "starting" };
|
|
44283
|
+
case "available":
|
|
44284
|
+
return { state: "idle" };
|
|
44285
|
+
case "ringing":
|
|
44286
|
+
return { state: "callRinging", call: asCall };
|
|
44287
|
+
case "dialing":
|
|
44288
|
+
return ((_c = leg.call) == null ? void 0 : _c.state) === "ringing" ? { state: "manualCallRinging", call: asCall } : { state: "manualCallSetup", call: asCall };
|
|
44289
|
+
case "inCall":
|
|
44290
|
+
return { state: "callInProgress", call: asCall };
|
|
44291
|
+
case "afterCall":
|
|
44292
|
+
return { state: "afterCall" };
|
|
44293
|
+
case "onBreak":
|
|
44294
|
+
return { state: "onBreak", break: ENGINE_BREAKS[0] };
|
|
44295
|
+
case "offline":
|
|
44296
|
+
return { state: "offline" };
|
|
44297
|
+
case "error":
|
|
44298
|
+
return { state: "startupError", error: "unknownError" };
|
|
44299
|
+
}
|
|
44300
|
+
}
|
|
44301
|
+
function callInfoFromView(view) {
|
|
44302
|
+
var _a2;
|
|
44303
|
+
const leg = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg;
|
|
44304
|
+
const call = leg == null ? void 0 : leg.call;
|
|
44305
|
+
if (!leg || !call) return void 0;
|
|
44306
|
+
const successQualifications = qualificationsFromView(view, "success");
|
|
44307
|
+
const discardQualifications = qualificationsFromView(view, "discard");
|
|
44308
|
+
const contact = call.contact;
|
|
44309
|
+
if (contact == null ? void 0 : contact.campaign) {
|
|
44310
|
+
const info = {
|
|
44311
|
+
campaign: {
|
|
44312
|
+
id: 0,
|
|
44313
|
+
name: contact.campaign,
|
|
44314
|
+
type: 0,
|
|
44315
|
+
requireCallComments: false,
|
|
44316
|
+
enableTransfer: false,
|
|
44317
|
+
enableScheduledCall: false,
|
|
44318
|
+
successQualifications,
|
|
44319
|
+
discardQualifications,
|
|
44320
|
+
previewDuration: "",
|
|
44321
|
+
showCampaignModelOnService: false,
|
|
44322
|
+
skipCallQualification: false
|
|
44323
|
+
},
|
|
44324
|
+
campaignList: { id: 0, name: "" },
|
|
44325
|
+
campaignContact: { id: call.campaignContactPhoneId ?? 0, label: contact.name, phoneNumber: contact.phone }
|
|
44326
|
+
};
|
|
44327
|
+
return { type: "campaign", info };
|
|
44328
|
+
}
|
|
44329
|
+
return { type: "manual", info: { successQualifications, discardQualifications } };
|
|
44330
|
+
}
|
|
44331
|
+
function useEngineCall(client2) {
|
|
44332
|
+
var _a2, _b;
|
|
44333
|
+
const state = useEngineState(client2);
|
|
44334
|
+
const snapshot = ((_b = (_a2 = state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.call) ?? null;
|
|
44335
|
+
const ref = useRef(null);
|
|
44336
|
+
if (!snapshot || snapshot.state === "ended") {
|
|
44337
|
+
ref.current = null;
|
|
44338
|
+
} else if (!ref.current || ref.current.id !== snapshot.id) {
|
|
44339
|
+
ref.current = new EngineCall(client2, snapshot);
|
|
44340
|
+
} else {
|
|
44341
|
+
ref.current.snapshot = snapshot;
|
|
44342
|
+
}
|
|
44343
|
+
return ref.current;
|
|
44344
|
+
}
|
|
44345
|
+
function useEngineOperatorState(client2) {
|
|
44346
|
+
const state = useEngineState(client2);
|
|
44347
|
+
const call = useEngineCall(client2);
|
|
44348
|
+
return useMemo(() => operatorStateFromView(state.view, call), [state.view, call]);
|
|
44349
|
+
}
|
|
44350
|
+
function useEngineCurrentCall(client2) {
|
|
44351
|
+
const call = useEngineCall(client2);
|
|
44352
|
+
return call ?? void 0;
|
|
44353
|
+
}
|
|
44354
|
+
function useEngineCurrentCallInfo(client2) {
|
|
44355
|
+
var _a2, _b, _c, _d, _e;
|
|
44356
|
+
const state = useEngineState(client2);
|
|
44357
|
+
const view = state.view;
|
|
44358
|
+
const key = JSON.stringify([(_b = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.call) == null ? void 0 : _b.id, (_d = (_c = view == null ? void 0 : view.snapshot) == null ? void 0 : _c.leg.call) == null ? void 0 : _d.contact, (_e = view == null ? void 0 : view.snapshot) == null ? void 0 : _e.leg.qualifications]);
|
|
44359
|
+
return useMemo(() => callInfoFromView(view), [key]);
|
|
44360
|
+
}
|
|
44361
|
+
function useEngineQualifications(client2, kind) {
|
|
44362
|
+
var _a2, _b;
|
|
44363
|
+
const state = useEngineState(client2);
|
|
44364
|
+
const raw = ((_b = (_a2 = state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.qualifications) ?? [];
|
|
44365
|
+
const key = JSON.stringify(raw);
|
|
44366
|
+
return useMemo(() => qualificationsFromView(state.view, kind), [key, kind]);
|
|
44367
|
+
}
|
|
44368
|
+
function useEngineControls(client2) {
|
|
44369
|
+
return useMemo(
|
|
44370
|
+
() => ({
|
|
44371
|
+
becomeAvailable: () => {
|
|
44372
|
+
var _a2, _b;
|
|
44373
|
+
const status = (_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status;
|
|
44374
|
+
if (status === "onBreak") client2.command({ name: "resume" });
|
|
44375
|
+
},
|
|
44376
|
+
goOffline: () => void 0,
|
|
44377
|
+
enterOnBreak: () => client2.command({ name: "pause" }),
|
|
44378
|
+
makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
|
|
44379
|
+
finishAfterCall: (data) => {
|
|
44380
|
+
if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
|
|
44381
|
+
else client2.command({ name: "finish" });
|
|
44382
|
+
}
|
|
44383
|
+
}),
|
|
44384
|
+
[client2]
|
|
44385
|
+
);
|
|
44386
|
+
}
|
|
44387
|
+
function useEngineCurrentCallControls(client2) {
|
|
44388
|
+
return useMemo(
|
|
44389
|
+
() => ({
|
|
44390
|
+
answer: () => client2.command({ name: "answer" }),
|
|
44391
|
+
reject: () => client2.command({ name: "reject" }),
|
|
44392
|
+
hangup: () => client2.command({ name: "hangup" }),
|
|
44393
|
+
isOnHold: () => ({ local: false, remote: false }),
|
|
44394
|
+
hold: async () => void 0,
|
|
44395
|
+
unhold: async () => void 0,
|
|
44396
|
+
isMuted: () => client2.getState().inputMuted,
|
|
44397
|
+
mute: () => client2.setInputMuted(true),
|
|
44398
|
+
unmute: () => client2.setInputMuted(false)
|
|
44399
|
+
}),
|
|
44400
|
+
[client2]
|
|
44401
|
+
);
|
|
44402
|
+
}
|
|
44403
|
+
function useEngineAudioInputMuted(client2) {
|
|
44404
|
+
const state = useEngineState(client2);
|
|
44405
|
+
const set = useCallback((muted) => client2.setInputMuted(muted), [client2]);
|
|
44406
|
+
return [state.inputMuted, set];
|
|
44407
|
+
}
|
|
44408
|
+
function useEngineAudioOutputMuted(client2) {
|
|
44409
|
+
const state = useEngineState(client2);
|
|
44410
|
+
const set = useCallback((muted) => client2.setOutputMuted(muted), [client2]);
|
|
44411
|
+
return [state.outputMuted, set];
|
|
44412
|
+
}
|
|
44413
|
+
function useEngineAudioOutputVolume(client2) {
|
|
44414
|
+
const state = useEngineState(client2);
|
|
44415
|
+
const set = useCallback((volume) => client2.setOutputVolume(volume), [client2]);
|
|
44416
|
+
return [state.outputVolume, set];
|
|
44417
|
+
}
|
|
44418
|
+
function useEngineEventListener(client2, event, listener) {
|
|
44419
|
+
const ref = useRef(listener);
|
|
44420
|
+
ref.current = listener;
|
|
44421
|
+
useEffect(() => {
|
|
44422
|
+
return client2.onSessionLost((lost) => {
|
|
44423
|
+
ref.current({ originator: lost.originator });
|
|
44424
|
+
});
|
|
44425
|
+
}, [client2, event]);
|
|
44426
|
+
}
|
|
44427
|
+
const operators = /* @__PURE__ */ new WeakMap();
|
|
44428
|
+
function engineOperator(client2) {
|
|
44429
|
+
let operator = operators.get(client2);
|
|
44430
|
+
if (!operator) {
|
|
44431
|
+
const breaks = new EngineSignal(ENGINE_BREAKS);
|
|
44432
|
+
operator = {
|
|
44433
|
+
breaks,
|
|
44434
|
+
user: client2.userSignal,
|
|
44435
|
+
audioOutput: {
|
|
44436
|
+
setAudioOutputDevice: (id) => void client2.setOutputDevice(id).catch(() => void 0),
|
|
44437
|
+
setVolume: (volume) => client2.setOutputVolume(volume),
|
|
44438
|
+
setMute: (muted) => client2.setOutputMuted(muted)
|
|
44439
|
+
},
|
|
44440
|
+
stop: () => void 0,
|
|
44441
|
+
goOffline: () => void 0,
|
|
44442
|
+
becomeAvailable: () => {
|
|
44443
|
+
var _a2, _b;
|
|
44444
|
+
if (((_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status) === "onBreak") client2.command({ name: "resume" });
|
|
44445
|
+
},
|
|
44446
|
+
enterOnBreak: () => client2.command({ name: "pause" }),
|
|
44447
|
+
makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
|
|
44448
|
+
finishAfterCall: (data) => {
|
|
44449
|
+
if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
|
|
44450
|
+
else client2.command({ name: "finish" });
|
|
44451
|
+
}
|
|
44452
|
+
};
|
|
44453
|
+
operators.set(client2, operator);
|
|
44454
|
+
}
|
|
44455
|
+
return operator;
|
|
44456
|
+
}
|
|
44457
|
+
function useEngineOperator(client2) {
|
|
44458
|
+
return useMemo(() => engineOperator(client2), [client2]);
|
|
44459
|
+
}
|
|
44460
|
+
function useEngineTranscript(client2) {
|
|
44461
|
+
return useEngineState(client2).transcript;
|
|
44462
|
+
}
|
|
44463
|
+
function useCallOperator() {
|
|
44464
|
+
const engine = useContext(EngineClientContext);
|
|
44465
|
+
return engine ? useEngineOperator(engine) : clientSdkReactExports.useCallOperator();
|
|
44466
|
+
}
|
|
44467
|
+
function useCallOperatorState() {
|
|
44468
|
+
const engine = useContext(EngineClientContext);
|
|
44469
|
+
return engine ? useEngineOperatorState(engine) : clientSdkReactExports.useCallOperatorState();
|
|
44470
|
+
}
|
|
44471
|
+
function useCallOperatorControls() {
|
|
44472
|
+
const engine = useContext(EngineClientContext);
|
|
44473
|
+
return engine ? useEngineControls(engine) : clientSdkReactExports.useCallOperatorControls();
|
|
44474
|
+
}
|
|
44475
|
+
function useCallOperatorCurrentCall() {
|
|
44476
|
+
const engine = useContext(EngineClientContext);
|
|
44477
|
+
return engine ? useEngineCurrentCall(engine) : clientSdkReactExports.useCallOperatorCurrentCall();
|
|
44478
|
+
}
|
|
44479
|
+
function useCallOperatorCurrentCallInfo() {
|
|
44480
|
+
const engine = useContext(EngineClientContext);
|
|
44481
|
+
return engine ? useEngineCurrentCallInfo(engine) : clientSdkReactExports.useCallOperatorCurrentCallInfo();
|
|
44482
|
+
}
|
|
44483
|
+
function useCallOperatorSuccessQualifications() {
|
|
44484
|
+
const engine = useContext(EngineClientContext);
|
|
44485
|
+
return engine ? useEngineQualifications(engine, "success") : clientSdkReactExports.useCallOperatorSuccessQualifications();
|
|
44486
|
+
}
|
|
44487
|
+
function useCallOperatorDiscardQualifications() {
|
|
44488
|
+
const engine = useContext(EngineClientContext);
|
|
44489
|
+
return engine ? useEngineQualifications(engine, "discard") : clientSdkReactExports.useCallOperatorDiscardQualifications();
|
|
44490
|
+
}
|
|
44491
|
+
function useCallOperatorCurrentCallControls() {
|
|
44492
|
+
const engine = useContext(EngineClientContext);
|
|
44493
|
+
return engine ? useEngineCurrentCallControls(engine) : clientSdkReactExports.useCallOperatorCurrentCallControls();
|
|
44494
|
+
}
|
|
44495
|
+
function useCallOperatorAudioInputMuted() {
|
|
44496
|
+
const engine = useContext(EngineClientContext);
|
|
44497
|
+
return engine ? useEngineAudioInputMuted(engine) : clientSdkReactExports.useCallOperatorAudioInputMuted();
|
|
44498
|
+
}
|
|
44499
|
+
function useCallOperatorAudioOutputMuted() {
|
|
44500
|
+
const engine = useContext(EngineClientContext);
|
|
44501
|
+
return engine ? useEngineAudioOutputMuted(engine) : clientSdkReactExports.useCallOperatorAudioOutputMuted();
|
|
44502
|
+
}
|
|
44503
|
+
function useCallOperatorAudioOutputVolume() {
|
|
44504
|
+
const engine = useContext(EngineClientContext);
|
|
44505
|
+
return engine ? useEngineAudioOutputVolume(engine) : clientSdkReactExports.useCallOperatorAudioOutputVolume();
|
|
44506
|
+
}
|
|
44507
|
+
function useCallOperatorEventListener(event, listener) {
|
|
44508
|
+
const engine = useContext(EngineClientContext);
|
|
44509
|
+
if (engine) useEngineEventListener(engine, event, listener);
|
|
44510
|
+
else clientSdkReactExports.useCallOperatorEventListener(event, listener);
|
|
44511
|
+
}
|
|
43728
44512
|
var DefaultContext = {
|
|
43729
44513
|
color: void 0,
|
|
43730
44514
|
size: void 0,
|
|
@@ -48745,8 +49529,8 @@ function formatPhoneValue(raw) {
|
|
|
48745
49529
|
}
|
|
48746
49530
|
function CallInfoDisplay() {
|
|
48747
49531
|
var _a2;
|
|
48748
|
-
const callInfo =
|
|
48749
|
-
const currentCall =
|
|
49532
|
+
const callInfo = useCallOperatorCurrentCallInfo();
|
|
49533
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
48750
49534
|
const campaignContactPhoneId = ((_a2 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a2.campaignContactPhoneId) ?? null;
|
|
48751
49535
|
if ((callInfo == null ? void 0 : callInfo.type) === "campaign") {
|
|
48752
49536
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(CampaignCallInfoDisplay, { campaignCallInfo: callInfo.info, campaignContactPhoneId });
|
|
@@ -48892,7 +49676,7 @@ function useSignal(signal) {
|
|
|
48892
49676
|
return value;
|
|
48893
49677
|
}
|
|
48894
49678
|
function useOperatorBreak() {
|
|
48895
|
-
const { enterOnBreak, becomeAvailable } =
|
|
49679
|
+
const { enterOnBreak, becomeAvailable } = useCallOperatorControls();
|
|
48896
49680
|
return {
|
|
48897
49681
|
// Use the SDK's native method
|
|
48898
49682
|
goOnBreak: enterOnBreak,
|
|
@@ -52189,19 +52973,19 @@ function AuthenticatedDialer({
|
|
|
52189
52973
|
const isMiniMode = variant === "min";
|
|
52190
52974
|
const { skipManualQualification } = useDialerConfig();
|
|
52191
52975
|
console.log("[FloatingDialer/AuthenticatedDialer] 🔧 skipManualQualification:", skipManualQualification);
|
|
52192
|
-
const operator =
|
|
52193
|
-
const operatorState =
|
|
52194
|
-
const currentCall =
|
|
52195
|
-
const currentCallInfo =
|
|
52196
|
-
const rawSuccessQualifications =
|
|
52197
|
-
const rawDiscardQualifications =
|
|
52976
|
+
const operator = useCallOperator();
|
|
52977
|
+
const operatorState = useCallOperatorState();
|
|
52978
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
52979
|
+
const currentCallInfo = useCallOperatorCurrentCallInfo();
|
|
52980
|
+
const rawSuccessQualifications = useCallOperatorSuccessQualifications();
|
|
52981
|
+
const rawDiscardQualifications = useCallOperatorDiscardQualifications();
|
|
52198
52982
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
52199
52983
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
52200
|
-
const { makeManualCall, becomeAvailable, finishAfterCall } =
|
|
52201
|
-
const { hangup, answer, reject } =
|
|
52202
|
-
const [inputMuted, setInputMuted] =
|
|
52203
|
-
const [outputMuted, setOutputMuted] =
|
|
52204
|
-
const [outputVolume, setOutputVolume] =
|
|
52984
|
+
const { makeManualCall, becomeAvailable, finishAfterCall } = useCallOperatorControls();
|
|
52985
|
+
const { hangup, answer, reject } = useCallOperatorCurrentCallControls();
|
|
52986
|
+
const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
|
|
52987
|
+
const [outputMuted, setOutputMuted] = useCallOperatorAudioOutputMuted();
|
|
52988
|
+
const [outputVolume, setOutputVolume] = useCallOperatorAudioOutputVolume();
|
|
52205
52989
|
useEffect(() => {
|
|
52206
52990
|
console.log("[FloatingDialer] Estado do operador mudou:", {
|
|
52207
52991
|
state: operatorState == null ? void 0 : operatorState.state,
|
|
@@ -52234,7 +53018,7 @@ function AuthenticatedDialer({
|
|
|
52234
53018
|
const visibleInputRef = useRef(null);
|
|
52235
53019
|
const [callError, setCallError] = useState(null);
|
|
52236
53020
|
const [availabilityError, setAvailabilityError] = useState(null);
|
|
52237
|
-
|
|
53021
|
+
useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
52238
53022
|
const notice = describeSessionDrop(data == null ? void 0 : data.originator);
|
|
52239
53023
|
setAvailabilityError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
52240
53024
|
});
|
|
@@ -56837,9 +57621,69 @@ function DialerNoticeBanner({ notice, onDismiss, onRecheck, onHeightChange }) {
|
|
|
56837
57621
|
dismissible && onDismiss && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "dialer-notice-close", onClick: onDismiss, "aria-label": "Fechar aviso", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-xmark", "aria-hidden": "true" }) })
|
|
56838
57622
|
] });
|
|
56839
57623
|
}
|
|
57624
|
+
const EMOTION_LABEL = {
|
|
57625
|
+
raiva: "raiva",
|
|
57626
|
+
irritacao: "irritação",
|
|
57627
|
+
frustracao: "frustração",
|
|
57628
|
+
desconfianca: "desconfiança",
|
|
57629
|
+
impaciencia: "impaciência",
|
|
57630
|
+
confusao: "confusão",
|
|
57631
|
+
nervosismo: "nervosismo",
|
|
57632
|
+
surpresa: "surpresa",
|
|
57633
|
+
desanimo: "desânimo",
|
|
57634
|
+
cansaco: "cansaço",
|
|
57635
|
+
interesse: "interesse",
|
|
57636
|
+
entusiasmo: "entusiasmo",
|
|
57637
|
+
satisfacao: "satisfação",
|
|
57638
|
+
alivio: "alívio",
|
|
57639
|
+
neutro: "neutro"
|
|
57640
|
+
};
|
|
57641
|
+
const STANCE_LABEL = {
|
|
57642
|
+
aberto: "cliente aberto",
|
|
57643
|
+
neutro: "cliente neutro",
|
|
57644
|
+
resistente: "cliente resistente",
|
|
57645
|
+
hostil: "cliente hostil",
|
|
57646
|
+
indefinido: "lendo a conversa"
|
|
57647
|
+
};
|
|
57648
|
+
const SPEAKER_LABEL = { agent: "Você", customer: "Cliente", mixed: "" };
|
|
57649
|
+
function topEmotions(segment, max2 = 2) {
|
|
57650
|
+
return (segment.emotions ?? []).filter((e) => e.label !== "neutro" && e.intensity >= 0.3).slice(0, max2);
|
|
57651
|
+
}
|
|
57652
|
+
function LiveCaptionPanel({ maxLines = 4 }) {
|
|
57653
|
+
const client2 = useEngineClient();
|
|
57654
|
+
if (!client2) return null;
|
|
57655
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(LiveCaptionPanelInner, { client: client2, maxLines });
|
|
57656
|
+
}
|
|
57657
|
+
function LiveCaptionPanelInner({ client: client2, maxLines }) {
|
|
57658
|
+
const { segments, insight, status } = useEngineTranscript(client2);
|
|
57659
|
+
const listening = (status == null ? void 0 : status.live) === "running";
|
|
57660
|
+
if (segments.length === 0 && !insight && !listening) return null;
|
|
57661
|
+
const rows = segments.slice(-maxLines);
|
|
57662
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "live-captions", id: "liveCaptions", "aria-live": "polite", children: [
|
|
57663
|
+
insight && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `live-insight stance-${insight.customer.stance}`, id: "liveInsight", title: insight.summary, children: [
|
|
57664
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-insight-stance", children: STANCE_LABEL[insight.customer.stance] ?? insight.customer.stance }),
|
|
57665
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "live-insight-interest", children: [
|
|
57666
|
+
"interesse ",
|
|
57667
|
+
Math.round(insight.customer.interest),
|
|
57668
|
+
"%"
|
|
57669
|
+
] }),
|
|
57670
|
+
insight.nextMove && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-insight-next", children: insight.nextMove })
|
|
57671
|
+
] }),
|
|
57672
|
+
rows.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "live-caption-list", children: rows.map((segment) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: `live-caption ${segment.speaker}${segment.interim ? " interim" : ""}${segment.refined ? " refined" : ""}`, children: [
|
|
57673
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-caption-speaker", children: SPEAKER_LABEL[segment.speaker] }),
|
|
57674
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-caption-text", children: segment.text }),
|
|
57675
|
+
topEmotions(segment).map((emotion) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `live-caption-emotion${emotion.source === "context" ? " ctx" : ""}`, title: emotion.evidence ?? "", children: [
|
|
57676
|
+
EMOTION_LABEL[emotion.label] ?? emotion.label,
|
|
57677
|
+
" ",
|
|
57678
|
+
Math.round(emotion.intensity * 100),
|
|
57679
|
+
"%"
|
|
57680
|
+
] }, emotion.label))
|
|
57681
|
+
] }, segment.id)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "live-caption-waiting", children: "Ouvindo a conversa…" })
|
|
57682
|
+
] });
|
|
57683
|
+
}
|
|
56840
57684
|
function useDialerNotices(options = {}) {
|
|
56841
|
-
const operatorState =
|
|
56842
|
-
const currentCall =
|
|
57685
|
+
const operatorState = useCallOperatorState();
|
|
57686
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
56843
57687
|
const [deviceIssue, setDeviceIssue] = useState(null);
|
|
56844
57688
|
const [sessionDrop, setSessionDrop] = useState(null);
|
|
56845
57689
|
const [callFailure, setCallFailure] = useState(null);
|
|
@@ -56869,7 +57713,7 @@ function useDialerNotices(options = {}) {
|
|
|
56869
57713
|
(_a3 = mediaDevices == null ? void 0 : mediaDevices.removeEventListener) == null ? void 0 : _a3.call(mediaDevices, "devicechange", handler);
|
|
56870
57714
|
};
|
|
56871
57715
|
}, [options.checkDevices]);
|
|
56872
|
-
|
|
57716
|
+
useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
56873
57717
|
setSessionDrop((data == null ? void 0 : data.originator) ?? "unknown");
|
|
56874
57718
|
});
|
|
56875
57719
|
useEffect(() => {
|
|
@@ -57762,21 +58606,21 @@ function AuthenticatedMobileDialer({
|
|
|
57762
58606
|
const [speakerDeviceId, setSpeakerDeviceId] = useState(null);
|
|
57763
58607
|
const [defaultDeviceId, setDefaultDeviceId] = useState("default");
|
|
57764
58608
|
const [speakerSupported, setSpeakerSupported] = useState(null);
|
|
57765
|
-
const operator =
|
|
57766
|
-
const operatorState =
|
|
57767
|
-
const currentCall =
|
|
57768
|
-
const { hangup, answer, reject } =
|
|
57769
|
-
const callInfo =
|
|
57770
|
-
const rawSuccessQualifications =
|
|
57771
|
-
const rawDiscardQualifications =
|
|
58609
|
+
const operator = useCallOperator();
|
|
58610
|
+
const operatorState = useCallOperatorState();
|
|
58611
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
58612
|
+
const { hangup, answer, reject } = useCallOperatorCurrentCallControls();
|
|
58613
|
+
const callInfo = useCallOperatorCurrentCallInfo();
|
|
58614
|
+
const rawSuccessQualifications = useCallOperatorSuccessQualifications();
|
|
58615
|
+
const rawDiscardQualifications = useCallOperatorDiscardQualifications();
|
|
57772
58616
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
57773
58617
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
57774
|
-
const { makeManualCall, finishAfterCall } =
|
|
58618
|
+
const { makeManualCall, finishAfterCall } = useCallOperatorControls();
|
|
57775
58619
|
const { goOnBreak, goOffBreak } = useOperatorBreak();
|
|
57776
58620
|
const breaks = useSignal(operator == null ? void 0 : operator.breaks) || [];
|
|
57777
58621
|
const userData = useSignal(operator == null ? void 0 : operator.user);
|
|
57778
58622
|
const isOnBreak = (operatorState == null ? void 0 : operatorState.state) === "onBreak";
|
|
57779
|
-
const [inputMuted, setInputMuted] =
|
|
58623
|
+
const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
|
|
57780
58624
|
const haptic = useHaptic();
|
|
57781
58625
|
const dtmf = useDTMF({ enabled: true });
|
|
57782
58626
|
const keySequenceRef = useRef("");
|
|
@@ -58595,6 +59439,7 @@ function AuthenticatedMobileDialer({
|
|
|
58595
59439
|
] })
|
|
58596
59440
|
] }) })
|
|
58597
59441
|
] }),
|
|
59442
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(LiveCaptionPanel, {}),
|
|
58598
59443
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "call-controls", children: [
|
|
58599
59444
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "control-row", children: [
|
|
58600
59445
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -59049,14 +59894,14 @@ function AuthenticatedMiniDialer({
|
|
|
59049
59894
|
}) {
|
|
59050
59895
|
var _a2;
|
|
59051
59896
|
const { skipManualQualification } = useDialerConfig();
|
|
59052
|
-
const operatorState =
|
|
59053
|
-
const currentCallInfo =
|
|
59054
|
-
const currentCall =
|
|
59055
|
-
const rawSuccessQualifications =
|
|
59056
|
-
const rawDiscardQualifications =
|
|
59897
|
+
const operatorState = useCallOperatorState();
|
|
59898
|
+
const currentCallInfo = useCallOperatorCurrentCallInfo();
|
|
59899
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
59900
|
+
const rawSuccessQualifications = useCallOperatorSuccessQualifications();
|
|
59901
|
+
const rawDiscardQualifications = useCallOperatorDiscardQualifications();
|
|
59057
59902
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
59058
59903
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
59059
|
-
const { finishAfterCall } =
|
|
59904
|
+
const { finishAfterCall } = useCallOperatorControls();
|
|
59060
59905
|
const [isClientInfoModalOpen, setIsClientInfoModalOpen] = useState(false);
|
|
59061
59906
|
const [isQualificationModalOpen, setIsQualificationModalOpen] = useState(false);
|
|
59062
59907
|
const [callDurationSeconds, setCallDurationSeconds] = useState(0);
|
|
@@ -59350,8 +60195,8 @@ function buildHeaders(sessionToken) {
|
|
|
59350
60195
|
}
|
|
59351
60196
|
const delay = (ms2) => new Promise((resolve) => setTimeout(resolve, ms2));
|
|
59352
60197
|
function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask }) {
|
|
59353
|
-
const operatorState =
|
|
59354
|
-
const { makeManualCall } =
|
|
60198
|
+
const operatorState = useCallOperatorState();
|
|
60199
|
+
const { makeManualCall } = useCallOperatorControls();
|
|
59355
60200
|
const stateRef = useRef(operatorState == null ? void 0 : operatorState.state);
|
|
59356
60201
|
stateRef.current = operatorState == null ? void 0 : operatorState.state;
|
|
59357
60202
|
const makeManualCallRef = useRef(makeManualCall);
|
|
@@ -59553,7 +60398,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
|
|
|
59553
60398
|
}, [enabled, pollIntervalMs, sessionToken]);
|
|
59554
60399
|
return null;
|
|
59555
60400
|
}
|
|
59556
|
-
const WIDGET_VERSION = "2.
|
|
60401
|
+
const WIDGET_VERSION = "2.6.0";
|
|
59557
60402
|
let currentSipWebSocket = null;
|
|
59558
60403
|
function sendSipUnregisterIfPossible(ws) {
|
|
59559
60404
|
if (ws.readyState !== WebSocket.OPEN) return;
|
|
@@ -59680,8 +60525,8 @@ function useDialerConfig() {
|
|
|
59680
60525
|
}
|
|
59681
60526
|
const SdkCleanupManager = forwardRef(
|
|
59682
60527
|
function SdkCleanupManager2({ children: children2 }, ref) {
|
|
59683
|
-
const operator =
|
|
59684
|
-
const controls =
|
|
60528
|
+
const operator = useCallOperator();
|
|
60529
|
+
const controls = useCallOperatorControls();
|
|
59685
60530
|
useImperativeHandle(ref, () => ({
|
|
59686
60531
|
goOffline: () => {
|
|
59687
60532
|
console.log("[Callix Dialer] 📴 Chamando goOffline() para enviar SIP UNREGISTER...");
|
|
@@ -59717,6 +60562,10 @@ const SdkCleanupManager = forwardRef(
|
|
|
59717
60562
|
);
|
|
59718
60563
|
function deriveOptionsFromElement(element) {
|
|
59719
60564
|
const result2 = {};
|
|
60565
|
+
const datasetEngineUrl = element.getAttribute("data-callix-engine-url") ?? element.dataset.callixEngineUrl;
|
|
60566
|
+
if (datasetEngineUrl && datasetEngineUrl.trim()) result2.engineUrl = datasetEngineUrl.trim();
|
|
60567
|
+
const datasetEngineToken = element.getAttribute("data-callix-engine-token") ?? element.dataset.callixEngineToken;
|
|
60568
|
+
if (datasetEngineToken && datasetEngineToken.trim()) result2.engineToken = datasetEngineToken.trim();
|
|
59720
60569
|
const datasetMode = element.getAttribute("data-callix-mode") || element.dataset.callixMode || element.dataset.mode;
|
|
59721
60570
|
const datasetUsername = element.getAttribute("data-callix-username") || element.dataset.callixUsername || element.dataset.username;
|
|
59722
60571
|
const classList = Array.from(element.classList);
|
|
@@ -59808,6 +60657,12 @@ class CallixDialerWidget {
|
|
|
59808
60657
|
if (this.options.clickToCall === void 0 && derived.clickToCall !== void 0) {
|
|
59809
60658
|
this.options.clickToCall = derived.clickToCall;
|
|
59810
60659
|
}
|
|
60660
|
+
if (!this.options.engineUrl && derived.engineUrl) {
|
|
60661
|
+
this.options.engineUrl = derived.engineUrl;
|
|
60662
|
+
}
|
|
60663
|
+
if (!this.options.engineToken && derived.engineToken) {
|
|
60664
|
+
this.options.engineToken = derived.engineToken;
|
|
60665
|
+
}
|
|
59811
60666
|
this.root = clientExports.createRoot(this.container);
|
|
59812
60667
|
this.root.render(
|
|
59813
60668
|
/* @__PURE__ */ jsxRuntimeExports.jsx(StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialerWrapper, { options: this.options, containerEl: this.container }) })
|
|
@@ -59840,6 +60695,20 @@ const getStorageKeys = (username) => ({
|
|
|
59840
60695
|
SKIP_MANUAL_QUALIFICATION: `callix_dialer_${username}_skip_manual_qualification`
|
|
59841
60696
|
});
|
|
59842
60697
|
const LAST_USERNAME_KEY = "callix_dialer_last_username";
|
|
60698
|
+
async function fetchSkipQualification(domain, token) {
|
|
60699
|
+
var _a2, _b;
|
|
60700
|
+
try {
|
|
60701
|
+
const response = await fetch(`https://${domain}/sdk/v1/client/user-service/config`, {
|
|
60702
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
60703
|
+
credentials: "omit"
|
|
60704
|
+
});
|
|
60705
|
+
if (!response.ok) return null;
|
|
60706
|
+
const data = await response.json();
|
|
60707
|
+
return ((_b = (_a2 = data == null ? void 0 : data.service) == null ? void 0 : _a2.outboundOptions) == null ? void 0 : _b.skipQualification) === true;
|
|
60708
|
+
} catch {
|
|
60709
|
+
return null;
|
|
60710
|
+
}
|
|
60711
|
+
}
|
|
59843
60712
|
function DialerWrapper({ options, containerEl }) {
|
|
59844
60713
|
const [username, setUsername] = useState(null);
|
|
59845
60714
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
@@ -59866,8 +60735,49 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59866
60735
|
useEffect(() => {
|
|
59867
60736
|
currentSessionRef.current.token = sessionToken;
|
|
59868
60737
|
}, [sessionToken]);
|
|
60738
|
+
const engineMode = Boolean(options.engineUrl);
|
|
60739
|
+
const engineModeRef = useRef(engineMode);
|
|
60740
|
+
engineModeRef.current = engineMode;
|
|
60741
|
+
const engineRef = useRef(null);
|
|
60742
|
+
const ensureEngine = useCallback(
|
|
60743
|
+
(user) => {
|
|
60744
|
+
const current = engineRef.current;
|
|
60745
|
+
if (current && current.agentId === user) {
|
|
60746
|
+
current.connect();
|
|
60747
|
+
return current;
|
|
60748
|
+
}
|
|
60749
|
+
current == null ? void 0 : current.close();
|
|
60750
|
+
const client2 = new EngineClient({
|
|
60751
|
+
url: options.engineUrl ?? "",
|
|
60752
|
+
token: options.engineToken ?? "",
|
|
60753
|
+
agentId: user,
|
|
60754
|
+
label: "Discador",
|
|
60755
|
+
log: (message) => console.log("[Callix Dialer/motor]", message)
|
|
60756
|
+
});
|
|
60757
|
+
client2.onSessionLost((lost) => {
|
|
60758
|
+
const notice = describeSessionDrop(lost.originator);
|
|
60759
|
+
setError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
60760
|
+
setIsAuthenticated(false);
|
|
60761
|
+
setSessionToken(null);
|
|
60762
|
+
autoLoginAttemptedRef.current = true;
|
|
60763
|
+
});
|
|
60764
|
+
client2.connect();
|
|
60765
|
+
engineRef.current = client2;
|
|
60766
|
+
return client2;
|
|
60767
|
+
},
|
|
60768
|
+
[options.engineUrl, options.engineToken]
|
|
60769
|
+
);
|
|
60770
|
+
useEffect(
|
|
60771
|
+
() => () => {
|
|
60772
|
+
var _a2;
|
|
60773
|
+
(_a2 = engineRef.current) == null ? void 0 : _a2.close();
|
|
60774
|
+
engineRef.current = null;
|
|
60775
|
+
},
|
|
60776
|
+
[]
|
|
60777
|
+
);
|
|
59869
60778
|
useEffect(() => {
|
|
59870
60779
|
const handleBeforeUnload = () => {
|
|
60780
|
+
if (engineModeRef.current) return;
|
|
59871
60781
|
const { apiUrl, token } = currentSessionRef.current;
|
|
59872
60782
|
if (token) {
|
|
59873
60783
|
const data = JSON.stringify({ logout: true });
|
|
@@ -59932,6 +60842,77 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59932
60842
|
setError(null);
|
|
59933
60843
|
setLoginStatus("Preparando login...");
|
|
59934
60844
|
console.log("[Callix Dialer] 🚀 handleLogin iniciado para usuário:", user);
|
|
60845
|
+
if (options.engineUrl) {
|
|
60846
|
+
const engineApiUrl = options.apiUrl || "/api/user-sdk-session";
|
|
60847
|
+
try {
|
|
60848
|
+
setLoginStatus("Conectando ao motor...");
|
|
60849
|
+
const client2 = ensureEngine(user);
|
|
60850
|
+
const view = await client2.waitForView(15e3);
|
|
60851
|
+
const domain = options.domain || new URL(engineApiUrl, window.location.href).host;
|
|
60852
|
+
const engineHasSession = (view.engine === "running" || view.engine === "starting") && !view.sessionLost;
|
|
60853
|
+
let token = null;
|
|
60854
|
+
if (engineHasSession) {
|
|
60855
|
+
try {
|
|
60856
|
+
token = localStorage.getItem(getStorageKeys(user).SESSION_TOKEN);
|
|
60857
|
+
} catch {
|
|
60858
|
+
token = null;
|
|
60859
|
+
}
|
|
60860
|
+
console.log("[Callix Dialer] motor já tem a sessão do agente; entrando sem novo login");
|
|
60861
|
+
} else {
|
|
60862
|
+
setLoginStatus("Autenticando no Callix...");
|
|
60863
|
+
const response = await fetch(engineApiUrl, {
|
|
60864
|
+
method: "POST",
|
|
60865
|
+
headers: { "Content-Type": "application/json" },
|
|
60866
|
+
body: JSON.stringify({ username: user }),
|
|
60867
|
+
credentials: "same-origin"
|
|
60868
|
+
});
|
|
60869
|
+
if (!response.ok) {
|
|
60870
|
+
const errorData = await response.json().catch(() => ({ error: "Erro desconhecido" }));
|
|
60871
|
+
throw new Error(errorData.error || `Falha na autenticação: ${response.status}`);
|
|
60872
|
+
}
|
|
60873
|
+
const data = await response.json();
|
|
60874
|
+
token = data.userSessionToken;
|
|
60875
|
+
client2.setSession({ domain, userSessionToken: token });
|
|
60876
|
+
setLoginStatus("Subindo o motor...");
|
|
60877
|
+
}
|
|
60878
|
+
if (token) {
|
|
60879
|
+
const skip = await fetchSkipQualification(domain, token);
|
|
60880
|
+
if (skip !== null) {
|
|
60881
|
+
setSkipManualQualification(skip);
|
|
60882
|
+
try {
|
|
60883
|
+
localStorage.setItem(getStorageKeys(user).SKIP_MANUAL_QUALIFICATION, String(skip));
|
|
60884
|
+
} catch {
|
|
60885
|
+
}
|
|
60886
|
+
}
|
|
60887
|
+
}
|
|
60888
|
+
try {
|
|
60889
|
+
localStorage.setItem(LAST_USERNAME_KEY, user);
|
|
60890
|
+
if (token) {
|
|
60891
|
+
const STORAGE_KEYS = getStorageKeys(user);
|
|
60892
|
+
localStorage.setItem(STORAGE_KEYS.SESSION_TOKEN, token);
|
|
60893
|
+
localStorage.setItem(STORAGE_KEYS.DOMAIN, domain);
|
|
60894
|
+
localStorage.setItem(STORAGE_KEYS.TIMESTAMP, Date.now().toString());
|
|
60895
|
+
}
|
|
60896
|
+
} catch {
|
|
60897
|
+
}
|
|
60898
|
+
setSessionToken(token);
|
|
60899
|
+
setCallixDomain(domain);
|
|
60900
|
+
setUsername(user);
|
|
60901
|
+
setIsAuthenticated(true);
|
|
60902
|
+
setError(null);
|
|
60903
|
+
setProviderKey(Date.now());
|
|
60904
|
+
console.log("[Callix Dialer] Conectado ao motor como", user);
|
|
60905
|
+
} catch (err) {
|
|
60906
|
+
console.error("[Callix Dialer] Erro ao entrar pelo motor:", err);
|
|
60907
|
+
setError(err instanceof Error ? err.message : "Erro ao conectar ao motor");
|
|
60908
|
+
setIsAuthenticated(false);
|
|
60909
|
+
setSessionToken(null);
|
|
60910
|
+
} finally {
|
|
60911
|
+
setIsLoading(false);
|
|
60912
|
+
setLoginStatus(null);
|
|
60913
|
+
}
|
|
60914
|
+
return;
|
|
60915
|
+
}
|
|
59935
60916
|
const apiUrl = options.apiUrl || "/api/user-sdk-session";
|
|
59936
60917
|
let previousToken;
|
|
59937
60918
|
if (typeof window !== "undefined") {
|
|
@@ -60078,7 +61059,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60078
61059
|
setIsLoading(false);
|
|
60079
61060
|
setLoginStatus(null);
|
|
60080
61061
|
}
|
|
60081
|
-
}, [options.apiUrl, options.domain, isAuthenticated, sessionToken]);
|
|
61062
|
+
}, [options.apiUrl, options.domain, options.engineUrl, isAuthenticated, sessionToken, ensureEngine]);
|
|
60082
61063
|
useEffect(() => {
|
|
60083
61064
|
if (lastPresetRef.current !== presetUsername) {
|
|
60084
61065
|
lastPresetRef.current = presetUsername;
|
|
@@ -60102,8 +61083,11 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60102
61083
|
void handleLogin(username);
|
|
60103
61084
|
}, [autoLogin, presetUsername, username, isAuthenticated, isLoading, isRestoringSession, error2, handleLogin]);
|
|
60104
61085
|
const handleLogout = useCallback(async () => {
|
|
61086
|
+
var _a2;
|
|
60105
61087
|
const currentUsername = username;
|
|
60106
|
-
if (
|
|
61088
|
+
if (options.engineUrl) {
|
|
61089
|
+
(_a2 = engineRef.current) == null ? void 0 : _a2.command({ name: "logoff" });
|
|
61090
|
+
} else if (cleanupRef.current) {
|
|
60107
61091
|
console.log("[Callix Dialer] 📴 Logout: Chamando goOffline()...");
|
|
60108
61092
|
cleanupRef.current.goOffline();
|
|
60109
61093
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -60130,7 +61114,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60130
61114
|
console.warn("[Callix Dialer] Não foi possível limpar sessão:", err);
|
|
60131
61115
|
}
|
|
60132
61116
|
}
|
|
60133
|
-
}, [username, presetUsername]);
|
|
61117
|
+
}, [username, presetUsername, options.engineUrl]);
|
|
60134
61118
|
const pendingAutoLogin = autoLogin && Boolean(username) && !isAuthenticated && !error2 && !autoLoginAttemptedRef.current;
|
|
60135
61119
|
const loginConfirmation = useMemo(() => {
|
|
60136
61120
|
if (isAuthenticated || isLoading || isRestoringSession || isDisconnecting || !username || pendingAutoLogin) {
|
|
@@ -60169,7 +61153,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60169
61153
|
}
|
|
60170
61154
|
) });
|
|
60171
61155
|
}
|
|
60172
|
-
if (!isAuthenticated || !sessionToken || !callixDomain) {
|
|
61156
|
+
if (!isAuthenticated || !engineMode && (!sessionToken || !callixDomain)) {
|
|
60173
61157
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
60174
61158
|
DialerComponent,
|
|
60175
61159
|
{
|
|
@@ -60189,6 +61173,38 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60189
61173
|
}
|
|
60190
61174
|
) });
|
|
60191
61175
|
}
|
|
61176
|
+
if (engineMode && engineRef.current) {
|
|
61177
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(EngineClientProvider, { client: engineRef.current, children: [
|
|
61178
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
61179
|
+
DialerComponent,
|
|
61180
|
+
{
|
|
61181
|
+
isAuthenticated,
|
|
61182
|
+
username,
|
|
61183
|
+
onLogin: handleLogin,
|
|
61184
|
+
onLogout: handleLogout,
|
|
61185
|
+
isLoadingSession: isLoading,
|
|
61186
|
+
sessionError: error2,
|
|
61187
|
+
sessionStatus: loginStatus,
|
|
61188
|
+
variant: uiVariant,
|
|
61189
|
+
portalTarget,
|
|
61190
|
+
density: options.density,
|
|
61191
|
+
mode: dialerMode
|
|
61192
|
+
}
|
|
61193
|
+
),
|
|
61194
|
+
sessionToken && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
61195
|
+
ClickToCallAgent,
|
|
61196
|
+
{
|
|
61197
|
+
enabled: clickToCall.enabled,
|
|
61198
|
+
pollIntervalMs: clickToCall.pollIntervalMs,
|
|
61199
|
+
sessionToken,
|
|
61200
|
+
onTask: clickToCall.onTask
|
|
61201
|
+
}
|
|
61202
|
+
)
|
|
61203
|
+
] }, providerKey) });
|
|
61204
|
+
}
|
|
61205
|
+
if (!sessionToken || !callixDomain) {
|
|
61206
|
+
return null;
|
|
61207
|
+
}
|
|
60192
61208
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
60193
61209
|
clientSdkReactExports.CallixClientProvider,
|
|
60194
61210
|
{
|