callix-dialer-widget 2.5.0 → 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 +1090 -48
- 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,
|
|
@@ -48532,7 +49316,13 @@ function describeCallFailure(failure) {
|
|
|
48532
49316
|
}
|
|
48533
49317
|
function pickNotice(inputs) {
|
|
48534
49318
|
if (inputs.sessionDrop) return describeSessionDrop(inputs.sessionDrop);
|
|
48535
|
-
if (inputs.operatorState === "startupError")
|
|
49319
|
+
if (inputs.operatorState === "startupError") {
|
|
49320
|
+
if (inputs.startupError === "missingAudioPermissions" && inputs.deviceIssue) {
|
|
49321
|
+
const action = inputs.deviceIssue.action ?? "";
|
|
49322
|
+
return { ...inputs.deviceIssue, action: /recarregue a página/i.test(action) ? action : `${action} Depois, recarregue a página para o discador iniciar.`.trim() };
|
|
49323
|
+
}
|
|
49324
|
+
return describeStartupError(inputs.startupError);
|
|
49325
|
+
}
|
|
48536
49326
|
if (inputs.deviceIssue) return inputs.deviceIssue;
|
|
48537
49327
|
if (inputs.operatorState === "manualCallFailed" || inputs.callFailure) return describeCallFailure(inputs.callFailure);
|
|
48538
49328
|
if (inputs.operatorState === "offline") return describeOffline();
|
|
@@ -48739,8 +49529,8 @@ function formatPhoneValue(raw) {
|
|
|
48739
49529
|
}
|
|
48740
49530
|
function CallInfoDisplay() {
|
|
48741
49531
|
var _a2;
|
|
48742
|
-
const callInfo =
|
|
48743
|
-
const currentCall =
|
|
49532
|
+
const callInfo = useCallOperatorCurrentCallInfo();
|
|
49533
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
48744
49534
|
const campaignContactPhoneId = ((_a2 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a2.campaignContactPhoneId) ?? null;
|
|
48745
49535
|
if ((callInfo == null ? void 0 : callInfo.type) === "campaign") {
|
|
48746
49536
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(CampaignCallInfoDisplay, { campaignCallInfo: callInfo.info, campaignContactPhoneId });
|
|
@@ -48886,7 +49676,7 @@ function useSignal(signal) {
|
|
|
48886
49676
|
return value;
|
|
48887
49677
|
}
|
|
48888
49678
|
function useOperatorBreak() {
|
|
48889
|
-
const { enterOnBreak, becomeAvailable } =
|
|
49679
|
+
const { enterOnBreak, becomeAvailable } = useCallOperatorControls();
|
|
48890
49680
|
return {
|
|
48891
49681
|
// Use the SDK's native method
|
|
48892
49682
|
goOnBreak: enterOnBreak,
|
|
@@ -52183,19 +52973,19 @@ function AuthenticatedDialer({
|
|
|
52183
52973
|
const isMiniMode = variant === "min";
|
|
52184
52974
|
const { skipManualQualification } = useDialerConfig();
|
|
52185
52975
|
console.log("[FloatingDialer/AuthenticatedDialer] 🔧 skipManualQualification:", skipManualQualification);
|
|
52186
|
-
const operator =
|
|
52187
|
-
const operatorState =
|
|
52188
|
-
const currentCall =
|
|
52189
|
-
const currentCallInfo =
|
|
52190
|
-
const rawSuccessQualifications =
|
|
52191
|
-
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();
|
|
52192
52982
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
52193
52983
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
52194
|
-
const { makeManualCall, becomeAvailable, finishAfterCall } =
|
|
52195
|
-
const { hangup, answer, reject } =
|
|
52196
|
-
const [inputMuted, setInputMuted] =
|
|
52197
|
-
const [outputMuted, setOutputMuted] =
|
|
52198
|
-
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();
|
|
52199
52989
|
useEffect(() => {
|
|
52200
52990
|
console.log("[FloatingDialer] Estado do operador mudou:", {
|
|
52201
52991
|
state: operatorState == null ? void 0 : operatorState.state,
|
|
@@ -52228,7 +53018,7 @@ function AuthenticatedDialer({
|
|
|
52228
53018
|
const visibleInputRef = useRef(null);
|
|
52229
53019
|
const [callError, setCallError] = useState(null);
|
|
52230
53020
|
const [availabilityError, setAvailabilityError] = useState(null);
|
|
52231
|
-
|
|
53021
|
+
useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
52232
53022
|
const notice = describeSessionDrop(data == null ? void 0 : data.originator);
|
|
52233
53023
|
setAvailabilityError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
|
|
52234
53024
|
});
|
|
@@ -56798,10 +57588,29 @@ function DebugPanel({ onClose }) {
|
|
|
56798
57588
|
showCopySuccess && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: styles.copySuccess, children: "✅ Logs copiados para a área de transferência!" })
|
|
56799
57589
|
] });
|
|
56800
57590
|
}
|
|
56801
|
-
function DialerNoticeBanner({ notice, onDismiss, onRecheck }) {
|
|
57591
|
+
function DialerNoticeBanner({ notice, onDismiss, onRecheck, onHeightChange }) {
|
|
57592
|
+
const ref = useRef(null);
|
|
57593
|
+
const visible = notice !== null;
|
|
57594
|
+
useLayoutEffect(() => {
|
|
57595
|
+
const el = ref.current;
|
|
57596
|
+
if (!onHeightChange) return;
|
|
57597
|
+
if (!el) {
|
|
57598
|
+
onHeightChange(0);
|
|
57599
|
+
return;
|
|
57600
|
+
}
|
|
57601
|
+
const report = () => onHeightChange(el.offsetHeight);
|
|
57602
|
+
report();
|
|
57603
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
57604
|
+
const observer = new ResizeObserver(report);
|
|
57605
|
+
observer.observe(el);
|
|
57606
|
+
return () => {
|
|
57607
|
+
observer.disconnect();
|
|
57608
|
+
onHeightChange(0);
|
|
57609
|
+
};
|
|
57610
|
+
}, [visible, onHeightChange]);
|
|
56802
57611
|
if (!notice) return null;
|
|
56803
57612
|
const dismissible = notice.source === "call" || notice.source === "session";
|
|
56804
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `dialer-notice ${notice.level}`, role: notice.level === "error" ? "alert" : "status", "data-code": notice.code, children: [
|
|
57613
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref, className: `dialer-notice ${notice.level}`, role: notice.level === "error" ? "alert" : "status", "data-code": notice.code, children: [
|
|
56805
57614
|
/* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: `fas ${notice.level === "error" ? "fa-circle-exclamation" : "fa-triangle-exclamation"} dialer-notice-icon`, "aria-hidden": "true" }),
|
|
56806
57615
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dialer-notice-body", children: [
|
|
56807
57616
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "dialer-notice-title", children: notice.title }),
|
|
@@ -56812,9 +57621,69 @@ function DialerNoticeBanner({ notice, onDismiss, onRecheck }) {
|
|
|
56812
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" }) })
|
|
56813
57622
|
] });
|
|
56814
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
|
+
}
|
|
56815
57684
|
function useDialerNotices(options = {}) {
|
|
56816
|
-
const operatorState =
|
|
56817
|
-
const currentCall =
|
|
57685
|
+
const operatorState = useCallOperatorState();
|
|
57686
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
56818
57687
|
const [deviceIssue, setDeviceIssue] = useState(null);
|
|
56819
57688
|
const [sessionDrop, setSessionDrop] = useState(null);
|
|
56820
57689
|
const [callFailure, setCallFailure] = useState(null);
|
|
@@ -56844,7 +57713,7 @@ function useDialerNotices(options = {}) {
|
|
|
56844
57713
|
(_a3 = mediaDevices == null ? void 0 : mediaDevices.removeEventListener) == null ? void 0 : _a3.call(mediaDevices, "devicechange", handler);
|
|
56845
57714
|
};
|
|
56846
57715
|
}, [options.checkDevices]);
|
|
56847
|
-
|
|
57716
|
+
useCallOperatorEventListener("userSessionDropped", (data) => {
|
|
56848
57717
|
setSessionDrop((data == null ? void 0 : data.originator) ?? "unknown");
|
|
56849
57718
|
});
|
|
56850
57719
|
useEffect(() => {
|
|
@@ -57737,21 +58606,21 @@ function AuthenticatedMobileDialer({
|
|
|
57737
58606
|
const [speakerDeviceId, setSpeakerDeviceId] = useState(null);
|
|
57738
58607
|
const [defaultDeviceId, setDefaultDeviceId] = useState("default");
|
|
57739
58608
|
const [speakerSupported, setSpeakerSupported] = useState(null);
|
|
57740
|
-
const operator =
|
|
57741
|
-
const operatorState =
|
|
57742
|
-
const currentCall =
|
|
57743
|
-
const { hangup, answer, reject } =
|
|
57744
|
-
const callInfo =
|
|
57745
|
-
const rawSuccessQualifications =
|
|
57746
|
-
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();
|
|
57747
58616
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
57748
58617
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
57749
|
-
const { makeManualCall, finishAfterCall } =
|
|
58618
|
+
const { makeManualCall, finishAfterCall } = useCallOperatorControls();
|
|
57750
58619
|
const { goOnBreak, goOffBreak } = useOperatorBreak();
|
|
57751
58620
|
const breaks = useSignal(operator == null ? void 0 : operator.breaks) || [];
|
|
57752
58621
|
const userData = useSignal(operator == null ? void 0 : operator.user);
|
|
57753
58622
|
const isOnBreak = (operatorState == null ? void 0 : operatorState.state) === "onBreak";
|
|
57754
|
-
const [inputMuted, setInputMuted] =
|
|
58623
|
+
const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
|
|
57755
58624
|
const haptic = useHaptic();
|
|
57756
58625
|
const dtmf = useDTMF({ enabled: true });
|
|
57757
58626
|
const keySequenceRef = useRef("");
|
|
@@ -57958,6 +58827,7 @@ function AuthenticatedMobileDialer({
|
|
|
57958
58827
|
}, [campaignFormKey]);
|
|
57959
58828
|
const callStateStr = callState;
|
|
57960
58829
|
const { notice: dialerNotice, dismiss: dismissDialerNotice, recheckDevices } = useDialerNotices();
|
|
58830
|
+
const [noticeHeight, setNoticeHeight] = useState(0);
|
|
57961
58831
|
const isError = callStateStr === "startupError" || callStateStr === "offline" || (dialerNotice == null ? void 0 : dialerNotice.source) === "device" || (dialerNotice == null ? void 0 : dialerNotice.source) === "session";
|
|
57962
58832
|
const isIdle = !callStateStr || callStateStr === "starting" || callStateStr === "idle" || callStateStr === "manualCallSetup" || callStateStr === "onBreak";
|
|
57963
58833
|
const isConnecting = callState === "manualCallSetup" || callState === "manualCallRinging" || callState === "callRinging";
|
|
@@ -58454,7 +59324,7 @@ function AuthenticatedMobileDialer({
|
|
|
58454
59324
|
addLog("info", "Debug", "🔒 Painel de debug fechado");
|
|
58455
59325
|
} });
|
|
58456
59326
|
}
|
|
58457
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", children: [
|
|
59327
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", style: { "--notice-h": `${noticeHeight}px` }, children: [
|
|
58458
59328
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `screen dialer-screen ${activeScreen === "dialer" ? "active" : ""}`, id: "dialerScreen", children: [
|
|
58459
59329
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dialer-header", children: [
|
|
58460
59330
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "status-container", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: `status-badge ${statusBadgeClass}`, id: "statusBadge", children: [
|
|
@@ -58472,7 +59342,7 @@ function AuthenticatedMobileDialer({
|
|
|
58472
59342
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Pausa" })
|
|
58473
59343
|
] }) })
|
|
58474
59344
|
] }),
|
|
58475
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DialerNoticeBanner, { notice: dialerNotice, onDismiss: dismissDialerNotice, onRecheck: () => void recheckDevices() }),
|
|
59345
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(DialerNoticeBanner, { notice: dialerNotice, onDismiss: dismissDialerNotice, onRecheck: () => void recheckDevices(), onHeightChange: setNoticeHeight }),
|
|
58476
59346
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "phone-number-display", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
58477
59347
|
PhoneNumberInput,
|
|
58478
59348
|
{
|
|
@@ -58569,6 +59439,7 @@ function AuthenticatedMobileDialer({
|
|
|
58569
59439
|
] })
|
|
58570
59440
|
] }) })
|
|
58571
59441
|
] }),
|
|
59442
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(LiveCaptionPanel, {}),
|
|
58572
59443
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "call-controls", children: [
|
|
58573
59444
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "control-row", children: [
|
|
58574
59445
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -59023,14 +59894,14 @@ function AuthenticatedMiniDialer({
|
|
|
59023
59894
|
}) {
|
|
59024
59895
|
var _a2;
|
|
59025
59896
|
const { skipManualQualification } = useDialerConfig();
|
|
59026
|
-
const operatorState =
|
|
59027
|
-
const currentCallInfo =
|
|
59028
|
-
const currentCall =
|
|
59029
|
-
const rawSuccessQualifications =
|
|
59030
|
-
const rawDiscardQualifications =
|
|
59897
|
+
const operatorState = useCallOperatorState();
|
|
59898
|
+
const currentCallInfo = useCallOperatorCurrentCallInfo();
|
|
59899
|
+
const currentCall = useCallOperatorCurrentCall();
|
|
59900
|
+
const rawSuccessQualifications = useCallOperatorSuccessQualifications();
|
|
59901
|
+
const rawDiscardQualifications = useCallOperatorDiscardQualifications();
|
|
59031
59902
|
const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
|
|
59032
59903
|
const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
|
|
59033
|
-
const { finishAfterCall } =
|
|
59904
|
+
const { finishAfterCall } = useCallOperatorControls();
|
|
59034
59905
|
const [isClientInfoModalOpen, setIsClientInfoModalOpen] = useState(false);
|
|
59035
59906
|
const [isQualificationModalOpen, setIsQualificationModalOpen] = useState(false);
|
|
59036
59907
|
const [callDurationSeconds, setCallDurationSeconds] = useState(0);
|
|
@@ -59324,8 +60195,8 @@ function buildHeaders(sessionToken) {
|
|
|
59324
60195
|
}
|
|
59325
60196
|
const delay = (ms2) => new Promise((resolve) => setTimeout(resolve, ms2));
|
|
59326
60197
|
function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask }) {
|
|
59327
|
-
const operatorState =
|
|
59328
|
-
const { makeManualCall } =
|
|
60198
|
+
const operatorState = useCallOperatorState();
|
|
60199
|
+
const { makeManualCall } = useCallOperatorControls();
|
|
59329
60200
|
const stateRef = useRef(operatorState == null ? void 0 : operatorState.state);
|
|
59330
60201
|
stateRef.current = operatorState == null ? void 0 : operatorState.state;
|
|
59331
60202
|
const makeManualCallRef = useRef(makeManualCall);
|
|
@@ -59527,7 +60398,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
|
|
|
59527
60398
|
}, [enabled, pollIntervalMs, sessionToken]);
|
|
59528
60399
|
return null;
|
|
59529
60400
|
}
|
|
59530
|
-
const WIDGET_VERSION = "2.
|
|
60401
|
+
const WIDGET_VERSION = "2.6.0";
|
|
59531
60402
|
let currentSipWebSocket = null;
|
|
59532
60403
|
function sendSipUnregisterIfPossible(ws) {
|
|
59533
60404
|
if (ws.readyState !== WebSocket.OPEN) return;
|
|
@@ -59654,8 +60525,8 @@ function useDialerConfig() {
|
|
|
59654
60525
|
}
|
|
59655
60526
|
const SdkCleanupManager = forwardRef(
|
|
59656
60527
|
function SdkCleanupManager2({ children: children2 }, ref) {
|
|
59657
|
-
const operator =
|
|
59658
|
-
const controls =
|
|
60528
|
+
const operator = useCallOperator();
|
|
60529
|
+
const controls = useCallOperatorControls();
|
|
59659
60530
|
useImperativeHandle(ref, () => ({
|
|
59660
60531
|
goOffline: () => {
|
|
59661
60532
|
console.log("[Callix Dialer] 📴 Chamando goOffline() para enviar SIP UNREGISTER...");
|
|
@@ -59691,6 +60562,10 @@ const SdkCleanupManager = forwardRef(
|
|
|
59691
60562
|
);
|
|
59692
60563
|
function deriveOptionsFromElement(element) {
|
|
59693
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();
|
|
59694
60569
|
const datasetMode = element.getAttribute("data-callix-mode") || element.dataset.callixMode || element.dataset.mode;
|
|
59695
60570
|
const datasetUsername = element.getAttribute("data-callix-username") || element.dataset.callixUsername || element.dataset.username;
|
|
59696
60571
|
const classList = Array.from(element.classList);
|
|
@@ -59782,6 +60657,12 @@ class CallixDialerWidget {
|
|
|
59782
60657
|
if (this.options.clickToCall === void 0 && derived.clickToCall !== void 0) {
|
|
59783
60658
|
this.options.clickToCall = derived.clickToCall;
|
|
59784
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
|
+
}
|
|
59785
60666
|
this.root = clientExports.createRoot(this.container);
|
|
59786
60667
|
this.root.render(
|
|
59787
60668
|
/* @__PURE__ */ jsxRuntimeExports.jsx(StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialerWrapper, { options: this.options, containerEl: this.container }) })
|
|
@@ -59814,6 +60695,20 @@ const getStorageKeys = (username) => ({
|
|
|
59814
60695
|
SKIP_MANUAL_QUALIFICATION: `callix_dialer_${username}_skip_manual_qualification`
|
|
59815
60696
|
});
|
|
59816
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
|
+
}
|
|
59817
60712
|
function DialerWrapper({ options, containerEl }) {
|
|
59818
60713
|
const [username, setUsername] = useState(null);
|
|
59819
60714
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
@@ -59840,8 +60735,49 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59840
60735
|
useEffect(() => {
|
|
59841
60736
|
currentSessionRef.current.token = sessionToken;
|
|
59842
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
|
+
);
|
|
59843
60778
|
useEffect(() => {
|
|
59844
60779
|
const handleBeforeUnload = () => {
|
|
60780
|
+
if (engineModeRef.current) return;
|
|
59845
60781
|
const { apiUrl, token } = currentSessionRef.current;
|
|
59846
60782
|
if (token) {
|
|
59847
60783
|
const data = JSON.stringify({ logout: true });
|
|
@@ -59906,6 +60842,77 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
59906
60842
|
setError(null);
|
|
59907
60843
|
setLoginStatus("Preparando login...");
|
|
59908
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
|
+
}
|
|
59909
60916
|
const apiUrl = options.apiUrl || "/api/user-sdk-session";
|
|
59910
60917
|
let previousToken;
|
|
59911
60918
|
if (typeof window !== "undefined") {
|
|
@@ -60052,7 +61059,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60052
61059
|
setIsLoading(false);
|
|
60053
61060
|
setLoginStatus(null);
|
|
60054
61061
|
}
|
|
60055
|
-
}, [options.apiUrl, options.domain, isAuthenticated, sessionToken]);
|
|
61062
|
+
}, [options.apiUrl, options.domain, options.engineUrl, isAuthenticated, sessionToken, ensureEngine]);
|
|
60056
61063
|
useEffect(() => {
|
|
60057
61064
|
if (lastPresetRef.current !== presetUsername) {
|
|
60058
61065
|
lastPresetRef.current = presetUsername;
|
|
@@ -60076,8 +61083,11 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60076
61083
|
void handleLogin(username);
|
|
60077
61084
|
}, [autoLogin, presetUsername, username, isAuthenticated, isLoading, isRestoringSession, error2, handleLogin]);
|
|
60078
61085
|
const handleLogout = useCallback(async () => {
|
|
61086
|
+
var _a2;
|
|
60079
61087
|
const currentUsername = username;
|
|
60080
|
-
if (
|
|
61088
|
+
if (options.engineUrl) {
|
|
61089
|
+
(_a2 = engineRef.current) == null ? void 0 : _a2.command({ name: "logoff" });
|
|
61090
|
+
} else if (cleanupRef.current) {
|
|
60081
61091
|
console.log("[Callix Dialer] 📴 Logout: Chamando goOffline()...");
|
|
60082
61092
|
cleanupRef.current.goOffline();
|
|
60083
61093
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -60104,7 +61114,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60104
61114
|
console.warn("[Callix Dialer] Não foi possível limpar sessão:", err);
|
|
60105
61115
|
}
|
|
60106
61116
|
}
|
|
60107
|
-
}, [username, presetUsername]);
|
|
61117
|
+
}, [username, presetUsername, options.engineUrl]);
|
|
60108
61118
|
const pendingAutoLogin = autoLogin && Boolean(username) && !isAuthenticated && !error2 && !autoLoginAttemptedRef.current;
|
|
60109
61119
|
const loginConfirmation = useMemo(() => {
|
|
60110
61120
|
if (isAuthenticated || isLoading || isRestoringSession || isDisconnecting || !username || pendingAutoLogin) {
|
|
@@ -60143,7 +61153,7 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60143
61153
|
}
|
|
60144
61154
|
) });
|
|
60145
61155
|
}
|
|
60146
|
-
if (!isAuthenticated || !sessionToken || !callixDomain) {
|
|
61156
|
+
if (!isAuthenticated || !engineMode && (!sessionToken || !callixDomain)) {
|
|
60147
61157
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
60148
61158
|
DialerComponent,
|
|
60149
61159
|
{
|
|
@@ -60163,6 +61173,38 @@ function DialerWrapper({ options, containerEl }) {
|
|
|
60163
61173
|
}
|
|
60164
61174
|
) });
|
|
60165
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
|
+
}
|
|
60166
61208
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
60167
61209
|
clientSdkReactExports.CallixClientProvider,
|
|
60168
61210
|
{
|