@coventia/softphone 0.4.0 → 0.5.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/dist/{call-C6YpT6uR.d.cts → call-FqcvDg1t.d.cts} +105 -12
- package/dist/{call-C6YpT6uR.d.ts → call-FqcvDg1t.d.ts} +105 -12
- package/dist/{chunk-EYEDPD7Y.js → chunk-6RR6G35E.js} +92 -17
- package/dist/chunk-6RR6G35E.js.map +1 -0
- package/dist/{chunk-2ZSOGGPO.js → chunk-FRXHEHQA.js} +15 -2
- package/dist/chunk-FRXHEHQA.js.map +1 -0
- package/dist/{chunk-UHNLEBBO.js → chunk-I3RSC52R.js} +28 -3
- package/dist/chunk-I3RSC52R.js.map +1 -0
- package/dist/chunk-M76SAXXM.js +13 -0
- package/dist/chunk-M76SAXXM.js.map +1 -0
- package/dist/{chunk-7R6LOOAI.js → chunk-N4PLEYFJ.js} +4 -2
- package/dist/chunk-N4PLEYFJ.js.map +1 -0
- package/dist/element/index.d.ts +142 -6
- package/dist/element/index.js +138 -43
- package/dist/element/index.js.map +1 -1
- package/dist/{engine-DPft99Wj.d.ts → engine-CsNRD7B7.d.ts} +1 -1
- package/dist/{engine-CDdaSojx.d.cts → engine-CzOCrfY9.d.cts} +1 -1
- package/dist/index.cjs +579 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +447 -37
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +26 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +24 -5
- package/dist/react/index.d.ts +24 -5
- package/dist/react/index.js +2 -2
- package/dist/{softphone-CY5d2WWY.d.cts → softphone-By9sizlV.d.ts} +42 -3
- package/dist/{softphone-DXgmTyrX.d.ts → softphone-JZvJdzU3.d.cts} +42 -3
- package/dist/{sources-ONYQW733.js → sources-QL2LRZVH.js} +19 -2
- package/dist/sources-QL2LRZVH.js.map +1 -0
- package/dist/testing/index.cjs +13 -0
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +1 -1
- package/dist/ui/index.cjs +92 -2
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +2 -2
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +69 -4
- package/dist/ui/index.js.map +1 -1
- package/dist/zadarma/index.cjs +111 -14
- package/dist/zadarma/index.cjs.map +1 -1
- package/dist/zadarma/index.d.cts +5 -6
- package/dist/zadarma/index.d.ts +5 -6
- package/dist/zadarma/index.js +3 -2
- package/dist/zadarma-engine-TIUE7PZ7.js +6 -0
- package/dist/{zadarma-engine-L5DEFXUS.js.map → zadarma-engine-TIUE7PZ7.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-2ZSOGGPO.js.map +0 -1
- package/dist/chunk-7R6LOOAI.js.map +0 -1
- package/dist/chunk-EYEDPD7Y.js.map +0 -1
- package/dist/chunk-UHNLEBBO.js.map +0 -1
- package/dist/sources-ONYQW733.js.map +0 -1
- package/dist/zadarma-engine-L5DEFXUS.js +0 -5
package/dist/element/index.d.ts
CHANGED
|
@@ -57,8 +57,44 @@ interface CallOptions {
|
|
|
57
57
|
* Zadarma registers on-demand, per call, in response to a push (SPEC §3.2); a persistent UA is
|
|
58
58
|
* ignored by its PBX. Leaking "registered" into the public API would force every future provider —
|
|
59
59
|
* including our own SIP infrastructure, which registers persistently — to fake it.
|
|
60
|
+
*
|
|
61
|
+
* `standby` means started, and this browser's leader, but ANOTHER softphone of the same extension has
|
|
62
|
+
* the line — a tab in another browser, the desktop app, a partner page (`docs/specs/line-owner.md` §5).
|
|
63
|
+
* Its push socket is closed on purpose, so nothing here registers or rings: `line` says where the calls
|
|
64
|
+
* go, and `takeLine()` — or a `call()` — brings them here. A host that reads `online` as "ready"
|
|
65
|
+
* correctly reads this as not ready.
|
|
66
|
+
*/
|
|
67
|
+
type SoftphoneState = 'stopped' | 'starting' | 'online' | 'standby' | 'offline' | 'reconnecting' | 'destroyed';
|
|
68
|
+
/**
|
|
69
|
+
* Where this extension's incoming calls ring, as far as this softphone knows. `here`: it has the line.
|
|
70
|
+
* `elsewhere`: another softphone of the same extension has it, and this one waits in `standby`. `unknown`:
|
|
71
|
+
* nobody has said — no presence source, a backend that predates the line owner, a presence socket that is
|
|
72
|
+
* down — and this softphone listens, exactly as before the line owner existed (§5.1 R7, fail-open).
|
|
73
|
+
*/
|
|
74
|
+
type LineState = 'here' | 'elsewhere' | 'unknown';
|
|
75
|
+
/** Who has the line when it is `elsewhere`: the desktop app, or any other softphone (a browser tab). */
|
|
76
|
+
type LineLocation = 'desktop' | 'browser';
|
|
77
|
+
/**
|
|
78
|
+
* The last attempt to bring the line here — `takeLine()`, or a `call()` from `standby`, whoever made it:
|
|
79
|
+
* `claiming` while it is in flight, `refused` when the user is on a call where the line is, `failed` when
|
|
80
|
+
* nothing could decide. `idle` before any attempt, once the line comes here, and after a `stop()`.
|
|
60
81
|
*/
|
|
61
|
-
type
|
|
82
|
+
type LineClaim = 'idle' | 'claiming' | 'refused' | 'failed';
|
|
83
|
+
/** The server's word on this softphone's line, as a `PresenceSource` hands it over (`PresenceCallbacks.onLine`). */
|
|
84
|
+
interface LineUpdate {
|
|
85
|
+
readonly state: 'here' | 'elsewhere';
|
|
86
|
+
/** Who has the line when it is `elsewhere`; `null` when it is here, or the server did not say. */
|
|
87
|
+
readonly where: LineLocation | null;
|
|
88
|
+
}
|
|
89
|
+
/** The answer to `PresenceConnection.claimLine()`. */
|
|
90
|
+
interface LineClaimResult {
|
|
91
|
+
readonly ok: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Why it was refused: `'on_call'` while the user is on a call (the line never moves during one). Any
|
|
94
|
+
* other value means nothing was decided. Absent when `ok`.
|
|
95
|
+
*/
|
|
96
|
+
readonly reason?: string;
|
|
97
|
+
}
|
|
62
98
|
interface ContactsPage {
|
|
63
99
|
readonly items: readonly Contact[];
|
|
64
100
|
/**
|
|
@@ -210,6 +246,18 @@ interface PresenceConnection {
|
|
|
210
246
|
* own source that has no such message simply omits it and gets the grace. Sent just before `close()`.
|
|
211
247
|
*/
|
|
212
248
|
readonly bye?: () => void;
|
|
249
|
+
/**
|
|
250
|
+
* The line owner (`docs/specs/line-owner.md` §5.2): says which softphone this connection speaks for, so
|
|
251
|
+
* the server can tell it whether it has the line — through `PresenceCallbacks.onLine`, right after this
|
|
252
|
+
* and on every change. Sent as the FIRST message of every connection. Optional: a source without it never
|
|
253
|
+
* takes part, and its softphone listens exactly as before (fail-open), reading its line as `unknown`.
|
|
254
|
+
*/
|
|
255
|
+
readonly hello?: (clientId: string) => void;
|
|
256
|
+
/**
|
|
257
|
+
* "Recibir aquí": asks for the line. Answered through `PresenceCallbacks.onClaimResult` — and, when it
|
|
258
|
+
* succeeds, by `onLine` saying `here` first. Optional, like `hello`.
|
|
259
|
+
*/
|
|
260
|
+
readonly claimLine?: () => void;
|
|
213
261
|
/**
|
|
214
262
|
* Ends this connection deliberately. Never causes the SAME callbacks' `onClose` to fire — that is
|
|
215
263
|
* reserved for the connection ending on its OWN (a network drop, a server-side close, a rejected
|
|
@@ -229,6 +277,13 @@ interface PresenceCallbacks {
|
|
|
229
277
|
* identical from here, and both get the same treatment (spec §4.4 — quiet retry, nothing surfaced).
|
|
230
278
|
*/
|
|
231
279
|
readonly onClose: () => void;
|
|
280
|
+
/**
|
|
281
|
+
* The line, as the server says it — right after `PresenceConnection.hello`, and on every change after
|
|
282
|
+
* that. Optional, so callbacks written before the line owner existed still type-check.
|
|
283
|
+
*/
|
|
284
|
+
readonly onLine?: (update: LineUpdate) => void;
|
|
285
|
+
/** The answer to `PresenceConnection.claimLine()`. Optional, like `onLine`. */
|
|
286
|
+
readonly onClaimResult?: (result: LineClaimResult) => void;
|
|
232
287
|
}
|
|
233
288
|
/**
|
|
234
289
|
* Opens one live presence connection. Coventia-backed by default (mints a one-time ticket, opens
|
|
@@ -470,6 +525,16 @@ declare const ERROR_CATALOGUE: {
|
|
|
470
525
|
readonly message: "The provider does not support this operation.";
|
|
471
526
|
readonly solution: "Guard the UI on the operation before offering it.";
|
|
472
527
|
};
|
|
528
|
+
readonly 'line/claim-refused': {
|
|
529
|
+
readonly fatal: false;
|
|
530
|
+
readonly message: "The line was not moved here: the user is on a call on the softphone that has it.";
|
|
531
|
+
readonly solution: "Nothing to fix. The line never moves during a call; take it again once that call has ended. Incoming calls keep ringing where they ring now.";
|
|
532
|
+
};
|
|
533
|
+
readonly 'line/unavailable': {
|
|
534
|
+
readonly fatal: false;
|
|
535
|
+
readonly message: "The line could not be taken: nothing decided where it is.";
|
|
536
|
+
readonly solution: "The presence connection is down, the backend predates the line owner, or it did not answer in time. Retry in a moment; incoming calls keep ringing where they ring now.";
|
|
537
|
+
};
|
|
473
538
|
readonly 'provider/provision-failed': {
|
|
474
539
|
readonly fatal: true;
|
|
475
540
|
readonly message: "The provider could not issue SIP credentials.";
|
|
@@ -556,10 +621,25 @@ declare enum SoftphoneEvent {
|
|
|
556
621
|
/**
|
|
557
622
|
* `softphone.presence` changed — the live agent-presence list (Phase B,
|
|
558
623
|
* `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a
|
|
559
|
-
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being
|
|
560
|
-
* `online
|
|
624
|
+
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being a
|
|
625
|
+
* started leader — `online`, or waiting in `standby` for the line; never fires at all otherwise,
|
|
626
|
+
* same reasoning as `TransferOutcome`.
|
|
627
|
+
*/
|
|
628
|
+
PresenceChanged = "presenceChanged",
|
|
629
|
+
/**
|
|
630
|
+
* `softphone.line` / `lineWhere` changed (`docs/specs/line-owner.md` §5): this softphone got the line,
|
|
631
|
+
* lost it to another softphone of the same extension, or stopped knowing (`unknown` — it then listens,
|
|
632
|
+
* as before the line owner existed). A third axis beside `StateChanged` and `LeaderChanged`: losing the
|
|
633
|
+
* line is what takes the softphone to `standby`, but not at once while a call is in progress — the line
|
|
634
|
+
* can read `elsewhere` while the state is still `online`, until that call ends.
|
|
635
|
+
*/
|
|
636
|
+
LineChanged = "lineChanged",
|
|
637
|
+
/**
|
|
638
|
+
* `softphone.lineClaim` changed: an attempt to bring the line here started, was refused, failed, or ended
|
|
639
|
+
* (the line came here, or the softphone stopped). Kept apart from `LineChanged`, which only ever means the
|
|
640
|
+
* line itself moved.
|
|
561
641
|
*/
|
|
562
|
-
|
|
642
|
+
LineClaimChanged = "lineClaimChanged"
|
|
563
643
|
}
|
|
564
644
|
interface SoftphoneEventMap {
|
|
565
645
|
[SoftphoneEvent.StateChanged]: {
|
|
@@ -580,6 +660,13 @@ interface SoftphoneEventMap {
|
|
|
580
660
|
[SoftphoneEvent.PresenceChanged]: {
|
|
581
661
|
readonly agents: readonly PresenceEntry[];
|
|
582
662
|
};
|
|
663
|
+
[SoftphoneEvent.LineChanged]: {
|
|
664
|
+
readonly line: LineState;
|
|
665
|
+
readonly where: LineLocation | null;
|
|
666
|
+
};
|
|
667
|
+
[SoftphoneEvent.LineClaimChanged]: {
|
|
668
|
+
readonly claim: LineClaim;
|
|
669
|
+
};
|
|
583
670
|
}
|
|
584
671
|
declare enum CallEvent {
|
|
585
672
|
/** Any transition. The catch-all; the specific events below are conveniences. */
|
|
@@ -878,6 +965,39 @@ interface Softphone extends TypedEmitter<SoftphoneEventMap> {
|
|
|
878
965
|
* "active in another tab" state for that duration (the `/ui` widget already does).
|
|
879
966
|
*/
|
|
880
967
|
readonly isLeader: boolean;
|
|
968
|
+
/**
|
|
969
|
+
* Where this extension's incoming calls ring (`docs/specs/line-owner.md` §5) — the axis ACROSS browsers,
|
|
970
|
+
* devices and the desktop app that `isLeader` cannot see. Only one softphone of an extension listens:
|
|
971
|
+
* the first one started keeps the line, and the others wait in `standby` until it is free or the user
|
|
972
|
+
* takes it (`takeLine()`).
|
|
973
|
+
*
|
|
974
|
+
* `unknown` until the backend has said, and whenever it cannot (no presence source, a backend that
|
|
975
|
+
* predates the line owner, a presence socket that is down): the softphone then listens, exactly as
|
|
976
|
+
* before this existed — it never leaves nobody listening. Changes fire `SoftphoneEvent.LineChanged`.
|
|
977
|
+
*/
|
|
978
|
+
readonly line: LineState;
|
|
979
|
+
/**
|
|
980
|
+
* Who has the line while it is `elsewhere` — the desktop app, or another softphone (a browser tab) —
|
|
981
|
+
* or `null`: the line is here, unknown, or the backend did not say.
|
|
982
|
+
*/
|
|
983
|
+
readonly lineWhere: LineLocation | null;
|
|
984
|
+
/**
|
|
985
|
+
* How the last attempt to bring the line here went, whoever made it (`takeLine()`, or a `call()` from
|
|
986
|
+
* `standby`) — what the waiting screen shows under its title, so a refused click-to-call explains itself
|
|
987
|
+
* just like a refused "Recibir aquí". Changes fire `SoftphoneEvent.LineClaimChanged`.
|
|
988
|
+
*/
|
|
989
|
+
readonly lineClaim: LineClaim;
|
|
990
|
+
/**
|
|
991
|
+
* "Recibir aquí": takes the line from whichever softphone has it, so incoming calls ring here. Resolves
|
|
992
|
+
* once the line IS here (at once when it already is); the softphone then leaves `standby` and starts
|
|
993
|
+
* listening on its own.
|
|
994
|
+
*
|
|
995
|
+
* Rejects with a non-fatal `SoftphoneError`: `line/claim-refused` while the user is on a call on the
|
|
996
|
+
* softphone that has the line (it never moves during a call — retry once it ends), or `line/unavailable`
|
|
997
|
+
* when nothing could decide (no presence connection, a backend without the line owner, no answer in
|
|
998
|
+
* time). Neither is emitted on `SoftphoneEvent.Error` — this promise is the answer.
|
|
999
|
+
*/
|
|
1000
|
+
takeLine(): Promise<void>;
|
|
881
1001
|
/**
|
|
882
1002
|
* Start.
|
|
883
1003
|
*
|
|
@@ -890,6 +1010,11 @@ interface Softphone extends TypedEmitter<SoftphoneEventMap> {
|
|
|
890
1010
|
stop(): Promise<void>;
|
|
891
1011
|
/** Idempotent. After this the instance is dead — construct a new one. */
|
|
892
1012
|
destroy(): Promise<void>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Dial. From `standby` this takes the line first (`takeLine()`), then dials — a call the host starts is
|
|
1015
|
+
* the user asking for the phone here — and rejects with that method's `line/*` error if the line cannot
|
|
1016
|
+
* be taken.
|
|
1017
|
+
*/
|
|
893
1018
|
call(destination: string, options?: CallOptions): Promise<Call>;
|
|
894
1019
|
/** The live call, or null. The consult leg of an attended transfer is reachable from it. */
|
|
895
1020
|
readonly activeCall: Call | null;
|
|
@@ -904,8 +1029,9 @@ interface Softphone extends TypedEmitter<SoftphoneEventMap> {
|
|
|
904
1029
|
/**
|
|
905
1030
|
* The live agent-presence list (Phase B, `docs/specs/transfer-presence-picker.md`) — every org
|
|
906
1031
|
* member with a phone extension, whatever their state. Empty when there is no `PresenceSource`
|
|
907
|
-
* (no Coventia backend, or a host that omitted one), or while this tab is not
|
|
908
|
-
*
|
|
1032
|
+
* (no Coventia backend, or a host that omitted one), or while this tab is not a started leader —
|
|
1033
|
+
* `online`, or waiting in `standby` for the line — or its connection is down: never an error, just
|
|
1034
|
+
* nothing to show (spec §4.4's own success criterion). Updates fire
|
|
909
1035
|
* `SoftphoneEvent.PresenceChanged`; `usePresence()` is the reactive read.
|
|
910
1036
|
*/
|
|
911
1037
|
readonly presence: readonly PresenceEntry[];
|
|
@@ -1053,6 +1179,16 @@ interface SoftphoneElementEventMap {
|
|
|
1053
1179
|
'cv-leadership-changed': CustomEvent<{
|
|
1054
1180
|
readonly isLeader: boolean;
|
|
1055
1181
|
}>;
|
|
1182
|
+
/**
|
|
1183
|
+
* Where this extension's calls ring changed (`docs/specs/line-owner.md` §5) — the axis across browsers,
|
|
1184
|
+
* devices and the desktop app that `cv-leadership-changed` cannot see. `line: 'elsewhere'` is what puts
|
|
1185
|
+
* the phone in `standby` (`cv-state-changed`), and **the widget already shows its own waiting screen with
|
|
1186
|
+
* "Recibir aquí" for it** — listen here for your own indicator; `el.softphone.takeLine()` takes the line.
|
|
1187
|
+
*/
|
|
1188
|
+
'cv-line-changed': CustomEvent<{
|
|
1189
|
+
readonly line: LineState;
|
|
1190
|
+
readonly where: LineLocation | null;
|
|
1191
|
+
}>;
|
|
1056
1192
|
/**
|
|
1057
1193
|
* A transfer's final handoff step resolved — `status` says whether the caller actually reached
|
|
1058
1194
|
* `target`. Polled, not instant (Phase A, `docs/specs/transfer-outcome-relay.md`); only for that
|
package/dist/element/index.js
CHANGED
|
@@ -6931,7 +6931,7 @@ var require_jsx_runtime = __commonJS({
|
|
|
6931
6931
|
});
|
|
6932
6932
|
|
|
6933
6933
|
// src/element/index.ts
|
|
6934
|
-
var
|
|
6934
|
+
var import_react47 = __toESM(require_react(), 1);
|
|
6935
6935
|
var import_client = __toESM(require_client(), 1);
|
|
6936
6936
|
|
|
6937
6937
|
// src/react/context.tsx
|
|
@@ -6950,7 +6950,7 @@ function useSoftphone() {
|
|
|
6950
6950
|
}
|
|
6951
6951
|
|
|
6952
6952
|
// src/ui/SoftphoneWidget.tsx
|
|
6953
|
-
var
|
|
6953
|
+
var import_react45 = __toESM(require_react(), 1);
|
|
6954
6954
|
|
|
6955
6955
|
// ../../node_modules/lucide-react/dist/esm/createLucideIcon.js
|
|
6956
6956
|
var import_react3 = __toESM(require_react());
|
|
@@ -7178,6 +7178,19 @@ var Pause = createLucideIcon("Pause", [
|
|
|
7178
7178
|
["rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", key: "1okwgv" }]
|
|
7179
7179
|
]);
|
|
7180
7180
|
|
|
7181
|
+
// ../../node_modules/lucide-react/dist/esm/icons/phone-forwarded.js
|
|
7182
|
+
var PhoneForwarded = createLucideIcon("PhoneForwarded", [
|
|
7183
|
+
["polyline", { points: "18 2 22 6 18 10", key: "6vjanh" }],
|
|
7184
|
+
["line", { x1: "14", x2: "22", y1: "6", y2: "6", key: "1jsywh" }],
|
|
7185
|
+
[
|
|
7186
|
+
"path",
|
|
7187
|
+
{
|
|
7188
|
+
d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z",
|
|
7189
|
+
key: "foiqr5"
|
|
7190
|
+
}
|
|
7191
|
+
]
|
|
7192
|
+
]);
|
|
7193
|
+
|
|
7181
7194
|
// ../../node_modules/lucide-react/dist/esm/icons/phone-incoming.js
|
|
7182
7195
|
var PhoneIncoming = createLucideIcon("PhoneIncoming", [
|
|
7183
7196
|
["polyline", { points: "16 2 16 8 22 8", key: "1ygljm" }],
|
|
@@ -7349,6 +7362,31 @@ function useIsLeader() {
|
|
|
7349
7362
|
(0, import_react4.useCallback)(() => phone.isLeader, [phone])
|
|
7350
7363
|
);
|
|
7351
7364
|
}
|
|
7365
|
+
function useLine() {
|
|
7366
|
+
const phone = useSoftphone();
|
|
7367
|
+
const cache = (0, import_react4.useRef)(null);
|
|
7368
|
+
return (0, import_react4.useSyncExternalStore)(
|
|
7369
|
+
(0, import_react4.useCallback)(
|
|
7370
|
+
(cb) => {
|
|
7371
|
+
const offLine = phone.on("lineChanged" /* LineChanged */, () => cb());
|
|
7372
|
+
const offClaim = phone.on("lineClaimChanged" /* LineClaimChanged */, () => cb());
|
|
7373
|
+
return () => {
|
|
7374
|
+
offLine();
|
|
7375
|
+
offClaim();
|
|
7376
|
+
};
|
|
7377
|
+
},
|
|
7378
|
+
[phone]
|
|
7379
|
+
),
|
|
7380
|
+
(0, import_react4.useCallback)(() => {
|
|
7381
|
+
const { line, lineWhere: where, lineClaim: claim } = phone;
|
|
7382
|
+
const cached = cache.current;
|
|
7383
|
+
if (cached?.line === line && cached.where === where && cached.claim === claim) return cached;
|
|
7384
|
+
const view = { line, where, claim };
|
|
7385
|
+
cache.current = view;
|
|
7386
|
+
return view;
|
|
7387
|
+
}, [phone])
|
|
7388
|
+
);
|
|
7389
|
+
}
|
|
7352
7390
|
function useActiveCall() {
|
|
7353
7391
|
const phone = useSoftphone();
|
|
7354
7392
|
return (0, import_react4.useSyncExternalStore)(
|
|
@@ -8787,6 +8825,22 @@ var ERROR_TEXT = {
|
|
|
8787
8825
|
// thinks they dropped it will hang up on a client to "fix" it.
|
|
8788
8826
|
"call/transfer-failed": { title: "No se pudo transferir. Sigues en la llamada" },
|
|
8789
8827
|
"call/unsupported": { title: "Esta funci\xF3n no est\xE1 disponible" },
|
|
8828
|
+
// ── Line owner ──────────────────────────────────────────────────────────────
|
|
8829
|
+
/*
|
|
8830
|
+
Not call outcomes: they answer "Recibir aquí" on `LineGate`, which has ONE line for them where the
|
|
8831
|
+
explanation was, so it reads the pair as two sentences — `${title}. ${body}`. Split anyway, so the
|
|
8832
|
+
title stands alone wherever there is only room for a headline (a host's own toast), under the same
|
|
8833
|
+
one-line rules as every other non-fatal title. "Allí" because the gate has just said where: the
|
|
8834
|
+
other softphone, the one that has the calls.
|
|
8835
|
+
*/
|
|
8836
|
+
"line/claim-refused": {
|
|
8837
|
+
title: "Est\xE1s en una llamada all\xED",
|
|
8838
|
+
body: "Podr\xE1s recibirlas aqu\xED cuando cuelgues."
|
|
8839
|
+
},
|
|
8840
|
+
"line/unavailable": {
|
|
8841
|
+
title: "No se ha podido cambiar",
|
|
8842
|
+
body: "Int\xE9ntalo de nuevo."
|
|
8843
|
+
},
|
|
8790
8844
|
// ── Provider ────────────────────────────────────────────────────────────────
|
|
8791
8845
|
"provider/provision-failed": {
|
|
8792
8846
|
title: "No se pudo activar el tel\xE9fono",
|
|
@@ -10929,18 +10983,55 @@ function LeaderGate() {
|
|
|
10929
10983
|
] });
|
|
10930
10984
|
}
|
|
10931
10985
|
|
|
10986
|
+
// src/ui/permission/LineGate.tsx
|
|
10987
|
+
var import_jsx_runtime24 = (
|
|
10988
|
+
/*
|
|
10989
|
+
`part` on all four, like `ErrorGate`: it replaces the whole dialpad, and inside `<coventia-softphone>`
|
|
10990
|
+
a shadow root makes it otherwise unreachable from the host's CSS. `data-claim` is for the light-DOM
|
|
10991
|
+
`/ui` path and for debugging — it cannot cross the shadow boundary either.
|
|
10992
|
+
*/
|
|
10993
|
+
__toESM(require_jsx_runtime(), 1)
|
|
10994
|
+
);
|
|
10995
|
+
var BODY = "Las llamadas entran all\xED. Si prefieres recibirlas aqu\xED, pulsa \xABRecibir aqu\xED\xBB.";
|
|
10996
|
+
var sentence = (code) => `${ERROR_TEXT[code].title}. ${ERROR_TEXT[code].body ?? ""}`.trim();
|
|
10997
|
+
function LineGate() {
|
|
10998
|
+
const softphone = useSoftphone();
|
|
10999
|
+
const { where, claim } = useLine();
|
|
11000
|
+
const take = () => {
|
|
11001
|
+
softphone.takeLine().catch(() => {
|
|
11002
|
+
});
|
|
11003
|
+
};
|
|
11004
|
+
const body = claim === "refused" ? sentence("line/claim-refused") : claim === "failed" ? sentence("line/unavailable") : BODY;
|
|
11005
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "cv-gate", "data-claim": claim, part: "line-gate", children: [
|
|
11006
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "cv-gate-mark", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PhoneForwarded, { size: 22 }) }),
|
|
11007
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h3", { className: "cv-gate-title", part: "line-title", children: where === "desktop" ? "Tu tel\xE9fono est\xE1 activo en la app de escritorio" : "Tu tel\xE9fono est\xE1 activo en otro sitio" }),
|
|
11008
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { role: "status", part: "line-body", children: body }),
|
|
11009
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
11010
|
+
"button",
|
|
11011
|
+
{
|
|
11012
|
+
className: "cv-btn cv-btn--accent",
|
|
11013
|
+
onClick: take,
|
|
11014
|
+
disabled: claim === "claiming",
|
|
11015
|
+
"aria-busy": claim === "claiming",
|
|
11016
|
+
part: "line-claim",
|
|
11017
|
+
children: claim === "claiming" ? "Cambiando\u2026" : "Recibir aqu\xED"
|
|
11018
|
+
}
|
|
11019
|
+
)
|
|
11020
|
+
] });
|
|
11021
|
+
}
|
|
11022
|
+
|
|
10932
11023
|
// src/ui/lib/useCallOutcome.ts
|
|
10933
|
-
var
|
|
11024
|
+
var import_react38 = __toESM(require_react(), 1);
|
|
10934
11025
|
var BACKSTOP_MS = 6e3;
|
|
10935
11026
|
function useCallOutcome() {
|
|
10936
11027
|
const softphone = useSoftphone();
|
|
10937
|
-
const [code, setCode] = (0,
|
|
10938
|
-
const timer = (0,
|
|
10939
|
-
const clear = (0,
|
|
11028
|
+
const [code, setCode] = (0, import_react38.useState)(null);
|
|
11029
|
+
const timer = (0, import_react38.useRef)(void 0);
|
|
11030
|
+
const clear = (0, import_react38.useCallback)(() => {
|
|
10940
11031
|
clearTimeout(timer.current);
|
|
10941
11032
|
setCode(null);
|
|
10942
11033
|
}, []);
|
|
10943
|
-
(0,
|
|
11034
|
+
(0, import_react38.useEffect)(() => {
|
|
10944
11035
|
const off = softphone.on("error" /* Error */, (error) => {
|
|
10945
11036
|
if (error.fatal) return;
|
|
10946
11037
|
clearTimeout(timer.current);
|
|
@@ -10952,7 +11043,7 @@ function useCallOutcome() {
|
|
|
10952
11043
|
clearTimeout(timer.current);
|
|
10953
11044
|
};
|
|
10954
11045
|
}, [softphone]);
|
|
10955
|
-
(0,
|
|
11046
|
+
(0, import_react38.useEffect)(() => softphone.on("incomingCall" /* IncomingCall */, () => clear()), [softphone, clear]);
|
|
10956
11047
|
return {
|
|
10957
11048
|
outcome: code === null ? null : { code, text: ERROR_TEXT[code].title },
|
|
10958
11049
|
clear
|
|
@@ -10960,12 +11051,12 @@ function useCallOutcome() {
|
|
|
10960
11051
|
}
|
|
10961
11052
|
|
|
10962
11053
|
// src/ui/audio/useCallAudio.ts
|
|
10963
|
-
var
|
|
11054
|
+
var import_react40 = __toESM(require_react(), 1);
|
|
10964
11055
|
function useCallAudio(enabled) {
|
|
10965
11056
|
const softphone = useSoftphone();
|
|
10966
|
-
const remote = (0,
|
|
10967
|
-
const tone = (0,
|
|
10968
|
-
(0,
|
|
11057
|
+
const remote = (0, import_react40.useRef)(null);
|
|
11058
|
+
const tone = (0, import_react40.useRef)(null);
|
|
11059
|
+
(0, import_react40.useEffect)(() => {
|
|
10969
11060
|
if (!enabled || typeof document === "undefined") return;
|
|
10970
11061
|
const prime = () => primeAudio();
|
|
10971
11062
|
const gestures = ["pointerdown", "keydown"];
|
|
@@ -11059,7 +11150,7 @@ function useCallAudio(enabled) {
|
|
|
11059
11150
|
}
|
|
11060
11151
|
|
|
11061
11152
|
// src/ui/notices/TransferNotices.tsx
|
|
11062
|
-
var
|
|
11153
|
+
var import_jsx_runtime25 = __toESM(require_jsx_runtime(), 1);
|
|
11063
11154
|
function TransferNotices({
|
|
11064
11155
|
notices,
|
|
11065
11156
|
dismiss
|
|
@@ -11071,7 +11162,7 @@ function TransferNotices({
|
|
|
11071
11162
|
if (notice.caller) void softphone.call(notice.caller).catch(() => {
|
|
11072
11163
|
});
|
|
11073
11164
|
};
|
|
11074
|
-
return /* @__PURE__ */ (0,
|
|
11165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
11075
11166
|
"div",
|
|
11076
11167
|
{
|
|
11077
11168
|
className: "cv-transfer-notices",
|
|
@@ -11079,20 +11170,20 @@ function TransferNotices({
|
|
|
11079
11170
|
part: "transfer-notices",
|
|
11080
11171
|
"data-has-notices": notices.length > 0,
|
|
11081
11172
|
children: [
|
|
11082
|
-
notices.length > 1 ? /* @__PURE__ */ (0,
|
|
11173
|
+
notices.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { className: "cv-transfer-notices-count", children: [
|
|
11083
11174
|
notices.length,
|
|
11084
11175
|
" traspasos sin conectar"
|
|
11085
11176
|
] }) : null,
|
|
11086
|
-
notices.map((notice) => /* @__PURE__ */ (0,
|
|
11087
|
-
/* @__PURE__ */ (0,
|
|
11088
|
-
/* @__PURE__ */ (0,
|
|
11089
|
-
/* @__PURE__ */ (0,
|
|
11090
|
-
/* @__PURE__ */ (0,
|
|
11177
|
+
notices.map((notice) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "cv-transfer-notice", part: "transfer-notice", children: [
|
|
11178
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "cv-transfer-notice-mark", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(PhoneMissed, { size: 16 }) }),
|
|
11179
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "cv-transfer-notice-body", children: [
|
|
11180
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "cv-transfer-notice-header", children: [
|
|
11181
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { className: "cv-transfer-notice-title", children: [
|
|
11091
11182
|
"El traspaso a ",
|
|
11092
11183
|
notice.target,
|
|
11093
11184
|
" no se conect\xF3"
|
|
11094
11185
|
] }),
|
|
11095
|
-
/* @__PURE__ */ (0,
|
|
11186
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
11096
11187
|
"button",
|
|
11097
11188
|
{
|
|
11098
11189
|
type: "button",
|
|
@@ -11100,17 +11191,17 @@ function TransferNotices({
|
|
|
11100
11191
|
onClick: () => dismiss(notice.id),
|
|
11101
11192
|
"aria-label": "Descartar aviso",
|
|
11102
11193
|
part: "transfer-notice-dismiss",
|
|
11103
|
-
children: /* @__PURE__ */ (0,
|
|
11194
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(X, { size: 14 })
|
|
11104
11195
|
}
|
|
11105
11196
|
)
|
|
11106
11197
|
] }),
|
|
11107
|
-
notice.caller ? /* @__PURE__ */ (0,
|
|
11198
|
+
notice.caller ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { className: "cv-transfer-notice-subtitle", children: [
|
|
11108
11199
|
notice.caller,
|
|
11109
11200
|
" se ha desconectado."
|
|
11110
11201
|
] }) : null,
|
|
11111
|
-
/* @__PURE__ */ (0,
|
|
11112
|
-
/* @__PURE__ */ (0,
|
|
11113
|
-
notice.caller ? /* @__PURE__ */ (0,
|
|
11202
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "cv-transfer-notice-footer", children: [
|
|
11203
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "cv-transfer-notice-age", children: formatAge(elapsedSeconds(notice.at, now)) }),
|
|
11204
|
+
notice.caller ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
11114
11205
|
"button",
|
|
11115
11206
|
{
|
|
11116
11207
|
type: "button",
|
|
@@ -11118,7 +11209,7 @@ function TransferNotices({
|
|
|
11118
11209
|
onClick: () => callBack(notice),
|
|
11119
11210
|
part: "transfer-notice-callback",
|
|
11120
11211
|
children: [
|
|
11121
|
-
/* @__PURE__ */ (0,
|
|
11212
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Phone, { size: 14, "aria-hidden": true }),
|
|
11122
11213
|
"Llamar de vuelta"
|
|
11123
11214
|
]
|
|
11124
11215
|
}
|
|
@@ -11132,15 +11223,15 @@ function TransferNotices({
|
|
|
11132
11223
|
}
|
|
11133
11224
|
|
|
11134
11225
|
// src/ui/notices/useTransferNotices.ts
|
|
11135
|
-
var
|
|
11226
|
+
var import_react43 = __toESM(require_react(), 1);
|
|
11136
11227
|
function useTransferNotices() {
|
|
11137
11228
|
const softphone = useSoftphone();
|
|
11138
|
-
const [notices, setNotices] = (0,
|
|
11139
|
-
const nextId = (0,
|
|
11140
|
-
const dismiss = (0,
|
|
11229
|
+
const [notices, setNotices] = (0, import_react43.useState)([]);
|
|
11230
|
+
const nextId = (0, import_react43.useRef)(0);
|
|
11231
|
+
const dismiss = (0, import_react43.useCallback)((id) => {
|
|
11141
11232
|
setNotices((prev) => prev.filter((n) => n.id !== id));
|
|
11142
11233
|
}, []);
|
|
11143
|
-
(0,
|
|
11234
|
+
(0, import_react43.useEffect)(
|
|
11144
11235
|
() => softphone.on("transferOutcome" /* TransferOutcome */, (payload) => {
|
|
11145
11236
|
if (payload.status === "not-connected") {
|
|
11146
11237
|
const id = nextId.current++;
|
|
@@ -11153,7 +11244,7 @@ function useTransferNotices() {
|
|
|
11153
11244
|
}
|
|
11154
11245
|
|
|
11155
11246
|
// src/ui/SoftphoneWidget.tsx
|
|
11156
|
-
var
|
|
11247
|
+
var import_jsx_runtime26 = __toESM(require_jsx_runtime(), 1);
|
|
11157
11248
|
function SoftphoneWidget(props) {
|
|
11158
11249
|
const {
|
|
11159
11250
|
contacts,
|
|
@@ -11169,16 +11260,17 @@ function SoftphoneWidget(props) {
|
|
|
11169
11260
|
const fallback = softphone.sources;
|
|
11170
11261
|
const contactsSource = contacts ?? fallback?.contacts;
|
|
11171
11262
|
const historySource = history ?? fallback?.history;
|
|
11172
|
-
const [tab, setTab] = (0,
|
|
11263
|
+
const [tab, setTab] = (0, import_react45.useState)("dialer");
|
|
11173
11264
|
const call = useActiveCall();
|
|
11174
11265
|
const isLeader = useIsLeader();
|
|
11175
11266
|
const softphoneState = useSoftphoneState();
|
|
11176
11267
|
const isFollower = !isLeader && softphoneState === "starting";
|
|
11268
|
+
const isStandby = softphoneState === "standby";
|
|
11177
11269
|
const mic = useMicPermission();
|
|
11178
11270
|
useCallAudio(sounds);
|
|
11179
11271
|
const blocked = requireMic && mic.state !== "ready";
|
|
11180
11272
|
const fatal = useLastError();
|
|
11181
|
-
const screenIsFree = !call && !blocked && !fatal && !isFollower;
|
|
11273
|
+
const screenIsFree = !call && !blocked && !fatal && !isFollower && !isStandby;
|
|
11182
11274
|
const { outcome, clear: clearOutcome } = useCallOutcome();
|
|
11183
11275
|
const { notices: transferNotices, dismiss: dismissTransferNotice } = useTransferNotices();
|
|
11184
11276
|
const retry = () => {
|
|
@@ -11198,15 +11290,15 @@ function SoftphoneWidget(props) {
|
|
|
11198
11290
|
// optional parts of a phone, so this is never conditionally hidden.
|
|
11199
11291
|
{ id: "settings", icon: Settings, label: "Ajustes", show: true }
|
|
11200
11292
|
].filter((t) => t.show);
|
|
11201
|
-
return /* @__PURE__ */ (0,
|
|
11293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
11202
11294
|
"div",
|
|
11203
11295
|
{
|
|
11204
11296
|
className: className ? `cv-softphone ${className}` : "cv-softphone",
|
|
11205
11297
|
...theme === "system" ? {} : { "data-cv-theme": theme },
|
|
11206
11298
|
children: [
|
|
11207
|
-
screenIsFree ? /* @__PURE__ */ (0,
|
|
11208
|
-
/* @__PURE__ */ (0,
|
|
11209
|
-
screenIsFree ? /* @__PURE__ */ (0,
|
|
11299
|
+
screenIsFree ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TransferNotices, { notices: transferNotices, dismiss: dismissTransferNotice }) : null,
|
|
11300
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "cv-body", children: isFollower ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(LeaderGate, {}) : isStandby && !call ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(LineGate, {}) : blocked ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(MicPrompt, { state: mic.state, onRequest: () => void mic.request() }) : call ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(CallScreen, { call, keyTones: sounds, ...incomingCallAction ? { incomingCallAction } : {} }) : fatal ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ErrorGate, { error: fatal, onRetry: retry }) : tab === "contacts" && contactsSource ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ContactsTab, { source: contactsSource, ...contactAction ? { contactAction } : {} }) : tab === "history" && historySource ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(HistoryTab, { source: historySource }) : tab === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SettingsTab, {}) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Dialpad, { outcome, onIntent: clearOutcome, keyTones: sounds }) }),
|
|
11301
|
+
screenIsFree ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "cv-tabs", role: "tablist", children: tabs.map(({ id, icon: Icon3, label }) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
11210
11302
|
"button",
|
|
11211
11303
|
{
|
|
11212
11304
|
role: "tab",
|
|
@@ -11214,8 +11306,8 @@ function SoftphoneWidget(props) {
|
|
|
11214
11306
|
className: "cv-tab",
|
|
11215
11307
|
onClick: () => setTab(id),
|
|
11216
11308
|
children: [
|
|
11217
|
-
/* @__PURE__ */ (0,
|
|
11218
|
-
/* @__PURE__ */ (0,
|
|
11309
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon3, { size: 20 }),
|
|
11310
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: label })
|
|
11219
11311
|
]
|
|
11220
11312
|
},
|
|
11221
11313
|
id
|
|
@@ -11236,6 +11328,7 @@ var EVENTS = {
|
|
|
11236
11328
|
callEnded: "cv-call-ended",
|
|
11237
11329
|
error: "cv-error",
|
|
11238
11330
|
leaderChanged: "cv-leadership-changed",
|
|
11331
|
+
lineChanged: "cv-line-changed",
|
|
11239
11332
|
transferOutcome: "cv-transfer-outcome"
|
|
11240
11333
|
};
|
|
11241
11334
|
var CoventiaSoftphoneElement = class extends HTMLElement {
|
|
@@ -11355,6 +11448,7 @@ var CoventiaSoftphoneElement = class extends HTMLElement {
|
|
|
11355
11448
|
"leaderChanged" /* LeaderChanged */,
|
|
11356
11449
|
({ isLeader }) => this.#emit(EVENTS.leaderChanged, { isLeader })
|
|
11357
11450
|
),
|
|
11451
|
+
phone.on("lineChanged" /* LineChanged */, ({ line, where }) => this.#emit(EVENTS.lineChanged, { line, where })),
|
|
11358
11452
|
// Payload is already exactly `TransferOutcomeEvent` — no reshaping needed, unlike the events above.
|
|
11359
11453
|
phone.on("transferOutcome" /* TransferOutcome */, (payload) => this.#emit(EVENTS.transferOutcome, payload))
|
|
11360
11454
|
];
|
|
@@ -11403,9 +11497,9 @@ var CoventiaSoftphoneElement = class extends HTMLElement {
|
|
|
11403
11497
|
...this.#config.incomingCallAction ? { incomingCallAction: this.#config.incomingCallAction } : {}
|
|
11404
11498
|
};
|
|
11405
11499
|
this.#root.render(
|
|
11406
|
-
(0,
|
|
11500
|
+
(0, import_react47.createElement)(
|
|
11407
11501
|
SoftphoneProvider,
|
|
11408
|
-
{ softphone: this.#softphone, children: (0,
|
|
11502
|
+
{ softphone: this.#softphone, children: (0, import_react47.createElement)(SoftphoneWidget, widgetProps) }
|
|
11409
11503
|
)
|
|
11410
11504
|
);
|
|
11411
11505
|
}
|
|
@@ -11476,6 +11570,7 @@ lucide-react/dist/esm/icons/mic.js:
|
|
|
11476
11570
|
lucide-react/dist/esm/icons/monitor-smartphone.js:
|
|
11477
11571
|
lucide-react/dist/esm/icons/music.js:
|
|
11478
11572
|
lucide-react/dist/esm/icons/pause.js:
|
|
11573
|
+
lucide-react/dist/esm/icons/phone-forwarded.js:
|
|
11479
11574
|
lucide-react/dist/esm/icons/phone-incoming.js:
|
|
11480
11575
|
lucide-react/dist/esm/icons/phone-missed.js:
|
|
11481
11576
|
lucide-react/dist/esm/icons/phone-off.js:
|