@coventia/softphone 0.3.0 → 0.4.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-CaPDWVk3.d.cts → call-C6YpT6uR.d.cts} +64 -1
- package/dist/{call-CaPDWVk3.d.ts → call-C6YpT6uR.d.ts} +64 -1
- package/dist/chunk-5E5OEXS2.js +14 -0
- package/dist/chunk-5E5OEXS2.js.map +1 -0
- package/dist/element/index.d.ts +56 -0
- package/dist/element/index.js +1064 -488
- package/dist/element/index.js.map +1 -1
- package/dist/{engine-CU2rEo3g.d.cts → engine-CDdaSojx.d.cts} +1 -1
- package/dist/{engine-BgBsIcEa.d.ts → engine-DPft99Wj.d.ts} +1 -1
- package/dist/index.cjs +97 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -6
- package/dist/index.d.ts +16 -6
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/{softphone-dZMkW8V1.d.cts → softphone-CY5d2WWY.d.cts} +3 -1
- package/dist/{softphone-DEt0TtQw.d.ts → softphone-DXgmTyrX.d.ts} +3 -1
- package/dist/softphone.css +254 -23
- package/dist/{sources-M3GYMS6V.js → sources-ONYQW733.js} +76 -8
- package/dist/sources-ONYQW733.js.map +1 -0
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/ui/index.cjs +578 -18
- 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 +573 -18
- package/dist/ui/index.js.map +1 -1
- package/dist/zadarma/index.d.cts +2 -2
- package/dist/zadarma/index.d.ts +2 -2
- package/package.json +1 -1
- package/dist/sources-M3GYMS6V.js.map +0 -1
package/dist/ui/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isWebRtcSupported } from '../chunk-KAXEMJ7E.js';
|
|
2
2
|
import { useSoftphone, useSoftphoneState, useCall, usePresence, useActiveCall, useIsLeader, useLastError, useDevices } from '../chunk-UHNLEBBO.js';
|
|
3
3
|
import '../chunk-7R6LOOAI.js';
|
|
4
|
+
import { isOutboundNumberRefused } from '../chunk-5E5OEXS2.js';
|
|
4
5
|
import { forwardRef, createElement, useState, useRef, useLayoutEffect, useEffect, useCallback, useMemo, useSyncExternalStore, useId } from 'react';
|
|
5
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
7
|
|
|
@@ -367,6 +368,36 @@ var PACKED = "AC247 AD376 AE971 AF93 AG1 AI1 AL355 AM374 AO244 AR54 AS1 AT43 AU6
|
|
|
367
368
|
var DIAL_CODES = new Map(
|
|
368
369
|
PACKED.split(" ").map((token) => [token.slice(0, 2), Number(token.slice(2))])
|
|
369
370
|
);
|
|
371
|
+
var REGIONS_BY_CODE = (() => {
|
|
372
|
+
const map = /* @__PURE__ */ new Map();
|
|
373
|
+
for (const [region, code] of DIAL_CODES) map.set(code, [...map.get(code) ?? [], region]);
|
|
374
|
+
return map;
|
|
375
|
+
})();
|
|
376
|
+
var MAIN_REGION = {
|
|
377
|
+
1: "US",
|
|
378
|
+
7: "RU",
|
|
379
|
+
39: "IT",
|
|
380
|
+
44: "GB",
|
|
381
|
+
47: "NO",
|
|
382
|
+
61: "AU",
|
|
383
|
+
212: "MA",
|
|
384
|
+
262: "RE",
|
|
385
|
+
290: "SH",
|
|
386
|
+
358: "FI",
|
|
387
|
+
590: "GP",
|
|
388
|
+
599: "CW"
|
|
389
|
+
};
|
|
390
|
+
function regionOfInternational(typed) {
|
|
391
|
+
const text = String(typed ?? "").trim();
|
|
392
|
+
if (!text.startsWith("+")) return null;
|
|
393
|
+
const digits = text.slice(1).replace(/\D/g, "");
|
|
394
|
+
for (let length = 1; length <= Math.min(3, digits.length); length++) {
|
|
395
|
+
const code = Number(digits.slice(0, length));
|
|
396
|
+
const regions = REGIONS_BY_CODE.get(code);
|
|
397
|
+
if (regions) return MAIN_REGION[code] ?? regions[0] ?? null;
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
370
401
|
var DEFAULT_COUNTRY = "ES";
|
|
371
402
|
var REGION_NAMES = (() => {
|
|
372
403
|
try {
|
|
@@ -418,12 +449,16 @@ function CountryMark({ code }) {
|
|
|
418
449
|
}
|
|
419
450
|
function CountryPicker({
|
|
420
451
|
value,
|
|
421
|
-
onChange
|
|
452
|
+
onChange,
|
|
453
|
+
fromNumber = false
|
|
422
454
|
}) {
|
|
423
455
|
const [open, setOpen] = useState(false);
|
|
424
456
|
const [query, setQuery] = useState("");
|
|
425
457
|
const rootRef = useRef(null);
|
|
426
458
|
const inputRef = useRef(null);
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
if (fromNumber) setOpen(false);
|
|
461
|
+
}, [fromNumber]);
|
|
427
462
|
const results = useMemo(() => {
|
|
428
463
|
const q = query.trim().toLowerCase().replace(/^\+/, "");
|
|
429
464
|
if (!q) return COUNTRIES;
|
|
@@ -452,6 +487,12 @@ function CountryPicker({
|
|
|
452
487
|
else setQuery("");
|
|
453
488
|
}, [open]);
|
|
454
489
|
const dial = DIAL_CODES.get(value);
|
|
490
|
+
if (fromNumber) {
|
|
491
|
+
return /* @__PURE__ */ jsx("div", { className: "cv-cp", ref: rootRef, children: /* @__PURE__ */ jsxs("span", { className: "cv-cp-chip", "data-from-number": true, title: "El prefijo viene del n\xFAmero marcado", part: "country-chip", children: [
|
|
492
|
+
/* @__PURE__ */ jsx(CountryMark, { code: value || "+" }),
|
|
493
|
+
/* @__PURE__ */ jsx("span", { className: "cv-sr-only", children: dial ? `Prefijo del n\xFAmero marcado: ${countryName(value)} (+${dial})` : "Prefijo del n\xFAmero marcado" })
|
|
494
|
+
] }) });
|
|
495
|
+
}
|
|
455
496
|
return /* @__PURE__ */ jsxs("div", { className: "cv-cp", ref: rootRef, children: [
|
|
456
497
|
/* @__PURE__ */ jsxs(
|
|
457
498
|
"button",
|
|
@@ -1018,18 +1059,378 @@ function playRingback() {
|
|
|
1018
1059
|
}
|
|
1019
1060
|
};
|
|
1020
1061
|
}
|
|
1062
|
+
var stores = /* @__PURE__ */ new WeakMap();
|
|
1063
|
+
function outboundNumberStore(softphone) {
|
|
1064
|
+
const source = softphone.sources?.outboundNumbers;
|
|
1065
|
+
if (!source) return null;
|
|
1066
|
+
let store = stores.get(softphone);
|
|
1067
|
+
if (!store) {
|
|
1068
|
+
store = createOutboundNumberStore(source);
|
|
1069
|
+
stores.set(softphone, store);
|
|
1070
|
+
}
|
|
1071
|
+
return store;
|
|
1072
|
+
}
|
|
1073
|
+
function createOutboundNumberStore(source) {
|
|
1074
|
+
let settings = null;
|
|
1075
|
+
let inFlight = null;
|
|
1076
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1077
|
+
const apply = (next) => {
|
|
1078
|
+
settings = next;
|
|
1079
|
+
for (const l of listeners) l();
|
|
1080
|
+
};
|
|
1081
|
+
return {
|
|
1082
|
+
getSettings: () => settings,
|
|
1083
|
+
refresh() {
|
|
1084
|
+
inFlight ??= (async () => {
|
|
1085
|
+
if (!settings && source.cached) {
|
|
1086
|
+
const early = await source.cached().catch(() => null);
|
|
1087
|
+
if (early && !settings) apply(early);
|
|
1088
|
+
}
|
|
1089
|
+
try {
|
|
1090
|
+
apply(await source.get());
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
console.warn("[softphone] outbound numbers could not be loaded; keeping the previous ones", error);
|
|
1093
|
+
} finally {
|
|
1094
|
+
inFlight = null;
|
|
1095
|
+
}
|
|
1096
|
+
})();
|
|
1097
|
+
return inFlight;
|
|
1098
|
+
},
|
|
1099
|
+
async setMine(id) {
|
|
1100
|
+
apply(await source.setMine(id));
|
|
1101
|
+
},
|
|
1102
|
+
subscribe(listener) {
|
|
1103
|
+
listeners.add(listener);
|
|
1104
|
+
return () => listeners.delete(listener);
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
var noSubscribe = () => () => {
|
|
1109
|
+
};
|
|
1110
|
+
var noSettings = () => null;
|
|
1111
|
+
function useOutboundNumbers(store) {
|
|
1112
|
+
const settings = useSyncExternalStore(store?.subscribe ?? noSubscribe, store?.getSettings ?? noSettings);
|
|
1113
|
+
const [failed, setFailed] = useState(false);
|
|
1114
|
+
const load = useCallback(() => {
|
|
1115
|
+
if (!store) return () => {
|
|
1116
|
+
};
|
|
1117
|
+
let live = true;
|
|
1118
|
+
setFailed(false);
|
|
1119
|
+
void store.refresh().then(() => {
|
|
1120
|
+
if (live) setFailed(store.getSettings() === null);
|
|
1121
|
+
});
|
|
1122
|
+
return () => {
|
|
1123
|
+
live = false;
|
|
1124
|
+
};
|
|
1125
|
+
}, [store]);
|
|
1126
|
+
useEffect(load, [load]);
|
|
1127
|
+
return { settings, failed, retry: load };
|
|
1128
|
+
}
|
|
1129
|
+
function useKnownOutboundNumbers(store) {
|
|
1130
|
+
return useSyncExternalStore(store?.subscribe ?? noSubscribe, store?.getSettings ?? noSettings);
|
|
1131
|
+
}
|
|
1132
|
+
function useCountdown(until) {
|
|
1133
|
+
const [, setTick] = useState(0);
|
|
1134
|
+
useEffect(() => {
|
|
1135
|
+
if (!until) return void 0;
|
|
1136
|
+
const timer = setInterval(() => {
|
|
1137
|
+
setTick((n) => n + 1);
|
|
1138
|
+
if (secondsLeft(until) === 0) clearInterval(timer);
|
|
1139
|
+
}, 1e3);
|
|
1140
|
+
return () => clearInterval(timer);
|
|
1141
|
+
}, [until]);
|
|
1142
|
+
return secondsLeft(until);
|
|
1143
|
+
}
|
|
1144
|
+
function secondsLeft(until) {
|
|
1145
|
+
return until ? Math.max(0, Math.ceil((until - Date.now()) / 1e3)) : 0;
|
|
1146
|
+
}
|
|
1147
|
+
function numberName(n, format = formatIntl) {
|
|
1148
|
+
if (!n) return "N\xFAmero de la organizaci\xF3n";
|
|
1149
|
+
return n.label || format(`+${n.number}`);
|
|
1150
|
+
}
|
|
1151
|
+
function effectiveNumber(settings) {
|
|
1152
|
+
return settings?.numbers.find((n) => n.id === settings.effective);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// src/ui/outbound/outbound-change.ts
|
|
1156
|
+
var KIND_TAG = {
|
|
1157
|
+
mobile: { tag: "M\xF3vil", hint: "No usar para atenci\xF3n al cliente ni para llamadas comerciales no solicitadas." },
|
|
1158
|
+
commercial: { tag: "Comercial (400)", hint: "Solo para llamadas comerciales. No recibe llamadas." }
|
|
1159
|
+
};
|
|
1160
|
+
function noticeText(situation, seconds, current) {
|
|
1161
|
+
switch (situation) {
|
|
1162
|
+
case "platform":
|
|
1163
|
+
return `El cambio de n\xFAmero de salida no est\xE1 disponible temporalmente. Vuelve a intentarlo en ${seconds} s. Hasta entonces, tus llamadas seguir\xE1n saliendo desde ${current}.`;
|
|
1164
|
+
case "user":
|
|
1165
|
+
return `Acabas de cambiar el n\xFAmero de salida. Podr\xE1s cambiarlo de nuevo en ${seconds} s. Hasta entonces, tus llamadas saldr\xE1n desde ${current}.`;
|
|
1166
|
+
case "wait":
|
|
1167
|
+
return `Podr\xE1s cambiar el n\xFAmero de salida en ${seconds} s.`;
|
|
1168
|
+
case "pending":
|
|
1169
|
+
return seconds > 0 ? `Tu n\xFAmero de salida todav\xEDa no se ha aplicado. Podr\xE1s volver a elegirlo en ${seconds} s.` : "Tu n\xFAmero de salida todav\xEDa no se ha aplicado. Vuelve a elegirlo para aplicarlo.";
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
function useOutboundNumberChange(store, settings, current) {
|
|
1173
|
+
const [busy, setBusy] = useState(false);
|
|
1174
|
+
const [notice, setNotice] = useState(null);
|
|
1175
|
+
const [waitUntil, setWaitUntil] = useState(null);
|
|
1176
|
+
useEffect(() => {
|
|
1177
|
+
const s = settings?.changeAvailableInSeconds ?? 0;
|
|
1178
|
+
setWaitUntil(s > 0 ? Date.now() + s * 1e3 : null);
|
|
1179
|
+
}, [settings]);
|
|
1180
|
+
const refusedUntil = notice?.kind === "refused" ? notice.until : null;
|
|
1181
|
+
const blockedFor = useCountdown(refusedUntil ?? waitUntil);
|
|
1182
|
+
useEffect(() => {
|
|
1183
|
+
if (notice?.kind === "refused" && blockedFor === 0) setNotice(null);
|
|
1184
|
+
}, [notice, blockedFor]);
|
|
1185
|
+
const locked = busy || blockedFor > 0;
|
|
1186
|
+
const choose = async (id) => {
|
|
1187
|
+
if (!store || !settings || locked) return false;
|
|
1188
|
+
if (id === settings.mine) return true;
|
|
1189
|
+
setBusy(true);
|
|
1190
|
+
setNotice(null);
|
|
1191
|
+
try {
|
|
1192
|
+
await store.setMine(id);
|
|
1193
|
+
return true;
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
setNotice(
|
|
1196
|
+
isOutboundNumberRefused(error) ? { kind: "refused", reason: error.refusal.reason, until: Date.now() + error.refusal.retryAfterSeconds * 1e3 } : { kind: "error", text: errorText(error) }
|
|
1197
|
+
);
|
|
1198
|
+
return false;
|
|
1199
|
+
} finally {
|
|
1200
|
+
setBusy(false);
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
return {
|
|
1204
|
+
busy,
|
|
1205
|
+
locked,
|
|
1206
|
+
choose,
|
|
1207
|
+
/** What to say about the choice right now, if anything. */
|
|
1208
|
+
message: settings ? currentMessage(notice, settings.state, blockedFor, current) : null,
|
|
1209
|
+
/** The message is an error (the change failed), not information (wait, pending). */
|
|
1210
|
+
isError: notice?.kind === "error"
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
function currentMessage(notice, state, blockedFor, current) {
|
|
1214
|
+
if (notice?.kind === "refused") return blockedFor > 0 ? noticeText(notice.reason, blockedFor, current) : null;
|
|
1215
|
+
if (notice?.kind === "error") return notice.text;
|
|
1216
|
+
if (state === "pending" || state === "failed") return noticeText("pending", blockedFor, current);
|
|
1217
|
+
return blockedFor > 0 ? noticeText("wait", blockedFor, current) : null;
|
|
1218
|
+
}
|
|
1219
|
+
function errorText(error) {
|
|
1220
|
+
const message = error instanceof Error ? error.message : "";
|
|
1221
|
+
return message && !/HTTP \d+/.test(message) ? message : "No se ha podido cambiar el n\xFAmero de salida. Int\xE9ntalo de nuevo.";
|
|
1222
|
+
}
|
|
1223
|
+
var CALLOUT_MS = 6e3;
|
|
1224
|
+
function OutboundNumberPicker({ onBusyChange }) {
|
|
1225
|
+
const softphone = useSoftphone();
|
|
1226
|
+
const store = outboundNumberStore(softphone);
|
|
1227
|
+
const { settings } = useOutboundNumbers(store);
|
|
1228
|
+
const formatIntl2 = useIntlFormatter();
|
|
1229
|
+
const current = settings ? numberName(effectiveNumber(settings), formatIntl2) : "";
|
|
1230
|
+
const { busy, locked, choose: apply, message, isError } = useOutboundNumberChange(store, settings, current);
|
|
1231
|
+
const [open, setOpen] = useState(false);
|
|
1232
|
+
const [applying, setApplying] = useState(void 0);
|
|
1233
|
+
const [callout, setCallout] = useState(false);
|
|
1234
|
+
const rootRef = useRef(null);
|
|
1235
|
+
const chipRef = useRef(null);
|
|
1236
|
+
const keys = useListKeyboard(open);
|
|
1237
|
+
useEffect(() => onBusyChange?.(busy), [busy, onBusyChange]);
|
|
1238
|
+
useEffect(() => {
|
|
1239
|
+
if (!message) setCallout(false);
|
|
1240
|
+
}, [message]);
|
|
1241
|
+
useEffect(() => {
|
|
1242
|
+
if (!callout) return void 0;
|
|
1243
|
+
const timer = setTimeout(() => setCallout(false), CALLOUT_MS);
|
|
1244
|
+
return () => clearTimeout(timer);
|
|
1245
|
+
}, [callout]);
|
|
1246
|
+
const floating = open || callout;
|
|
1247
|
+
useEffect(() => {
|
|
1248
|
+
if (!floating) return;
|
|
1249
|
+
const close = () => {
|
|
1250
|
+
setOpen(false);
|
|
1251
|
+
setCallout(false);
|
|
1252
|
+
};
|
|
1253
|
+
const root = rootRef.current?.getRootNode() ?? document;
|
|
1254
|
+
const onDown = (e) => {
|
|
1255
|
+
if (!rootRef.current?.contains(e.target)) close();
|
|
1256
|
+
};
|
|
1257
|
+
const onKey = (e) => {
|
|
1258
|
+
if (e.key !== "Escape") return;
|
|
1259
|
+
if (rootRef.current?.contains(e.target)) chipRef.current?.focus();
|
|
1260
|
+
close();
|
|
1261
|
+
};
|
|
1262
|
+
root.addEventListener("pointerdown", onDown);
|
|
1263
|
+
document.addEventListener("keydown", onKey);
|
|
1264
|
+
return () => {
|
|
1265
|
+
root.removeEventListener("pointerdown", onDown);
|
|
1266
|
+
document.removeEventListener("keydown", onKey);
|
|
1267
|
+
};
|
|
1268
|
+
}, [floating]);
|
|
1269
|
+
if (!store || !settings || settings.numbers.length < 2) return null;
|
|
1270
|
+
const orgDefault = settings.numbers.find((n) => n.id === settings.orgDefault);
|
|
1271
|
+
const applyingNumber = applying === null ? orgDefault : settings.numbers.find((n) => n.id === applying);
|
|
1272
|
+
const shown = applying === void 0 ? current : numberName(applyingNumber, formatIntl2);
|
|
1273
|
+
const choose = async (id) => {
|
|
1274
|
+
if (locked) return;
|
|
1275
|
+
setOpen(false);
|
|
1276
|
+
chipRef.current?.focus();
|
|
1277
|
+
if (id === settings.mine) return;
|
|
1278
|
+
setCallout(false);
|
|
1279
|
+
setApplying(id);
|
|
1280
|
+
const applied = await apply(id);
|
|
1281
|
+
setApplying(void 0);
|
|
1282
|
+
if (!applied) setCallout(true);
|
|
1283
|
+
};
|
|
1284
|
+
return /* @__PURE__ */ jsxs("div", { className: "cv-on", ref: rootRef, children: [
|
|
1285
|
+
/* @__PURE__ */ jsxs(
|
|
1286
|
+
"button",
|
|
1287
|
+
{
|
|
1288
|
+
ref: chipRef,
|
|
1289
|
+
type: "button",
|
|
1290
|
+
className: "cv-on-chip",
|
|
1291
|
+
onClick: () => {
|
|
1292
|
+
setCallout(false);
|
|
1293
|
+
setOpen((o) => !o);
|
|
1294
|
+
},
|
|
1295
|
+
"aria-haspopup": "listbox",
|
|
1296
|
+
"aria-expanded": open,
|
|
1297
|
+
"aria-label": `Llamar desde: ${shown}${busy ? " (aplicando)" : ""}. Cambiar n\xFAmero de salida`,
|
|
1298
|
+
"aria-busy": busy,
|
|
1299
|
+
part: "outbound-number-chip",
|
|
1300
|
+
children: [
|
|
1301
|
+
/* @__PURE__ */ jsx("span", { className: "cv-on-label", children: "Llamar desde:" }),
|
|
1302
|
+
busy ? /* @__PURE__ */ jsx(LoaderCircle, { size: 13, className: "cv-on-icon cv-on-spin", "aria-hidden": true }) : null,
|
|
1303
|
+
/* @__PURE__ */ jsx("span", { className: "cv-on-current", children: shown }),
|
|
1304
|
+
/* @__PURE__ */ jsx(ChevronDown, { size: 13, className: "cv-cp-caret", "data-open": open, "aria-hidden": true })
|
|
1305
|
+
]
|
|
1306
|
+
}
|
|
1307
|
+
),
|
|
1308
|
+
open ? /* @__PURE__ */ jsxs(
|
|
1309
|
+
"div",
|
|
1310
|
+
{
|
|
1311
|
+
className: "cv-cp-pop cv-on-pop",
|
|
1312
|
+
role: "dialog",
|
|
1313
|
+
"aria-label": "Llamar desde",
|
|
1314
|
+
part: "outbound-number-list",
|
|
1315
|
+
onBlur: (e) => {
|
|
1316
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setOpen(false);
|
|
1317
|
+
},
|
|
1318
|
+
children: [
|
|
1319
|
+
/* @__PURE__ */ jsx("p", { className: "cv-on-title", children: "Llamar desde" }),
|
|
1320
|
+
/* @__PURE__ */ jsxs("div", { className: "cv-cp-list", role: "listbox", "aria-disabled": locked, ref: keys.listRef, onKeyDown: keys.onKeyDown, children: [
|
|
1321
|
+
/* @__PURE__ */ jsx(
|
|
1322
|
+
OptionRow,
|
|
1323
|
+
{
|
|
1324
|
+
selected: settings.mine === null,
|
|
1325
|
+
disabled: locked,
|
|
1326
|
+
title: "Por defecto de la organizaci\xF3n",
|
|
1327
|
+
sub: orgDefault ? numberName(orgDefault, formatIntl2) : "Sin n\xFAmero por defecto",
|
|
1328
|
+
onSelect: () => void choose(null)
|
|
1329
|
+
}
|
|
1330
|
+
),
|
|
1331
|
+
settings.numbers.map((n) => /* @__PURE__ */ jsx(
|
|
1332
|
+
OptionRow,
|
|
1333
|
+
{
|
|
1334
|
+
selected: settings.mine === n.id,
|
|
1335
|
+
disabled: locked,
|
|
1336
|
+
title: numberName(n, formatIntl2),
|
|
1337
|
+
sub: n.label ? formatIntl2(`+${n.number}`) : null,
|
|
1338
|
+
tag: KIND_TAG[n.kind],
|
|
1339
|
+
onSelect: () => void choose(n.id)
|
|
1340
|
+
},
|
|
1341
|
+
n.id
|
|
1342
|
+
))
|
|
1343
|
+
] }),
|
|
1344
|
+
message ? /* @__PURE__ */ jsx("p", { className: "cv-on-notice", role: "status", "data-kind": isError ? "error" : "info", children: message }) : null
|
|
1345
|
+
]
|
|
1346
|
+
}
|
|
1347
|
+
) : null,
|
|
1348
|
+
callout && !open && message ? /* @__PURE__ */ jsx("p", { className: "cv-on-callout", role: "status", "data-kind": isError ? "error" : "info", part: "outbound-number-notice", children: message }) : null
|
|
1349
|
+
] });
|
|
1350
|
+
}
|
|
1351
|
+
function OptionRow({
|
|
1352
|
+
selected,
|
|
1353
|
+
disabled,
|
|
1354
|
+
title,
|
|
1355
|
+
sub,
|
|
1356
|
+
tag,
|
|
1357
|
+
onSelect
|
|
1358
|
+
}) {
|
|
1359
|
+
return /* @__PURE__ */ jsxs(
|
|
1360
|
+
"button",
|
|
1361
|
+
{
|
|
1362
|
+
type: "button",
|
|
1363
|
+
role: "option",
|
|
1364
|
+
"aria-selected": selected,
|
|
1365
|
+
"aria-disabled": disabled,
|
|
1366
|
+
className: "cv-cp-item cv-on-item",
|
|
1367
|
+
tabIndex: -1,
|
|
1368
|
+
onClick: onSelect,
|
|
1369
|
+
children: [
|
|
1370
|
+
/* @__PURE__ */ jsxs("span", { className: "cv-on-text", children: [
|
|
1371
|
+
/* @__PURE__ */ jsxs("span", { className: "cv-on-line", children: [
|
|
1372
|
+
/* @__PURE__ */ jsx("span", { className: "cv-cp-name", children: title }),
|
|
1373
|
+
tag ? /* @__PURE__ */ jsx("span", { className: "cv-on-tag", children: tag.tag }) : null
|
|
1374
|
+
] }),
|
|
1375
|
+
sub ? /* @__PURE__ */ jsx("span", { className: "cv-on-sub", children: sub }) : null,
|
|
1376
|
+
tag ? /* @__PURE__ */ jsx("span", { className: "cv-on-hint", children: tag.hint }) : null
|
|
1377
|
+
] }),
|
|
1378
|
+
/* @__PURE__ */ jsx(Check, { size: 14, className: "cv-cp-check", "data-on": selected, "aria-hidden": true })
|
|
1379
|
+
]
|
|
1380
|
+
}
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
var optionsOf = (list) => list ? [...list.querySelectorAll('[role="option"]')] : [];
|
|
1384
|
+
var fold = (text) => text.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase();
|
|
1385
|
+
var TYPEAHEAD_MS = 700;
|
|
1386
|
+
function useListKeyboard(open) {
|
|
1387
|
+
const listRef = useRef(null);
|
|
1388
|
+
const typed = useRef({ text: "", at: 0 });
|
|
1389
|
+
useEffect(() => {
|
|
1390
|
+
if (!open) return;
|
|
1391
|
+
const options = optionsOf(listRef.current);
|
|
1392
|
+
(options.find((o) => o.getAttribute("aria-selected") === "true") ?? options[0])?.focus();
|
|
1393
|
+
}, [open]);
|
|
1394
|
+
const onKeyDown = (e) => {
|
|
1395
|
+
const options = optionsOf(listRef.current);
|
|
1396
|
+
const at = options.indexOf(e.target);
|
|
1397
|
+
const move = {
|
|
1398
|
+
ArrowDown: Math.min(at + 1, options.length - 1),
|
|
1399
|
+
ArrowUp: Math.max(at - 1, 0),
|
|
1400
|
+
Home: 0,
|
|
1401
|
+
End: options.length - 1
|
|
1402
|
+
};
|
|
1403
|
+
let next = move[e.key];
|
|
1404
|
+
if (next === void 0 && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1405
|
+
const now = Date.now();
|
|
1406
|
+
const text = now - typed.current.at < TYPEAHEAD_MS ? typed.current.text + e.key : e.key;
|
|
1407
|
+
typed.current = { text, at: now };
|
|
1408
|
+
const wanted = fold(text);
|
|
1409
|
+
const match = options.findIndex((o) => fold(o.querySelector(".cv-cp-name")?.textContent ?? "").startsWith(wanted));
|
|
1410
|
+
next = match >= 0 ? match : void 0;
|
|
1411
|
+
}
|
|
1412
|
+
if (next === void 0) return;
|
|
1413
|
+
e.preventDefault();
|
|
1414
|
+
options[next]?.focus();
|
|
1415
|
+
};
|
|
1416
|
+
return { listRef, onKeyDown };
|
|
1417
|
+
}
|
|
1021
1418
|
var NOT_DIALABLE = /* @__PURE__ */ new Set(["*", "#"]);
|
|
1419
|
+
var DISPLAY_SIZES = ["1.375rem", "1.25rem", "1.125rem", "1rem"];
|
|
1022
1420
|
function Dialpad({ outcome = null, onIntent, keyTones = true } = {}) {
|
|
1023
1421
|
const softphone = useSoftphone();
|
|
1024
1422
|
const state = useSoftphoneState();
|
|
1025
1423
|
const [value, setValue] = useState("");
|
|
1026
1424
|
const [country, setCountry] = useState(DEFAULT_COUNTRY);
|
|
1425
|
+
const [changingNumber, setChangingNumber] = useState(false);
|
|
1027
1426
|
const format = useNumberFormatter();
|
|
1028
1427
|
const inputRef = useRef(null);
|
|
1029
1428
|
const caretRef = useRef(null);
|
|
1030
1429
|
const display = format(country, value);
|
|
1031
1430
|
const target = toE164(country, value);
|
|
1032
|
-
const canCall = state === "online" && target.length > 1;
|
|
1431
|
+
const canCall = state === "online" && target.length > 1 && !changingNumber;
|
|
1432
|
+
const typedInternational = value.trim().startsWith("+");
|
|
1433
|
+
const numberRegion = regionOfInternational(value);
|
|
1033
1434
|
useLayoutEffect(() => {
|
|
1034
1435
|
const caret = caretRef.current;
|
|
1035
1436
|
if (caret === null || !inputRef.current) return;
|
|
@@ -1037,6 +1438,14 @@ function Dialpad({ outcome = null, onIntent, keyTones = true } = {}) {
|
|
|
1037
1438
|
const pos = caretAfterDigits(display, caret);
|
|
1038
1439
|
inputRef.current.setSelectionRange(pos, pos);
|
|
1039
1440
|
}, [display]);
|
|
1441
|
+
useLayoutEffect(() => {
|
|
1442
|
+
const input = inputRef.current;
|
|
1443
|
+
if (!input) return;
|
|
1444
|
+
for (const size of DISPLAY_SIZES) {
|
|
1445
|
+
input.style.fontSize = size;
|
|
1446
|
+
if (input.scrollWidth <= input.clientWidth) break;
|
|
1447
|
+
}
|
|
1448
|
+
}, [display]);
|
|
1040
1449
|
const append = (key) => {
|
|
1041
1450
|
onIntent?.();
|
|
1042
1451
|
setValue((v) => v + key);
|
|
@@ -1052,9 +1461,16 @@ function Dialpad({ outcome = null, onIntent, keyTones = true } = {}) {
|
|
|
1052
1461
|
void softphone.call(target);
|
|
1053
1462
|
};
|
|
1054
1463
|
return /* @__PURE__ */ jsxs("div", { className: "cv-view", children: [
|
|
1055
|
-
/* @__PURE__ */ jsx(
|
|
1464
|
+
/* @__PURE__ */ jsx(OutboundNumberPicker, { onBusyChange: setChangingNumber }),
|
|
1056
1465
|
/* @__PURE__ */ jsxs("div", { className: "cv-dial-head", children: [
|
|
1057
|
-
/* @__PURE__ */ jsx(
|
|
1466
|
+
/* @__PURE__ */ jsx(
|
|
1467
|
+
CountryPicker,
|
|
1468
|
+
{
|
|
1469
|
+
value: numberRegion ?? country,
|
|
1470
|
+
onChange: setCountry,
|
|
1471
|
+
fromNumber: typedInternational
|
|
1472
|
+
}
|
|
1473
|
+
),
|
|
1058
1474
|
/* @__PURE__ */ jsx(
|
|
1059
1475
|
"input",
|
|
1060
1476
|
{
|
|
@@ -1078,6 +1494,8 @@ function Dialpad({ outcome = null, onIntent, keyTones = true } = {}) {
|
|
|
1078
1494
|
type: "button",
|
|
1079
1495
|
className: "cv-dial-side",
|
|
1080
1496
|
"data-hidden": value.length === 0,
|
|
1497
|
+
tabIndex: value.length === 0 ? -1 : void 0,
|
|
1498
|
+
"aria-hidden": value.length === 0 || void 0,
|
|
1081
1499
|
onClick: backspace,
|
|
1082
1500
|
"aria-label": "Borrar",
|
|
1083
1501
|
children: /* @__PURE__ */ jsx(Delete, { size: 19 })
|
|
@@ -1502,6 +1920,9 @@ function CallScreen({
|
|
|
1502
1920
|
}) {
|
|
1503
1921
|
const view = useCall(call);
|
|
1504
1922
|
const softphone = useSoftphone();
|
|
1923
|
+
const outbound = useOutboundNumbers(call.direction === "outbound" ? outboundNumberStore(softphone) : null).settings;
|
|
1924
|
+
const formatIntl2 = useIntlFormatter();
|
|
1925
|
+
const fromName = outbound && outbound.numbers.length > 1 ? numberName(effectiveNumber(outbound), formatIntl2) : null;
|
|
1505
1926
|
const [panel, setPanel] = useState(null);
|
|
1506
1927
|
const showTransfer = panel === "transfer";
|
|
1507
1928
|
const showKeypad = panel === "keypad";
|
|
@@ -1712,6 +2133,13 @@ function CallScreen({
|
|
|
1712
2133
|
] }),
|
|
1713
2134
|
/* @__PURE__ */ jsx("div", { className: "cv-call-name", children: name }),
|
|
1714
2135
|
showNumber ? /* @__PURE__ */ jsx("div", { className: "cv-call-number", children: number }) : null,
|
|
2136
|
+
fromName ? /* @__PURE__ */ jsxs("div", { className: "cv-call-from", children: [
|
|
2137
|
+
/* @__PURE__ */ jsx(PhoneOutgoing, { size: 11, "aria-hidden": true }),
|
|
2138
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
2139
|
+
"Desde ",
|
|
2140
|
+
fromName
|
|
2141
|
+
] })
|
|
2142
|
+
] }) : null,
|
|
1715
2143
|
s === "active" ? /* @__PURE__ */ jsxs("div", { className: "cv-timer", "aria-label": "Duraci\xF3n de la llamada", children: [
|
|
1716
2144
|
/* @__PURE__ */ jsx("b", { children: mm }),
|
|
1717
2145
|
/* @__PURE__ */ jsx("i", { children: ":" }),
|
|
@@ -2127,12 +2555,23 @@ function HandlerChain({ handlers }) {
|
|
|
2127
2555
|
] }, `${h.extension}-${i}`);
|
|
2128
2556
|
}) });
|
|
2129
2557
|
}
|
|
2558
|
+
function outboundFrom(entry, known, formatIntl2) {
|
|
2559
|
+
if (entry.direction !== "outbound" || !entry.from) return null;
|
|
2560
|
+
const number = formatIntl2(`+${entry.from}`);
|
|
2561
|
+
const label = known?.numbers.find((n) => n.number === entry.from)?.label;
|
|
2562
|
+
if (!label) return number;
|
|
2563
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2564
|
+
label,
|
|
2565
|
+
/* @__PURE__ */ jsx("span", { className: "cv-detail-row-sub", children: number })
|
|
2566
|
+
] });
|
|
2567
|
+
}
|
|
2130
2568
|
function HistoryDetail({
|
|
2131
2569
|
entry,
|
|
2132
2570
|
onBack
|
|
2133
2571
|
}) {
|
|
2134
2572
|
const softphone = useSoftphone();
|
|
2135
2573
|
const formatIntl2 = useIntlFormatter();
|
|
2574
|
+
const knownNumbers = useKnownOutboundNumbers(outboundNumberStore(softphone));
|
|
2136
2575
|
const [detail, setDetail] = useState(null);
|
|
2137
2576
|
const [loadingDetail, setLoadingDetail] = useState(false);
|
|
2138
2577
|
useEffect(() => {
|
|
@@ -2148,6 +2587,7 @@ function HistoryDetail({
|
|
|
2148
2587
|
}, [entry]);
|
|
2149
2588
|
const formatted = formatIntl2(entry.number);
|
|
2150
2589
|
const name = entry.name ?? formatted;
|
|
2590
|
+
const from = outboundFrom(entry, knownNumbers, formatIntl2);
|
|
2151
2591
|
const handledBy = detail?.handledBy ?? entry.handledBy;
|
|
2152
2592
|
const recordingUrls = detail?.recordingUrls ?? [];
|
|
2153
2593
|
const loadingRecording = entry.recorded && loadingDetail && recordingUrls.length === 0;
|
|
@@ -2184,7 +2624,8 @@ function HistoryDetail({
|
|
|
2184
2624
|
entry.durationSec > 0 ? /* @__PURE__ */ jsx(DetailRow, { icon: Timer, eyebrow: "Duraci\xF3n", text: formatDuration(entry.durationSec) }) : null,
|
|
2185
2625
|
/* @__PURE__ */ jsx(DetailRow, { icon: Phone, eyebrow: "N\xFAmero", text: formatted }),
|
|
2186
2626
|
handledBy.length > 0 ? /* @__PURE__ */ jsx(DetailRow, { icon: User, eyebrow: handlerLabel, text: /* @__PURE__ */ jsx(HandlerChain, { handlers: handledBy }) }) : null,
|
|
2187
|
-
entry.direction === "inbound" && entry.did ? /* @__PURE__ */ jsx(DetailRow, { icon: Hash, eyebrow: "N\xFAmero marcado (DID)", text: formatIntl2(entry.did) }) : null
|
|
2627
|
+
entry.direction === "inbound" && entry.did ? /* @__PURE__ */ jsx(DetailRow, { icon: Hash, eyebrow: "N\xFAmero marcado (DID)", text: formatIntl2(entry.did) }) : null,
|
|
2628
|
+
from ? /* @__PURE__ */ jsx(DetailRow, { icon: PhoneOutgoing, eyebrow: "Llamada desde", text: from }) : null
|
|
2188
2629
|
] })
|
|
2189
2630
|
] }),
|
|
2190
2631
|
/* @__PURE__ */ jsx("div", { className: "cv-detail-foot", children: /* @__PURE__ */ jsxs(
|
|
@@ -2379,14 +2820,14 @@ function HistoryTab({ source }) {
|
|
|
2379
2820
|
}
|
|
2380
2821
|
|
|
2381
2822
|
// src/ui/audio/ringtone-store.ts
|
|
2382
|
-
var
|
|
2823
|
+
var stores2 = /* @__PURE__ */ new WeakMap();
|
|
2383
2824
|
function ringtoneStore(softphone) {
|
|
2384
2825
|
const source = softphone.sources?.ringtones;
|
|
2385
2826
|
if (!source) return null;
|
|
2386
|
-
let store =
|
|
2827
|
+
let store = stores2.get(softphone);
|
|
2387
2828
|
if (!store) {
|
|
2388
2829
|
store = createRingtoneStore(source);
|
|
2389
|
-
|
|
2830
|
+
stores2.set(softphone, store);
|
|
2390
2831
|
}
|
|
2391
2832
|
return store;
|
|
2392
2833
|
}
|
|
@@ -2512,17 +2953,19 @@ function ChoiceList({
|
|
|
2512
2953
|
choices,
|
|
2513
2954
|
value,
|
|
2514
2955
|
onSelect,
|
|
2515
|
-
onReselect
|
|
2956
|
+
onReselect,
|
|
2957
|
+
disabled = false
|
|
2516
2958
|
}) {
|
|
2517
2959
|
const name = useId();
|
|
2518
2960
|
const pointer = useRef(false);
|
|
2519
|
-
return /* @__PURE__ */ jsx("div", { className: "cv-choices", role: "radiogroup", "aria-label": label, children: choices.map((c) => {
|
|
2961
|
+
return /* @__PURE__ */ jsx("div", { className: "cv-choices", role: "radiogroup", "aria-label": label, "aria-disabled": disabled || void 0, children: choices.map((c) => {
|
|
2520
2962
|
const checked = c.value === value;
|
|
2521
2963
|
return /* @__PURE__ */ jsxs(
|
|
2522
2964
|
"label",
|
|
2523
2965
|
{
|
|
2524
2966
|
className: "cv-choice",
|
|
2525
2967
|
"data-checked": checked || void 0,
|
|
2968
|
+
"data-disabled": disabled || void 0,
|
|
2526
2969
|
onPointerDown: () => {
|
|
2527
2970
|
pointer.current = true;
|
|
2528
2971
|
},
|
|
@@ -2538,19 +2981,23 @@ function ChoiceList({
|
|
|
2538
2981
|
onChange: () => {
|
|
2539
2982
|
const via = pointer.current ? "pointer" : "keyboard";
|
|
2540
2983
|
pointer.current = false;
|
|
2541
|
-
onSelect(c.value, via);
|
|
2984
|
+
if (!disabled) onSelect(c.value, via);
|
|
2542
2985
|
},
|
|
2543
2986
|
onClick: () => {
|
|
2544
2987
|
if (c.value === value) {
|
|
2545
2988
|
pointer.current = false;
|
|
2546
|
-
onReselect?.(c.value);
|
|
2989
|
+
if (!disabled) onReselect?.(c.value);
|
|
2547
2990
|
}
|
|
2548
2991
|
}
|
|
2549
2992
|
}
|
|
2550
2993
|
),
|
|
2551
2994
|
/* @__PURE__ */ jsxs("span", { className: "cv-choice-text", children: [
|
|
2552
|
-
/* @__PURE__ */
|
|
2553
|
-
|
|
2995
|
+
c.tag ? /* @__PURE__ */ jsxs("span", { className: "cv-choice-line", children: [
|
|
2996
|
+
/* @__PURE__ */ jsx("span", { className: "cv-choice-label", children: c.label }),
|
|
2997
|
+
/* @__PURE__ */ jsx("span", { className: "cv-choice-tag", children: c.tag })
|
|
2998
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "cv-choice-label", children: c.label }),
|
|
2999
|
+
c.sub ? /* @__PURE__ */ jsx("span", { className: "cv-choice-sub", children: c.sub }) : null,
|
|
3000
|
+
c.hint ? /* @__PURE__ */ jsx("span", { className: "cv-choice-hint", children: c.hint }) : null
|
|
2554
3001
|
] }),
|
|
2555
3002
|
c.trailing,
|
|
2556
3003
|
/* @__PURE__ */ jsx(Check, { size: 16, className: "cv-choice-check", "aria-hidden": true })
|
|
@@ -2712,11 +3159,11 @@ function previewRingtone(ringtone, options = {}) {
|
|
|
2712
3159
|
}
|
|
2713
3160
|
|
|
2714
3161
|
// src/ui/views/settings/ringtone-state.ts
|
|
2715
|
-
var
|
|
3162
|
+
var noSubscribe2 = () => () => {
|
|
2716
3163
|
};
|
|
2717
|
-
var
|
|
3164
|
+
var noSettings2 = () => null;
|
|
2718
3165
|
function useRingtoneSettings(store) {
|
|
2719
|
-
const settings = useSyncExternalStore(store?.subscribe ??
|
|
3166
|
+
const settings = useSyncExternalStore(store?.subscribe ?? noSubscribe2, store?.getSettings ?? noSettings2);
|
|
2720
3167
|
const [failed, setFailed] = useState(false);
|
|
2721
3168
|
const load = useCallback(() => {
|
|
2722
3169
|
if (!store) return () => {
|
|
@@ -2917,11 +3364,109 @@ function useSerialSave(store, handlers) {
|
|
|
2917
3364
|
});
|
|
2918
3365
|
return save;
|
|
2919
3366
|
}
|
|
3367
|
+
var ORG_DEFAULT2 = "";
|
|
3368
|
+
var KEYBOARD_SETTLE_MS2 = 800;
|
|
3369
|
+
function OutboundNumberPage({
|
|
3370
|
+
store,
|
|
3371
|
+
settings,
|
|
3372
|
+
onBack
|
|
3373
|
+
}) {
|
|
3374
|
+
const formatIntl2 = useIntlFormatter();
|
|
3375
|
+
const current = numberName(effectiveNumber(settings), formatIntl2);
|
|
3376
|
+
const change = useOutboundNumberChange(store, settings, current);
|
|
3377
|
+
const [pending, setPending] = useState(void 0);
|
|
3378
|
+
const settle = useRef(null);
|
|
3379
|
+
const choose = useRef(change.choose);
|
|
3380
|
+
choose.current = change.choose;
|
|
3381
|
+
useEffect(
|
|
3382
|
+
() => () => {
|
|
3383
|
+
if (settle.current) clearTimeout(settle.current);
|
|
3384
|
+
},
|
|
3385
|
+
[]
|
|
3386
|
+
);
|
|
3387
|
+
const notice = useRef(null);
|
|
3388
|
+
const apply = async (value) => {
|
|
3389
|
+
setPending(value);
|
|
3390
|
+
const applied = await choose.current(value || null);
|
|
3391
|
+
setPending(void 0);
|
|
3392
|
+
if (!applied) requestAnimationFrame(() => notice.current?.scrollIntoView?.({ block: "nearest" }));
|
|
3393
|
+
};
|
|
3394
|
+
const onSelect = (value, via) => {
|
|
3395
|
+
if (settle.current) clearTimeout(settle.current);
|
|
3396
|
+
settle.current = null;
|
|
3397
|
+
if (via === "pointer") {
|
|
3398
|
+
void apply(value);
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
setPending(value);
|
|
3402
|
+
settle.current = setTimeout(() => {
|
|
3403
|
+
settle.current = null;
|
|
3404
|
+
void apply(value);
|
|
3405
|
+
}, KEYBOARD_SETTLE_MS2);
|
|
3406
|
+
};
|
|
3407
|
+
return /* @__PURE__ */ jsxs(
|
|
3408
|
+
SettingsPage,
|
|
3409
|
+
{
|
|
3410
|
+
title: "N\xFAmero de salida",
|
|
3411
|
+
intro: "El n\xFAmero que ve la persona a la que llamas. Es el mismo en todos tus dispositivos y tambi\xE9n puedes cambiarlo desde el teclado.",
|
|
3412
|
+
onBack,
|
|
3413
|
+
children: [
|
|
3414
|
+
change.message ? /* @__PURE__ */ jsx("p", { className: "cv-settings-notice", role: "status", "data-kind": change.isError ? "error" : "info", ref: notice, children: change.message }) : null,
|
|
3415
|
+
/* @__PURE__ */ jsx(
|
|
3416
|
+
ChoiceList,
|
|
3417
|
+
{
|
|
3418
|
+
label: "N\xFAmero de salida",
|
|
3419
|
+
choices: choicesFor2(settings, formatIntl2, change.busy ? pending : void 0),
|
|
3420
|
+
value: pending ?? currentValue2(settings),
|
|
3421
|
+
onSelect,
|
|
3422
|
+
disabled: change.locked
|
|
3423
|
+
}
|
|
3424
|
+
)
|
|
3425
|
+
]
|
|
3426
|
+
}
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
function currentValue2(settings) {
|
|
3430
|
+
return settings.mine && settings.numbers.some((n) => n.id === settings.mine) ? settings.mine : ORG_DEFAULT2;
|
|
3431
|
+
}
|
|
3432
|
+
function choicesFor2(settings, formatIntl2, applying) {
|
|
3433
|
+
const orgDefault = settings.numbers.find((n) => n.id === settings.orgDefault);
|
|
3434
|
+
const spinner = (value) => applying === value ? /* @__PURE__ */ jsxs("span", { className: "cv-choice-applying", children: [
|
|
3435
|
+
/* @__PURE__ */ jsx(LoaderCircle, { size: 15, className: "cv-choice-applying-icon", "aria-hidden": true }),
|
|
3436
|
+
/* @__PURE__ */ jsx("span", { className: "cv-sr-only", children: "Aplicando" })
|
|
3437
|
+
] }) : void 0;
|
|
3438
|
+
return [
|
|
3439
|
+
{
|
|
3440
|
+
value: ORG_DEFAULT2,
|
|
3441
|
+
label: "Por defecto de la organizaci\xF3n",
|
|
3442
|
+
sub: orgDefault ? numberName(orgDefault, formatIntl2) : "Sin n\xFAmero por defecto",
|
|
3443
|
+
trailing: spinner(ORG_DEFAULT2)
|
|
3444
|
+
},
|
|
3445
|
+
...settings.numbers.map((n) => {
|
|
3446
|
+
const kind = KIND_TAG[n.kind];
|
|
3447
|
+
return {
|
|
3448
|
+
value: n.id,
|
|
3449
|
+
label: numberName(n, formatIntl2),
|
|
3450
|
+
...n.label ? { sub: formatIntl2(`+${n.number}`) } : {},
|
|
3451
|
+
...kind ? { tag: kind.tag, hint: kind.hint } : {},
|
|
3452
|
+
trailing: spinner(n.id)
|
|
3453
|
+
};
|
|
3454
|
+
})
|
|
3455
|
+
];
|
|
3456
|
+
}
|
|
3457
|
+
function outboundSummary(settings, formatIntl2) {
|
|
3458
|
+
const effective = effectiveNumber(settings);
|
|
3459
|
+
const name = numberName(effective, formatIntl2);
|
|
3460
|
+
return settings.mine || !effective ? name : `${name} \xB7 organizaci\xF3n`;
|
|
3461
|
+
}
|
|
2920
3462
|
function SettingsTab() {
|
|
2921
3463
|
const softphone = useSoftphone();
|
|
2922
3464
|
const devices = useDevices();
|
|
2923
3465
|
const store = ringtoneStore(softphone);
|
|
2924
3466
|
const ringtone = useRingtoneSettings(store);
|
|
3467
|
+
const outStore = outboundNumberStore(softphone);
|
|
3468
|
+
const outbound = useOutboundNumbers(outStore).settings;
|
|
3469
|
+
const formatIntl2 = useIntlFormatter();
|
|
2925
3470
|
const [page, setPage] = useState(null);
|
|
2926
3471
|
const list = useRef(null);
|
|
2927
3472
|
const rows = useRef({});
|
|
@@ -2956,6 +3501,16 @@ function SettingsTab() {
|
|
|
2956
3501
|
rowRef: rowRef("tone")
|
|
2957
3502
|
}
|
|
2958
3503
|
) }) : null,
|
|
3504
|
+
outStore && outbound && outbound.numbers.length > 1 ? /* @__PURE__ */ jsx(SettingsSection, { title: "Llamadas salientes", children: /* @__PURE__ */ jsx(
|
|
3505
|
+
SettingsNavRow,
|
|
3506
|
+
{
|
|
3507
|
+
icon: PhoneOutgoing,
|
|
3508
|
+
title: "N\xFAmero de salida",
|
|
3509
|
+
value: outboundSummary(outbound, formatIntl2),
|
|
3510
|
+
onOpen: open("outbound"),
|
|
3511
|
+
rowRef: rowRef("outbound")
|
|
3512
|
+
}
|
|
3513
|
+
) }) : null,
|
|
2959
3514
|
/* @__PURE__ */ jsxs(SettingsSection, { title: "Dispositivos", children: [
|
|
2960
3515
|
/* @__PURE__ */ jsx(
|
|
2961
3516
|
SettingsNavRow,
|
|
@@ -3009,7 +3564,7 @@ function SettingsTab() {
|
|
|
3009
3564
|
onRetry: ringtone.retry,
|
|
3010
3565
|
onBack: back
|
|
3011
3566
|
}
|
|
3012
|
-
) : page !== null && page !== "tone" ? /* @__PURE__ */ jsx(DevicePage, { kind: page, onBack: back }) : null
|
|
3567
|
+
) : page === "outbound" && outStore && outbound ? /* @__PURE__ */ jsx(OutboundNumberPage, { store: outStore, settings: outbound, onBack: back }) : page !== null && page !== "tone" && page !== "outbound" ? /* @__PURE__ */ jsx(DevicePage, { kind: page, onBack: back }) : null
|
|
3013
3568
|
] });
|
|
3014
3569
|
}
|
|
3015
3570
|
function useMicPermission() {
|