callix-dialer-widget 2.8.4 → 2.8.5

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.
@@ -43754,6 +43754,7 @@ const INITIAL_TRANSCRIPT = { segments: [], status: null, insight: null, interims
43754
43754
  const MAX_SEGMENTS = 300;
43755
43755
  const RECONNECT_MIN_MS = 500;
43756
43756
  const RECONNECT_MAX_MS = 5e3;
43757
+ const ENGINE_VIEW_TIMEOUT_MS = 15e3;
43757
43758
  const PENDING_COMMANDS_MAX = 10;
43758
43759
  const PENDING_COMMANDS_TTL_MS = 15e3;
43759
43760
  function engineWsUrl(baseUrl) {
@@ -43775,6 +43776,9 @@ class EngineClient {
43775
43776
  this.reconnectDelay = RECONNECT_MIN_MS;
43776
43777
  this.pendingCommands = [];
43777
43778
  this.reconnectTimer = null;
43779
+ this.viewTimer = null;
43780
+ this.lastSnapshotAt = null;
43781
+ this.connectionError = null;
43778
43782
  this.closed = false;
43779
43783
  this.iceServers = [{ urls: "stun:stun.l.google.com:19302" }];
43780
43784
  this.pc = null;
@@ -43828,9 +43832,10 @@ class EngineClient {
43828
43832
  if (this.closed) this.closed = false;
43829
43833
  if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) return;
43830
43834
  const Impl = this.options.WebSocketImpl ?? WebSocket;
43831
- this.setState({ wsState: "connecting" });
43835
+ this.setState({ wsState: "connecting", tabId: null, view: null });
43832
43836
  const ws = new Impl(engineWsUrl(this.options.url));
43833
43837
  this.ws = ws;
43838
+ this.watchForView(ws);
43834
43839
  ws.onopen = () => {
43835
43840
  var _a2, _b;
43836
43841
  if (this.ws !== ws) return;
@@ -43840,6 +43845,7 @@ class EngineClient {
43840
43845
  }
43841
43846
  this.reconnectDelay = RECONNECT_MIN_MS;
43842
43847
  this.setState({ wsState: "open" });
43848
+ this.watchForView(ws);
43843
43849
  const sessionToken = !this.session && this.options.sessionToken ? this.options.sessionToken() : null;
43844
43850
  const hello = {
43845
43851
  type: "hello",
@@ -43865,24 +43871,14 @@ class EngineClient {
43865
43871
  this.handle(message);
43866
43872
  };
43867
43873
  ws.onclose = () => {
43868
- if (this.ws !== ws) return;
43869
- this.ws = null;
43870
- const wasEngaged = this.state.engaged;
43871
- this.setState({ wsState: "closed", tabId: null, engaged: false });
43872
- if (wasEngaged) this.stopAudio();
43873
- if (this.closed) return;
43874
- this.log(`conexão com o motor caiu; nova tentativa em ${this.reconnectDelay} ms`);
43875
- this.reconnectTimer = setTimeout(() => {
43876
- this.reconnectTimer = null;
43877
- this.connect();
43878
- }, this.reconnectDelay);
43879
- this.reconnectDelay = Math.min(this.reconnectDelay * 2, RECONNECT_MAX_MS);
43874
+ this.disconnected(ws, "Conexão com o motor interrompida. Reconectando...");
43880
43875
  };
43881
43876
  ws.onerror = () => void 0;
43882
43877
  }
43883
43878
  /** fecha de vez (sem reconectar) e solta o áudio */
43884
43879
  close() {
43885
43880
  this.closed = true;
43881
+ this.clearViewTimer();
43886
43882
  if (this.reconnectTimer) {
43887
43883
  clearTimeout(this.reconnectTimer);
43888
43884
  this.reconnectTimer = null;
@@ -43896,7 +43892,39 @@ class EngineClient {
43896
43892
  }
43897
43893
  this.pendingCommands = [];
43898
43894
  this.rejectWaiters(new Error("a conexão com o motor foi encerrada"));
43899
- this.setState({ wsState: "closed", tabId: null, engaged: false });
43895
+ this.userSignal.set(void 0);
43896
+ this.setState({ wsState: "closed", tabId: null, view: null, engaged: false });
43897
+ }
43898
+ clearViewTimer() {
43899
+ if (this.viewTimer) clearTimeout(this.viewTimer);
43900
+ this.viewTimer = null;
43901
+ }
43902
+ watchForView(ws) {
43903
+ var _a2, _b;
43904
+ this.clearViewTimer();
43905
+ this.viewTimer = setTimeout(() => {
43906
+ if (this.ws !== ws) return;
43907
+ this.disconnected(ws, "O motor parou de atualizar o estado. Reconectando...");
43908
+ ws.close();
43909
+ }, ENGINE_VIEW_TIMEOUT_MS);
43910
+ (_b = (_a2 = this.viewTimer).unref) == null ? void 0 : _b.call(_a2);
43911
+ }
43912
+ disconnected(ws, message) {
43913
+ if (this.ws !== ws) return;
43914
+ this.ws = null;
43915
+ this.clearViewTimer();
43916
+ this.userSignal.set(void 0);
43917
+ this.connectionError = message;
43918
+ this.setState({ wsState: "closed", tabId: null, view: null, engaged: false, lastError: message });
43919
+ this.stopAudio();
43920
+ if (this.closed) return;
43921
+ this.log(`conexão com o motor caiu; nova tentativa em ${this.reconnectDelay} ms`);
43922
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
43923
+ this.reconnectTimer = setTimeout(() => {
43924
+ this.reconnectTimer = null;
43925
+ this.connect();
43926
+ }, this.reconnectDelay);
43927
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, RECONNECT_MAX_MS);
43900
43928
  }
43901
43929
  /**
43902
43930
  * A view atual, ou a próxima que o host mandar (welcome/view). Com `accept`, espera uma
@@ -43906,7 +43934,7 @@ class EngineClient {
43906
43934
  waitForView(timeoutMs = 15e3, accept, options) {
43907
43935
  const rejected = this.state.rejected;
43908
43936
  if (rejected) return Promise.reject(new EngineAccessError(rejected.code, rejected.message));
43909
- if (!(options == null ? void 0 : options.fresh) && this.state.view && this.state.tabId && (!accept || accept(this.state.view))) return Promise.resolve(this.state.view);
43937
+ if (!(options == null ? void 0 : options.fresh) && this.state.wsState === "open" && this.state.view && this.state.tabId && (!accept || accept(this.state.view))) return Promise.resolve(this.state.view);
43910
43938
  return new Promise((resolve, reject) => {
43911
43939
  const timer = setTimeout(() => {
43912
43940
  const index = this.viewWaiters.findIndex((w) => w.timer === timer);
@@ -43930,6 +43958,7 @@ class EngineClient {
43930
43958
  case "welcome": {
43931
43959
  if (Array.isArray(message.iceServers) && message.iceServers.length > 0) this.iceServers = message.iceServers;
43932
43960
  this.setState({ tabId: message.tabId, rejected: null });
43961
+ if (this.ws) this.watchForView(this.ws);
43933
43962
  this.flushPendingCommands();
43934
43963
  this.applyView(message.view);
43935
43964
  this.log(`sou a aba ${message.tabId}`);
@@ -43987,21 +44016,34 @@ class EngineClient {
43987
44016
  }
43988
44017
  }
43989
44018
  applyView(view) {
43990
- var _a2, _b;
44019
+ var _a2, _b, _c;
43991
44020
  const previous = this.state.view;
44021
+ const snapshotAt = (_a2 = view.snapshot) == null ? void 0 : _a2.at;
44022
+ if (view.engine === "stopped" || view.engine === "crashed") {
44023
+ this.clearViewTimer();
44024
+ } else if (this.ws && (snapshotAt !== void 0 && snapshotAt !== this.lastSnapshotAt || (previous == null ? void 0 : previous.engine) === "stopped" || (previous == null ? void 0 : previous.engine) === "crashed")) {
44025
+ this.watchForView(this.ws);
44026
+ }
44027
+ if (snapshotAt !== void 0) this.lastSnapshotAt = snapshotAt;
43992
44028
  const lost = view.sessionLost ?? null;
43993
44029
  const newlyLost = Boolean(lost) && !(previous == null ? void 0 : previous.sessionLost);
43994
44030
  if (newlyLost) {
43995
44031
  this.session = null;
43996
44032
  this.log(`o Callix derrubou a sessão: ${(lost == null ? void 0 : lost.message) ?? ""}`);
43997
44033
  }
43998
- const user = (_a2 = view.snapshot) == null ? void 0 : _a2.leg.user;
44034
+ const user = (_b = view.snapshot) == null ? void 0 : _b.leg.user;
43999
44035
  this.userSignal.set(user ? { ...user, sessionId: 0 } : void 0);
44000
- const call = (_b = view.snapshot) == null ? void 0 : _b.leg.call;
44036
+ const call = (_c = view.snapshot) == null ? void 0 : _c.leg.call;
44001
44037
  const patch = { view, sessionLost: lost ? { originator: lost.originator, message: lost.message } : null };
44038
+ if (this.connectionError && this.state.lastError === this.connectionError) patch.lastError = null;
44039
+ this.connectionError = null;
44002
44040
  if (call && typeof call.muted === "boolean" && call.muted !== this.state.inputMuted && call.state !== "ended") patch.inputMuted = call.muted;
44003
44041
  this.setState(patch);
44004
44042
  for (const waiter of this.viewWaiters.splice(0)) {
44043
+ if (this.state.wsState !== "open" || !this.state.tabId) {
44044
+ this.viewWaiters.push(waiter);
44045
+ continue;
44046
+ }
44005
44047
  if (waiter.accept && !waiter.accept(view)) {
44006
44048
  this.viewWaiters.push(waiter);
44007
44049
  continue;
@@ -44087,6 +44129,7 @@ class EngineClient {
44087
44129
  }
44088
44130
  command(command) {
44089
44131
  if (this.ws && this.ws.readyState === WebSocket.OPEN && this.state.tabId) {
44132
+ this.setState({ lastError: null });
44090
44133
  this.send({ type: "command", command });
44091
44134
  this.log(`comando: ${command.name}${command.phone ? ` ${command.phone}` : ""}`);
44092
44135
  return;
@@ -44094,7 +44137,8 @@ class EngineClient {
44094
44137
  this.pendingCommands.push({ command, at: Date.now() });
44095
44138
  if (this.pendingCommands.length > PENDING_COMMANDS_MAX) this.pendingCommands.shift();
44096
44139
  this.log(`comando: ${command.name} guardado até a conexão com o motor voltar`);
44097
- this.setState({ lastError: "Sem conexão com o motor neste momento; o comando será enviado assim que ela voltar." });
44140
+ this.connectionError = "Sem conexão com o motor neste momento; o comando será enviado assim que ela voltar.";
44141
+ this.setState({ lastError: this.connectionError });
44098
44142
  this.connect();
44099
44143
  }
44100
44144
  /** manda os comandos guardados que ainda fazem sentido (os velhos poderiam atingir outra chamada) */
@@ -44356,13 +44400,13 @@ class EngineCall {
44356
44400
  return Math.max(0, Math.floor((Date.now() - from) / 1e3));
44357
44401
  }
44358
44402
  answer() {
44359
- this.client.command({ name: "answer" });
44403
+ engineCommand(this.client, { name: "answer" });
44360
44404
  }
44361
44405
  reject() {
44362
- this.client.command({ name: "reject" });
44406
+ engineCommand(this.client, { name: "reject" });
44363
44407
  }
44364
44408
  hangup() {
44365
- this.client.command({ name: "hangup" });
44409
+ engineCommand(this.client, { name: "hangup" });
44366
44410
  }
44367
44411
  isOnHold() {
44368
44412
  return { local: false, remote: false };
@@ -44381,7 +44425,7 @@ class EngineCall {
44381
44425
  this.client.setInputMuted(false);
44382
44426
  }
44383
44427
  sendDtmf(tones) {
44384
- this.client.command({ name: "dtmf", tones });
44428
+ engineCommand(this.client, { name: "dtmf", tones });
44385
44429
  }
44386
44430
  on(event, listener) {
44387
44431
  let set = this.listeners.get(event);
@@ -44417,10 +44461,12 @@ class EngineCall {
44417
44461
  }
44418
44462
  }
44419
44463
  function operatorStateFromView(view, call) {
44420
- var _a2, _b, _c;
44464
+ var _a2, _b, _c, _d, _e;
44421
44465
  if (!view) return void 0;
44422
- if (((_a2 = view.sessionLost) == null ? void 0 : _a2.originator) === "anotherDevice") return { state: "startupError", error: "userIsAlreadyConnectedOnAnotherDevice" };
44423
- const leg = (_b = view.snapshot) == null ? void 0 : _b.leg;
44466
+ if (view.logoffError || ((_a2 = view.snapshot) == null ? void 0 : _a2.leg.logoffError)) return { state: "startupError", error: "unknownError" };
44467
+ if (view.loggingOff || ((_b = view.snapshot) == null ? void 0 : _b.leg.loggingOff)) return { state: "starting" };
44468
+ if (((_c = view.sessionLost) == null ? void 0 : _c.originator) === "anotherDevice") return { state: "startupError", error: "userIsAlreadyConnectedOnAnotherDevice" };
44469
+ const leg = (_d = view.snapshot) == null ? void 0 : _d.leg;
44424
44470
  if (!leg) {
44425
44471
  if (view.engine === "starting") return { state: "starting" };
44426
44472
  if (view.engine === "running") return { state: "starting" };
@@ -44435,7 +44481,7 @@ function operatorStateFromView(view, call) {
44435
44481
  case "ringing":
44436
44482
  return { state: "callRinging", call: asCall };
44437
44483
  case "dialing":
44438
- return ((_c = leg.call) == null ? void 0 : _c.state) === "ringing" ? { state: "manualCallRinging", call: asCall } : { state: "manualCallSetup", call: asCall };
44484
+ return ((_e = leg.call) == null ? void 0 : _e.state) === "ringing" ? { state: "manualCallRinging", call: asCall } : { state: "manualCallSetup", call: asCall };
44439
44485
  case "inCall":
44440
44486
  return { state: "callInProgress", call: asCall };
44441
44487
  case "afterCall":
@@ -44448,6 +44494,30 @@ function operatorStateFromView(view, call) {
44448
44494
  return { state: "startupError", error: "unknownError" };
44449
44495
  }
44450
44496
  }
44497
+ function operatorStateFromClientState(state, call) {
44498
+ if (state.wsState !== "open" || !state.tabId || !state.view) return { state: "starting" };
44499
+ if (state.rejected) return { state: "startupError", error: "unknownError" };
44500
+ return operatorStateFromView(state.view, call);
44501
+ }
44502
+ function assertEngineCommand(client2, allowDisconnected = false) {
44503
+ var _a2, _b, _c, _d;
44504
+ const state = client2.getState();
44505
+ const view = state.view;
44506
+ const error2 = ((_a2 = state.rejected) == null ? void 0 : _a2.message) ?? ((_b = state.sessionLost) == null ? void 0 : _b.message) ?? (view == null ? void 0 : view.logoffError) ?? ((_c = view == null ? void 0 : view.snapshot) == null ? void 0 : _c.leg.logoffError);
44507
+ if (error2) throw new Error(error2);
44508
+ if ((view == null ? void 0 : view.loggingOff) || ((_d = view == null ? void 0 : view.snapshot) == null ? void 0 : _d.leg.loggingOff)) throw new Error("Aguarde o encerramento da sessão antes de usar o discador.");
44509
+ if (state.wsState !== "open" || !state.tabId || !view) {
44510
+ if (allowDisconnected) return;
44511
+ throw new Error("Sem conexão com o motor. Aguarde a reconexão antes de usar o discador.");
44512
+ }
44513
+ if (view.engine !== "running" || !view.snapshot || view.snapshot.leg.status === "offline" || view.snapshot.leg.status === "error" || view.snapshot.leg.status === "starting") {
44514
+ throw new Error("O atendimento ainda não está conectado ao Callix. Aguarde antes de usar o discador.");
44515
+ }
44516
+ }
44517
+ function engineCommand(client2, command) {
44518
+ assertEngineCommand(client2, command.name === "hangup");
44519
+ client2.command(command);
44520
+ }
44451
44521
  function callInfoFromView(view) {
44452
44522
  var _a2;
44453
44523
  const leg = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg;
@@ -44479,9 +44549,9 @@ function callInfoFromView(view) {
44479
44549
  return { type: "manual", info: { successQualifications, discardQualifications } };
44480
44550
  }
44481
44551
  function useEngineCall(client2) {
44482
- var _a2, _b;
44552
+ var _a2, _b, _c, _d, _e, _f, _g, _h;
44483
44553
  const state = useEngineState(client2);
44484
- const snapshot = ((_b = (_a2 = state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.call) ?? null;
44554
+ const snapshot = state.wsState === "open" && state.tabId && !((_a2 = state.view) == null ? void 0 : _a2.logoffError) && !((_b = state.view) == null ? void 0 : _b.loggingOff) && !((_d = (_c = state.view) == null ? void 0 : _c.snapshot) == null ? void 0 : _d.leg.logoffError) && !((_f = (_e = state.view) == null ? void 0 : _e.snapshot) == null ? void 0 : _f.leg.loggingOff) ? ((_h = (_g = state.view) == null ? void 0 : _g.snapshot) == null ? void 0 : _h.leg.call) ?? null : null;
44485
44555
  const ref = useRef(null);
44486
44556
  if (!snapshot || snapshot.state === "ended") {
44487
44557
  ref.current = null;
@@ -44495,7 +44565,7 @@ function useEngineCall(client2) {
44495
44565
  function useEngineOperatorState(client2) {
44496
44566
  const state = useEngineState(client2);
44497
44567
  const call = useEngineCall(client2);
44498
- return useMemo(() => operatorStateFromView(state.view, call), [state.view, call]);
44568
+ return useMemo(() => operatorStateFromClientState(state, call), [state, call]);
44499
44569
  }
44500
44570
  function useEngineCurrentCall(client2) {
44501
44571
  const call = useEngineCall(client2);
@@ -44520,15 +44590,16 @@ function useEngineControls(client2) {
44520
44590
  () => ({
44521
44591
  becomeAvailable: () => {
44522
44592
  var _a2, _b;
44593
+ assertEngineCommand(client2);
44523
44594
  const status = (_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status;
44524
- if (status === "onBreak") client2.command({ name: "resume" });
44595
+ if (status === "onBreak") engineCommand(client2, { name: "resume" });
44525
44596
  },
44526
44597
  goOffline: () => void 0,
44527
- enterOnBreak: (breakId) => client2.command(pauseCommand(breakId)),
44528
- makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
44598
+ enterOnBreak: (breakId) => engineCommand(client2, pauseCommand(breakId)),
44599
+ makeManualCall: (phoneNumber) => engineCommand(client2, { name: "dial", phone: phoneNumber }),
44529
44600
  finishAfterCall: (data) => {
44530
- if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
44531
- else client2.command({ name: "finish" });
44601
+ if (typeof data.qualificationId === "number") engineCommand(client2, { name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
44602
+ else engineCommand(client2, { name: "finish" });
44532
44603
  }
44533
44604
  }),
44534
44605
  [client2]
@@ -44537,9 +44608,9 @@ function useEngineControls(client2) {
44537
44608
  function useEngineCurrentCallControls(client2) {
44538
44609
  return useMemo(
44539
44610
  () => ({
44540
- answer: () => client2.command({ name: "answer" }),
44541
- reject: () => client2.command({ name: "reject" }),
44542
- hangup: () => client2.command({ name: "hangup" }),
44611
+ answer: () => engineCommand(client2, { name: "answer" }),
44612
+ reject: () => engineCommand(client2, { name: "reject" }),
44613
+ hangup: () => engineCommand(client2, { name: "hangup" }),
44543
44614
  isOnHold: () => ({ local: false, remote: false }),
44544
44615
  hold: async () => void 0,
44545
44616
  unhold: async () => void 0,
@@ -44592,13 +44663,14 @@ function engineOperator(client2) {
44592
44663
  goOffline: () => void 0,
44593
44664
  becomeAvailable: () => {
44594
44665
  var _a2, _b;
44595
- if (((_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status) === "onBreak") client2.command({ name: "resume" });
44666
+ assertEngineCommand(client2);
44667
+ if (((_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status) === "onBreak") engineCommand(client2, { name: "resume" });
44596
44668
  },
44597
- enterOnBreak: (breakId) => client2.command(pauseCommand(breakId)),
44598
- makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
44669
+ enterOnBreak: (breakId) => engineCommand(client2, pauseCommand(breakId)),
44670
+ makeManualCall: (phoneNumber) => engineCommand(client2, { name: "dial", phone: phoneNumber }),
44599
44671
  finishAfterCall: (data) => {
44600
- if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
44601
- else client2.command({ name: "finish" });
44672
+ if (typeof data.qualificationId === "number") engineCommand(client2, { name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
44673
+ else engineCommand(client2, { name: "finish" });
44602
44674
  }
44603
44675
  };
44604
44676
  operators.set(client2, operator);
@@ -57872,8 +57944,14 @@ function LiveCaptionPanelInner({ client: client2, maxLines }) {
57872
57944
  ] });
57873
57945
  }
57874
57946
  function useDialerNotices(options = {}) {
57947
+ var _a2, _b, _c;
57875
57948
  const operatorState = useCallOperatorState();
57876
57949
  const currentCall = useCallOperatorCurrentCall();
57950
+ const engine = useEngineClient();
57951
+ const subscribeEngine = useCallback((listener) => engine ? engine.subscribe(listener) : () => {
57952
+ }, [engine]);
57953
+ const readEngine = useCallback(() => (engine == null ? void 0 : engine.getState()) ?? null, [engine]);
57954
+ const engineState = useSyncExternalStore(subscribeEngine, readEngine, readEngine);
57877
57955
  const [deviceIssue, setDeviceIssue] = useState(null);
57878
57956
  const [sessionDrop, setSessionDrop] = useState(null);
57879
57957
  const [callFailure, setCallFailure] = useState(null);
@@ -57886,7 +57964,7 @@ function useDialerNotices(options = {}) {
57886
57964
  setDeviceIssue(issue);
57887
57965
  }, [options.checkDevices]);
57888
57966
  useEffect(() => {
57889
- var _a2;
57967
+ var _a3;
57890
57968
  if (options.checkDevices === false) return;
57891
57969
  let cancelled = false;
57892
57970
  const run = async () => {
@@ -57896,11 +57974,11 @@ function useDialerNotices(options = {}) {
57896
57974
  void run();
57897
57975
  const mediaDevices = typeof navigator !== "undefined" ? navigator.mediaDevices : void 0;
57898
57976
  const handler = () => void run();
57899
- (_a2 = mediaDevices == null ? void 0 : mediaDevices.addEventListener) == null ? void 0 : _a2.call(mediaDevices, "devicechange", handler);
57977
+ (_a3 = mediaDevices == null ? void 0 : mediaDevices.addEventListener) == null ? void 0 : _a3.call(mediaDevices, "devicechange", handler);
57900
57978
  return () => {
57901
- var _a3;
57979
+ var _a4;
57902
57980
  cancelled = true;
57903
- (_a3 = mediaDevices == null ? void 0 : mediaDevices.removeEventListener) == null ? void 0 : _a3.call(mediaDevices, "devicechange", handler);
57981
+ (_a4 = mediaDevices == null ? void 0 : mediaDevices.removeEventListener) == null ? void 0 : _a4.call(mediaDevices, "devicechange", handler);
57904
57982
  };
57905
57983
  }, [options.checkDevices]);
57906
57984
  useCallOperatorEventListener("userSessionDropped", (data) => {
@@ -57909,8 +57987,8 @@ function useDialerNotices(options = {}) {
57909
57987
  useEffect(() => {
57910
57988
  if (!(currentCall == null ? void 0 : currentCall.on)) return;
57911
57989
  const handleFailed = (event) => {
57912
- var _a2;
57913
- const failure = (event == null ? void 0 : event.failure) ?? ((_a2 = currentCall.toCallData) == null ? void 0 : _a2.call(currentCall).failure) ?? null;
57990
+ var _a3;
57991
+ const failure = (event == null ? void 0 : event.failure) ?? ((_a3 = currentCall.toCallData) == null ? void 0 : _a3.call(currentCall).failure) ?? null;
57914
57992
  setCallFailure(failure ?? { cause: null });
57915
57993
  setDismissedCode(null);
57916
57994
  if (failureTimer.current) clearTimeout(failureTimer.current);
@@ -57918,14 +57996,14 @@ function useDialerNotices(options = {}) {
57918
57996
  };
57919
57997
  currentCall.on("failed", handleFailed);
57920
57998
  return () => {
57921
- var _a2;
57922
- (_a2 = currentCall.off) == null ? void 0 : _a2.call(currentCall, "failed", handleFailed);
57999
+ var _a3;
58000
+ (_a3 = currentCall.off) == null ? void 0 : _a3.call(currentCall, "failed", handleFailed);
57923
58001
  };
57924
58002
  }, [currentCall, ttl]);
57925
58003
  useEffect(() => {
57926
- var _a2, _b;
58004
+ var _a3, _b2;
57927
58005
  if ((operatorState == null ? void 0 : operatorState.state) !== "manualCallFailed") return;
57928
- const failure = ((_b = (_a2 = operatorState.call) == null ? void 0 : _a2.toCallData) == null ? void 0 : _b.call(_a2).failure) ?? null;
58006
+ const failure = ((_b2 = (_a3 = operatorState.call) == null ? void 0 : _a3.toCallData) == null ? void 0 : _b2.call(_a3).failure) ?? null;
57929
58007
  setCallFailure((current) => current ?? failure ?? { cause: null });
57930
58008
  setDismissedCode(null);
57931
58009
  }, [operatorState]);
@@ -57938,13 +58016,26 @@ function useDialerNotices(options = {}) {
57938
58016
  useEffect(() => () => {
57939
58017
  if (failureTimer.current) clearTimeout(failureTimer.current);
57940
58018
  }, []);
57941
- const notice = pickNotice({
58019
+ let notice = pickNotice({
57942
58020
  deviceIssue,
57943
58021
  operatorState: (operatorState == null ? void 0 : operatorState.state) ?? null,
57944
58022
  startupError: (operatorState == null ? void 0 : operatorState.state) === "startupError" ? operatorState.error ?? null : null,
57945
58023
  sessionDrop,
57946
58024
  callFailure
57947
58025
  });
58026
+ const logoffError = ((_a2 = engineState == null ? void 0 : engineState.view) == null ? void 0 : _a2.logoffError) ?? ((_c = (_b = engineState == null ? void 0 : engineState.view) == null ? void 0 : _b.snapshot) == null ? void 0 : _c.leg.logoffError);
58027
+ const reconnecting = Boolean(engineState && (engineState.wsState !== "open" || !engineState.tabId));
58028
+ const engineError = logoffError ?? (engineState == null ? void 0 : engineState.lastError);
58029
+ if (engineState && !sessionDrop && (engineError || reconnecting)) {
58030
+ notice = {
58031
+ level: "error",
58032
+ source: "offline",
58033
+ title: logoffError ? "Saída não concluída" : reconnecting ? "Reconectando ao discador" : "Comando não concluído",
58034
+ detail: engineError || "Aguardando a conexão e o estado atual do motor.",
58035
+ action: logoffError ? "Clique em Sair novamente para concluir o encerramento da sessão." : reconnecting ? "Aguarde a conexão voltar antes de ligar." : "Confira o estado do discador e tente novamente.",
58036
+ code: logoffError ? "engine:logoff" : reconnecting ? "engine:reconnecting" : "engine:command"
58037
+ };
58038
+ }
57948
58039
  const visible = notice && notice.code !== dismissedCode ? notice : null;
57949
58040
  const dismiss = useCallback(() => {
57950
58041
  if (!notice) return;
@@ -58778,7 +58869,7 @@ function AuthenticatedMobileDialer({
58778
58869
  showClientInfoModal = false,
58779
58870
  density
58780
58871
  }) {
58781
- var _a2, _b, _c, _d, _e, _f;
58872
+ var _a2, _b, _c, _d, _e;
58782
58873
  const { skipManualQualification } = useDialerConfig();
58783
58874
  const [dialValue, setDialValue] = useState("");
58784
58875
  const [activeScreen, setActiveScreen] = useState("dialer");
@@ -58792,7 +58883,6 @@ function AuthenticatedMobileDialer({
58792
58883
  const [isClientInfoModalOpen, setIsClientInfoModalOpen] = useState(false);
58793
58884
  const [isDebugPanelOpen, setIsDebugPanelOpen] = useState(false);
58794
58885
  useDebugLogger();
58795
- const [currentBreakName, setCurrentBreakName] = useState(null);
58796
58886
  const [pauseStartTime, setPauseStartTime] = useState(null);
58797
58887
  const [pauseDurationSeconds, setPauseDurationSeconds] = useState(0);
58798
58888
  const [contactData, setContactData] = useState({
@@ -58828,6 +58918,10 @@ function AuthenticatedMobileDialer({
58828
58918
  const breaks = useSignal(operator == null ? void 0 : operator.breaks) || [];
58829
58919
  const userData = useSignal(operator == null ? void 0 : operator.user);
58830
58920
  const isOnBreak = (operatorState == null ? void 0 : operatorState.state) === "onBreak";
58921
+ const activeBreak = (operatorState == null ? void 0 : operatorState.state) === "onBreak" ? operatorState.break : void 0;
58922
+ const activeBreakId = activeBreak == null ? void 0 : activeBreak.id;
58923
+ const hasConfirmedPauseState = isOnBreak || ["idle", "manualCallSetup", "manualCallRinging", "manualCallFailed", "callRinging", "callInProgress", "afterCall"].includes((operatorState == null ? void 0 : operatorState.state) ?? "");
58924
+ const previousBreakRef = useRef({ isOnBreak, id: activeBreakId, confirmed: hasConfirmedPauseState });
58831
58925
  const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
58832
58926
  const haptic = useHaptic();
58833
58927
  const dtmf = useDTMF({ enabled: true });
@@ -58927,8 +59021,12 @@ function AuthenticatedMobileDialer({
58927
59021
  const INITIAL_BREAK_ID = 5;
58928
59022
  const INITIAL_BREAK_NAME = "Reunião";
58929
59023
  const callState = operatorState == null ? void 0 : operatorState.state;
58930
- const hasEnteredInitialBreakRef = useRef(false);
59024
+ const hasEnteredInitialBreakRef = useRef(isOnBreak);
58931
59025
  useEffect(() => {
59026
+ if (isOnBreak) {
59027
+ hasEnteredInitialBreakRef.current = true;
59028
+ return;
59029
+ }
58932
59030
  if (breaks.length === 0 || callState !== "idle" || hasEnteredInitialBreakRef.current || isOnBreak) {
58933
59031
  return;
58934
59032
  }
@@ -58941,10 +59039,8 @@ function AuthenticatedMobileDialer({
58941
59039
  );
58942
59040
  if (reuniaoBreak) {
58943
59041
  console.log("[MobileDialer] Operador conectado (idle), entrando em pausa inicial:", reuniaoBreak.name);
58944
- Promise.resolve(goOnBreak(reuniaoBreak.id)).then(() => {
58945
- console.log("[MobileDialer] Pausa inicial aplicada com sucesso:", reuniaoBreak.name);
58946
- setCurrentBreakName(reuniaoBreak.name);
58947
- setPauseStartTime(Date.now());
59042
+ Promise.resolve().then(() => goOnBreak(reuniaoBreak.id)).then(() => {
59043
+ console.log("[MobileDialer] Pausa inicial solicitada:", reuniaoBreak.name);
58948
59044
  }).catch((err) => {
58949
59045
  console.error("[MobileDialer] Falha ao entrar em pausa:", err);
58950
59046
  hasEnteredInitialBreakRef.current = false;
@@ -58952,10 +59048,8 @@ function AuthenticatedMobileDialer({
58952
59048
  } else {
58953
59049
  const firstBreak = breaks[0];
58954
59050
  console.log('[MobileDialer] Pausa "Reunião" não encontrada, usando:', firstBreak.name);
58955
- Promise.resolve(goOnBreak(firstBreak.id)).then(() => {
58956
- console.log("[MobileDialer] Pausa inicial aplicada com sucesso:", firstBreak.name);
58957
- setCurrentBreakName(firstBreak.name);
58958
- setPauseStartTime(Date.now());
59051
+ Promise.resolve().then(() => goOnBreak(firstBreak.id)).then(() => {
59052
+ console.log("[MobileDialer] Pausa inicial solicitada:", firstBreak.name);
58959
59053
  }).catch((err) => {
58960
59054
  console.error("[MobileDialer] Falha ao entrar em pausa:", err);
58961
59055
  hasEnteredInitialBreakRef.current = false;
@@ -59037,7 +59131,8 @@ function AuthenticatedMobileDialer({
59037
59131
  const { notice: dialerNotice, dismiss: dismissDialerNotice, recheckDevices } = useDialerNotices();
59038
59132
  const [noticeHeight, setNoticeHeight] = useState(0);
59039
59133
  const isError = callStateStr === "startupError" || callStateStr === "offline" || (dialerNotice == null ? void 0 : dialerNotice.source) === "device" || (dialerNotice == null ? void 0 : dialerNotice.source) === "session";
59040
- const isIdle = !callStateStr || callStateStr === "starting" || callStateStr === "idle" || callStateStr === "manualCallSetup" || callStateStr === "onBreak";
59134
+ const isReconnecting = !callStateStr || callStateStr === "starting";
59135
+ const isIdle = callStateStr === "idle" || callStateStr === "onBreak";
59041
59136
  const isConnecting = callState === "manualCallSetup" || callState === "manualCallRinging" || callState === "callRinging";
59042
59137
  const isInCall = callState === "callInProgress";
59043
59138
  const isAfterCall = callState === "afterCall";
@@ -59070,22 +59165,32 @@ function AuthenticatedMobileDialer({
59070
59165
  return () => clearInterval(interval);
59071
59166
  }, [isInCall]);
59072
59167
  useEffect(() => {
59073
- if (!isOnBreak || !pauseStartTime) {
59168
+ const previous = previousBreakRef.current;
59169
+ if (!hasConfirmedPauseState) {
59170
+ previousBreakRef.current = { ...previous, confirmed: false };
59074
59171
  return;
59075
59172
  }
59076
- const interval = setInterval(() => {
59077
- const elapsed = Math.floor((Date.now() - pauseStartTime) / 1e3);
59078
- setPauseDurationSeconds(elapsed);
59079
- }, 1e3);
59080
- return () => clearInterval(interval);
59081
- }, [isOnBreak, pauseStartTime]);
59082
- useEffect(() => {
59083
- if (!isOnBreak && currentBreakName) {
59084
- setCurrentBreakName(null);
59173
+ previousBreakRef.current = { isOnBreak, id: activeBreakId, confirmed: true };
59174
+ if (!isOnBreak) {
59085
59175
  setPauseStartTime(null);
59086
59176
  setPauseDurationSeconds(0);
59177
+ } else if (previous.confirmed && !previous.isOnBreak || previous.isOnBreak && previous.id !== activeBreakId) {
59178
+ setPauseStartTime(Date.now());
59179
+ setPauseDurationSeconds(0);
59087
59180
  }
59088
- }, [isOnBreak, currentBreakName]);
59181
+ }, [isOnBreak, activeBreakId, hasConfirmedPauseState]);
59182
+ useEffect(() => {
59183
+ if (!isOnBreak || pauseStartTime === null) {
59184
+ return;
59185
+ }
59186
+ const update2 = () => {
59187
+ const elapsed = Math.max(0, Math.floor((Date.now() - pauseStartTime) / 1e3));
59188
+ setPauseDurationSeconds(elapsed);
59189
+ };
59190
+ update2();
59191
+ const interval = setInterval(update2, 1e3);
59192
+ return () => clearInterval(interval);
59193
+ }, [isOnBreak, pauseStartTime]);
59089
59194
  useEffect(() => {
59090
59195
  const onClickToCall = (event) => {
59091
59196
  const task = event.detail;
@@ -59211,6 +59316,7 @@ function AuthenticatedMobileDialer({
59211
59316
  setDialValue((prev) => prev.slice(0, -1));
59212
59317
  }, [haptic]);
59213
59318
  const handleCall = useCallback(async () => {
59319
+ if (!isIdle) return;
59214
59320
  console.log("[MobileDialer] handleCall triggered, dialValue:", dialValue);
59215
59321
  addLog("event", "Call", "📞 Iniciando chamada manual", { dialValue });
59216
59322
  const sanitized = sanitizeInput(dialValue);
@@ -59243,7 +59349,7 @@ function AuthenticatedMobileDialer({
59243
59349
  setCallError("Erro ao iniciar chamada.");
59244
59350
  haptic.notificationError();
59245
59351
  }
59246
- }, [dialValue, makeManualCall, sanitizeInput, haptic]);
59352
+ }, [dialValue, makeManualCall, sanitizeInput, haptic, isIdle]);
59247
59353
  const handleHangup = useCallback(async () => {
59248
59354
  if (!hangup) return;
59249
59355
  addLog("event", "Call", "📴 Encerrando chamada");
@@ -59415,28 +59521,21 @@ function AuthenticatedMobileDialer({
59415
59521
  async (breakId) => {
59416
59522
  if (!goOnBreak) return;
59417
59523
  try {
59418
- const selectedBreak = breaks.find((b) => b.id === breakId);
59419
59524
  await goOnBreak(breakId);
59420
59525
  haptic.notificationSuccess();
59421
- setCurrentBreakName((selectedBreak == null ? void 0 : selectedBreak.name) ?? "Em pausa");
59422
- setPauseStartTime(Date.now());
59423
- setPauseDurationSeconds(0);
59424
59526
  setIsPauseModalOpen(false);
59425
59527
  } catch (error2) {
59426
59528
  console.error("Erro ao entrar em pausa", error2);
59427
59529
  haptic.notificationError();
59428
59530
  }
59429
59531
  },
59430
- [goOnBreak, breaks, haptic]
59532
+ [goOnBreak, haptic]
59431
59533
  );
59432
59534
  const handleGoOffBreak = useCallback(async () => {
59433
59535
  if (!goOffBreak) return;
59434
59536
  try {
59435
59537
  await goOffBreak();
59436
59538
  haptic.notificationSuccess();
59437
- setCurrentBreakName(null);
59438
- setPauseStartTime(null);
59439
- setPauseDurationSeconds(0);
59440
59539
  setIsPauseModalOpen(false);
59441
59540
  } catch (error2) {
59442
59541
  console.error("Erro ao sair de pausa", error2);
@@ -59482,6 +59581,7 @@ function AuthenticatedMobileDialer({
59482
59581
  return activeCallNumber ? formatPhoneForDisplay(activeCallNumber) : "(--) ----- ----";
59483
59582
  }, [callInfo, formattedDial, activeCallNumber]);
59484
59583
  const callStatusLabel = useMemo(() => {
59584
+ if (isReconnecting) return "Conectando...";
59485
59585
  if (isConnecting) return "Ligando...";
59486
59586
  if (isInCall) return "Em chamada";
59487
59587
  if (isAfterCall) return "Encerrando...";
@@ -59489,19 +59589,22 @@ function AuthenticatedMobileDialer({
59489
59589
  if (isError) return (dialerNotice == null ? void 0 : dialerNotice.title) ?? "Erro de conexão";
59490
59590
  if (isOnBreak) return "Em pausa";
59491
59591
  return "Disponível";
59492
- }, [callStateStr, dialerNotice, isAfterCall, isConnecting, isError, isInCall, isOnBreak]);
59592
+ }, [callStateStr, dialerNotice, isAfterCall, isConnecting, isError, isInCall, isOnBreak, isReconnecting]);
59493
59593
  const statusBadgeClass = useMemo(() => {
59594
+ if (isReconnecting) return "connecting";
59494
59595
  if (isError) return "error";
59495
59596
  if (isOnBreak) return "paused";
59496
59597
  return "available";
59497
- }, [isError, isOnBreak]);
59498
- const activeBreakName = (operatorState == null ? void 0 : operatorState.state) === "onBreak" ? (_b = operatorState.break) == null ? void 0 : _b.name : void 0;
59598
+ }, [isError, isOnBreak, isReconnecting]);
59599
+ const activeBreakName = activeBreak == null ? void 0 : activeBreak.name;
59499
59600
  const statusBadgeText = useMemo(() => {
59601
+ if (isReconnecting) return "Conectando";
59500
59602
  if (isError) return (dialerNotice == null ? void 0 : dialerNotice.source) === "device" ? dialerNotice.title : "Erro";
59501
- if (isOnBreak) return currentBreakName || activeBreakName || "Em pausa";
59603
+ if (isOnBreak) return activeBreakName || "Em pausa";
59502
59604
  return "Disponível";
59503
- }, [dialerNotice, isError, isOnBreak, currentBreakName, activeBreakName]);
59605
+ }, [dialerNotice, isError, isOnBreak, activeBreakName, isReconnecting]);
59504
59606
  const pauseDurationLabel = useMemo(() => {
59607
+ if (pauseStartTime === null) return "--:--";
59505
59608
  const hours = Math.floor(pauseDurationSeconds / 3600);
59506
59609
  const minutes = Math.floor(pauseDurationSeconds % 3600 / 60).toString().padStart(2, "0");
59507
59610
  const seconds = (pauseDurationSeconds % 60).toString().padStart(2, "0");
@@ -59509,7 +59612,7 @@ function AuthenticatedMobileDialer({
59509
59612
  return `${hours}:${minutes}:${seconds}`;
59510
59613
  }
59511
59614
  return `${minutes}:${seconds}`;
59512
- }, [pauseDurationSeconds]);
59615
+ }, [pauseDurationSeconds, pauseStartTime]);
59513
59616
  const callDurationLabel = useMemo(() => {
59514
59617
  const m = Math.floor(callDurationSeconds / 60).toString().padStart(2, "0");
59515
59618
  const s = (callDurationSeconds % 60).toString().padStart(2, "0");
@@ -59550,7 +59653,7 @@ function AuthenticatedMobileDialer({
59550
59653
  isOnBreak ? /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "resume-btn", id: "resumeBtn", onClick: handleGoOffBreak, children: [
59551
59654
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-play" }),
59552
59655
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Voltar" })
59553
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "pause-btn", id: "pauseBtn", onClick: () => setIsPauseModalOpen(true), children: [
59656
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "pause-btn", id: "pauseBtn", disabled: isReconnecting || callStateStr === "offline" || callStateStr === "startupError", onClick: () => setIsPauseModalOpen(true), children: [
59554
59657
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-pause" }),
59555
59658
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Pausa" })
59556
59659
  ] }),
@@ -59638,15 +59741,15 @@ function AuthenticatedMobileDialer({
59638
59741
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-card-header", children: [
59639
59742
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-user-circle client-avatar" }),
59640
59743
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-main-info", children: [
59641
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-name", id: "clientName", children: findValue(contactData.finalData, ["DECISOR", "NOME", "CLIENTE", "PROSPECT"]) ?? ((_c = callInfo.info.campaignContact) == null ? void 0 : _c.label) ?? "--" }),
59642
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-company", id: "clientCompany", children: findValue(contactData.finalData, ["EMPRESA", "RAZAO SOCIAL", "RAZÃO SOCIAL", "RAZAO_SOCIAL"]) ?? ((_d = callInfo.info.campaign) == null ? void 0 : _d.name) ?? "--" })
59744
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-name", id: "clientName", children: findValue(contactData.finalData, ["DECISOR", "NOME", "CLIENTE", "PROSPECT"]) ?? ((_b = callInfo.info.campaignContact) == null ? void 0 : _b.label) ?? "--" }),
59745
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-company", id: "clientCompany", children: findValue(contactData.finalData, ["EMPRESA", "RAZAO SOCIAL", "RAZÃO SOCIAL", "RAZAO_SOCIAL"]) ?? ((_c = callInfo.info.campaign) == null ? void 0 : _c.name) ?? "--" })
59643
59746
  ] })
59644
59747
  ] }),
59645
59748
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "client-card-divider" }),
59646
59749
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-info-list", children: [
59647
59750
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-info-row", children: [
59648
59751
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-label", children: "Socio" }),
59649
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-value", children: findValue(contactData.finalData, ["SOCIO", "PROSPECT", "DECISOR", "NOME", "CLIENTE"]) ?? ((_e = callInfo.info.campaignContact) == null ? void 0 : _e.label) ?? "--" })
59752
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-value", children: findValue(contactData.finalData, ["SOCIO", "PROSPECT", "DECISOR", "NOME", "CLIENTE"]) ?? ((_d = callInfo.info.campaignContact) == null ? void 0 : _d.label) ?? "--" })
59650
59753
  ] }),
59651
59754
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "client-info-row", children: [
59652
59755
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "client-info-label", children: "Empresa" }),
@@ -59843,7 +59946,7 @@ function AuthenticatedMobileDialer({
59843
59946
  {
59844
59947
  isOpen: isClientInfoModalOpen,
59845
59948
  campaignCallInfo,
59846
- campaignContactPhoneId: ((_f = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _f.campaignContactPhoneId) ?? null,
59949
+ campaignContactPhoneId: ((_e = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _e.campaignContactPhoneId) ?? null,
59847
59950
  callDuration: callDurationSeconds,
59848
59951
  isCallActive: isInCall || isConnecting,
59849
59952
  onClose: () => setIsClientInfoModalOpen(false)
@@ -60878,7 +60981,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
60878
60981
  }, [enabled, pollIntervalMs, sessionToken]);
60879
60982
  return null;
60880
60983
  }
60881
- const WIDGET_VERSION = "2.8.4";
60984
+ const WIDGET_VERSION = "2.8.5";
60882
60985
  let currentSipWebSocket = null;
60883
60986
  function sendSipUnregisterIfPossible(ws) {
60884
60987
  if (ws.readyState !== WebSocket.OPEN) return;
@@ -61236,6 +61339,7 @@ function DialerWrapper({ options, containerEl }) {
61236
61339
  const [tabDecided, setTabDecided] = useState(false);
61237
61340
  const [tabNote, setTabNote] = useState(null);
61238
61341
  const [engineWaiting, setEngineWaiting] = useState(false);
61342
+ const [engineError, setEngineError] = useState(null);
61239
61343
  const busRef = useRef(null);
61240
61344
  const takeoverTimerRef = useRef(null);
61241
61345
  const isAuthenticatedRef = useRef(false);
@@ -61388,6 +61492,7 @@ function DialerWrapper({ options, containerEl }) {
61388
61492
  [options.domain, options.apiUrl]
61389
61493
  );
61390
61494
  const enterEngine = useCallback(async (user, domain, token) => {
61495
+ var _a2, _b, _c, _d;
61391
61496
  if (token) {
61392
61497
  const skip = await fetchSkipQualification(domain, token);
61393
61498
  if (skip !== null) {
@@ -61398,6 +61503,16 @@ function DialerWrapper({ options, containerEl }) {
61398
61503
  }
61399
61504
  }
61400
61505
  }
61506
+ const engineState = (_a2 = engineRef.current) == null ? void 0 : _a2.getState();
61507
+ if (!engineState || engineState.wsState !== "open" || !engineState.tabId || !engineState.view || engineState.rejected) {
61508
+ throw new Error("A conexão com o motor caiu. Aguarde a reconexão antes de entrar.");
61509
+ }
61510
+ if (engineState.view.engine !== "running" && engineState.view.engine !== "starting" || engineState.sessionLost || engineState.view.sessionLost) {
61511
+ throw new Error("A sessão do motor foi encerrada durante a entrada. Entre novamente.");
61512
+ }
61513
+ if (engineState.view.logoffError || engineState.view.loggingOff || ((_b = engineState.view.snapshot) == null ? void 0 : _b.leg.logoffError) || ((_c = engineState.view.snapshot) == null ? void 0 : _c.leg.loggingOff)) {
61514
+ throw new Error(engineState.view.logoffError || ((_d = engineState.view.snapshot) == null ? void 0 : _d.leg.logoffError) || "A sessão anterior ainda está sendo encerrada.");
61515
+ }
61401
61516
  try {
61402
61517
  localStorage.setItem(LAST_USERNAME_KEY, user);
61403
61518
  if (token) {
@@ -61438,12 +61553,15 @@ function DialerWrapper({ options, containerEl }) {
61438
61553
  if (!engineMode || !username || isRestoringSession) return;
61439
61554
  const user = username;
61440
61555
  const client2 = ensureEngine(user);
61441
- setEngineWaiting(!client2.getState().view);
61556
+ const initial = client2.getState();
61557
+ setEngineWaiting(initial.wsState !== "open" || !initial.tabId || !initial.view);
61442
61558
  const fallback = setTimeout(() => setEngineWaiting(false), 15e3);
61443
61559
  let joining = false;
61444
61560
  let rejectedNote = false;
61445
61561
  const sync = () => {
61446
- const { view, sessionLost, rejected } = client2.getState();
61562
+ var _a2;
61563
+ const { view, sessionLost, rejected, wsState, tabId, lastError } = client2.getState();
61564
+ setEngineError((view == null ? void 0 : view.logoffError) ?? ((_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.logoffError) ?? lastError ?? null);
61447
61565
  if (!rejected && rejectedNote) {
61448
61566
  rejectedNote = false;
61449
61567
  setTabNote(null);
@@ -61461,7 +61579,7 @@ function DialerWrapper({ options, containerEl }) {
61461
61579
  setTabNote(rejected.message);
61462
61580
  return;
61463
61581
  }
61464
- if (!view) return;
61582
+ if (wsState !== "open" || !tabId || !view) return;
61465
61583
  setEngineWaiting(false);
61466
61584
  const hasSession = (view.engine === "running" || view.engine === "starting") && !sessionLost;
61467
61585
  if (hasSession && !view.loggingOff && !view.logoffError && !logoffPendingRef.current && !isAuthenticatedRef.current && !isLoadingRef.current && !joining) {
@@ -62071,7 +62189,7 @@ function DialerWrapper({ options, containerEl }) {
62071
62189
  onLogin: handleLogin,
62072
62190
  onLogout: handleLogout,
62073
62191
  isLoadingSession: isLoading || isRestoringSession || deciding,
62074
- sessionError: error2,
62192
+ sessionError: error2 || engineError,
62075
62193
  sessionStatus: loginStatus,
62076
62194
  loginConfirmation,
62077
62195
  onCancelLoginConfirmation: handleCancelLoginConfirmation,
@@ -62094,7 +62212,7 @@ function DialerWrapper({ options, containerEl }) {
62094
62212
  onLogin: handleLogin,
62095
62213
  onLogout: handleLogout,
62096
62214
  isLoadingSession: isLoading,
62097
- sessionError: error2,
62215
+ sessionError: error2 || engineError,
62098
62216
  sessionStatus: loginStatus,
62099
62217
  variant: uiVariant,
62100
62218
  portalTarget,