@adatechnology/conversations-ui 0.13.0 → 0.14.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/chunk-RLUNDWGA.js +51 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -43
- package/dist/participant/index.d.ts +74 -2
- package/dist/participant/index.js +1058 -399
- package/dist/styles.css +213 -0
- package/package.json +3 -3
- package/src/conversationTranscript.ts +6 -2
- package/src/participant/ConversationThread.tsx +4 -0
- package/src/participant/ParticipantAuthorAvatar.tsx +7 -1
- package/src/participant/ParticipantBubbleBody.tsx +18 -3
- package/src/participant/ParticipantConversationMenu.tsx +89 -0
- package/src/participant/ParticipantConversations.tsx +3 -0
- package/src/participant/ParticipantMenuBubble.tsx +106 -0
- package/src/participant/ParticipantMessageBubble.tsx +34 -7
- package/src/participant/ParticipantMessageInfo.tsx +70 -0
- package/src/participant/ParticipantMessageMenu.tsx +168 -0
- package/src/participant/ParticipantMessageMenuContext.tsx +109 -0
- package/src/participant/ParticipantSelectionBar.tsx +64 -0
- package/src/participant/ParticipantThread.tsx +27 -2
- package/src/participant/ParticipantThreadScreen.tsx +1 -0
- package/src/participant/index.ts +6 -0
- package/src/participant/participantConversationMenuDom.test.tsx +166 -0
- package/src/participant/participantLabels.ts +40 -0
- package/src/participant/participantMessageInfoModel.test.ts +68 -0
- package/src/participant/participantMessageInfoModel.ts +80 -0
- package/src/participant/participantMessageMenuDom.test.tsx +111 -0
- package/src/participant/participantMessageMenuModel.test.ts +43 -0
- package/src/participant/participantMessageMenuModel.ts +91 -0
- package/src/participant/participantMessageMoment.ts +7 -0
- package/src/participant/participantTranscriptionText.test.ts +48 -0
- package/src/participant/participantTranscriptionText.ts +28 -0
- package/src/participant/useOpenUpwards.ts +22 -0
- package/src/participant/useParticipantFailedMenu.ts +3 -0
- package/src/styles.css +40 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
downloadTextFile
|
|
3
|
+
} from "../chunk-RLUNDWGA.js";
|
|
1
4
|
import {
|
|
2
5
|
TooltipLayer
|
|
3
6
|
} from "../chunk-NSOAUYNE.js";
|
|
@@ -12,7 +15,7 @@ import {
|
|
|
12
15
|
} from "../chunk-WCZNIJ3M.js";
|
|
13
16
|
|
|
14
17
|
// src/participant/ParticipantConversationsScreen.tsx
|
|
15
|
-
import { useMemo as
|
|
18
|
+
import { useMemo as useMemo7, useReducer as useReducer6, useRef as useRef23, useState as useState18 } from "react";
|
|
16
19
|
|
|
17
20
|
// src/participant/participantLoadView.ts
|
|
18
21
|
function resolveLoadView({ status, hasItems }) {
|
|
@@ -103,6 +106,23 @@ var DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS = {
|
|
|
103
106
|
edit: "Edit",
|
|
104
107
|
messageOptions: DEFAULT_MESSAGE_OPTIONS_LABEL,
|
|
105
108
|
showOriginalText: DEFAULT_SHOW_ORIGINAL_TEXT_LABEL,
|
|
109
|
+
copyMessage: "Copy",
|
|
110
|
+
copySelection: "Copy selection",
|
|
111
|
+
selectMessages: "Select messages",
|
|
112
|
+
selectMessage: "Select message",
|
|
113
|
+
cancelSelection: "Cancel selection",
|
|
114
|
+
selectedCount: "{count} selected",
|
|
115
|
+
messageInfo: "Message info",
|
|
116
|
+
infoAuthor: "From",
|
|
117
|
+
infoSentAt: "Sent",
|
|
118
|
+
infoStatus: "Status",
|
|
119
|
+
infoReadAt: "Read",
|
|
120
|
+
closeMessageInfo: "Close",
|
|
121
|
+
conversationOptions: "Conversation options",
|
|
122
|
+
downloadConversation: "Download conversation",
|
|
123
|
+
transcriptionPrefix: "Transcription",
|
|
124
|
+
copyTranscription: "Copy transcription",
|
|
125
|
+
downloadTranscription: "Download transcription",
|
|
106
126
|
notFound: "Conversation not found",
|
|
107
127
|
loading: "Loading\u2026",
|
|
108
128
|
loadError: "Could not load. Check your connection and try again.",
|
|
@@ -426,7 +446,7 @@ function ParticipantInbox(props) {
|
|
|
426
446
|
}
|
|
427
447
|
|
|
428
448
|
// src/participant/ParticipantThreadScreen.tsx
|
|
429
|
-
import { useEffect as
|
|
449
|
+
import { useEffect as useEffect22, useState as useState17 } from "react";
|
|
430
450
|
|
|
431
451
|
// src/participant/ParticipantBackButton.tsx
|
|
432
452
|
import { ArrowLeft } from "lucide-react";
|
|
@@ -436,7 +456,7 @@ function ParticipantBackButton({ label, onBack }) {
|
|
|
436
456
|
}
|
|
437
457
|
|
|
438
458
|
// src/participant/ParticipantThread.tsx
|
|
439
|
-
import { useRef as
|
|
459
|
+
import { useRef as useRef15 } from "react";
|
|
440
460
|
|
|
441
461
|
// src/participant/ParticipantComposer.tsx
|
|
442
462
|
import { useState as useState3 } from "react";
|
|
@@ -1424,10 +1444,312 @@ function ParticipantComposer(props) {
|
|
|
1424
1444
|
] });
|
|
1425
1445
|
}
|
|
1426
1446
|
|
|
1447
|
+
// src/participant/ParticipantConversationMenu.tsx
|
|
1448
|
+
import { EllipsisVertical } from "lucide-react";
|
|
1449
|
+
import { useId as useId2 } from "react";
|
|
1450
|
+
|
|
1451
|
+
// src/participant/participantMessageMoment.ts
|
|
1452
|
+
function formatMessageMoment(iso, locale) {
|
|
1453
|
+
const date = new Date(iso);
|
|
1454
|
+
const day = date.toLocaleDateString(locale, { day: "2-digit", month: "2-digit", year: "numeric" });
|
|
1455
|
+
const time = date.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" });
|
|
1456
|
+
return `${day} ${time}`;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
// src/participant/participantTranscriptionText.ts
|
|
1460
|
+
function readyTranscriptions(attachments) {
|
|
1461
|
+
return attachments.flatMap((attachment) => {
|
|
1462
|
+
const transcription = attachment.kind === "audio" ? attachment.transcription : void 0;
|
|
1463
|
+
const text = transcription?.status === "ready" ? transcription.text.trim() : "";
|
|
1464
|
+
return text === "" ? [] : [{ filename: attachment.filename, text }];
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
function attachmentForCopy(attachment, transcriptionLabel) {
|
|
1468
|
+
const [ready] = readyTranscriptions([attachment]);
|
|
1469
|
+
return ready ? `[${attachment.filename}] ${transcriptionLabel}: ${ready.text}` : `[${attachment.filename}]`;
|
|
1470
|
+
}
|
|
1471
|
+
function transcriptionFileName(audioFilename, transcriptionLabel) {
|
|
1472
|
+
const base = audioFilename.replace(/\.[^.]+$/, "");
|
|
1473
|
+
const safe = `${base} ${transcriptionLabel}`.replace(/[\\/:*?"<>|]+/g, " ").replace(/\s+/g, " ").trim();
|
|
1474
|
+
return `${safe}.txt`;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/participant/participantMessageMenuModel.ts
|
|
1478
|
+
function toggleSelectedId(ids, id) {
|
|
1479
|
+
return ids.includes(id) ? ids.filter((candidate) => candidate !== id) : [...ids, id];
|
|
1480
|
+
}
|
|
1481
|
+
function selectedInOrder(messages, ids) {
|
|
1482
|
+
const picked = new Set(ids);
|
|
1483
|
+
return messages.filter((message) => picked.has(message.id));
|
|
1484
|
+
}
|
|
1485
|
+
function copyableText(message) {
|
|
1486
|
+
const text = message.text?.trim() ?? "";
|
|
1487
|
+
return text === "" ? null : text;
|
|
1488
|
+
}
|
|
1489
|
+
function formatMessagesForCopy(messages, format) {
|
|
1490
|
+
return messages.map((message) => {
|
|
1491
|
+
const author = message.isMine ? format.meLabel : message.authorName ?? "";
|
|
1492
|
+
const head = `[${formatMessageMoment(message.createdAt, format.locale)}]${author === "" ? "" : ` ${author}:`}`;
|
|
1493
|
+
const files = message.attachments.map(
|
|
1494
|
+
(attachment) => attachmentForCopy(attachment, format.transcriptionLabel ?? "Transcription")
|
|
1495
|
+
);
|
|
1496
|
+
return [head, copyableText(message) ?? "", ...files].filter((part) => part !== "").join(" ");
|
|
1497
|
+
}).join("\n");
|
|
1498
|
+
}
|
|
1499
|
+
function selectedTextWithin(container) {
|
|
1500
|
+
const selection = typeof window === "undefined" ? null : window.getSelection();
|
|
1501
|
+
if (!container || !selection || selection.isCollapsed) return "";
|
|
1502
|
+
const { anchorNode, focusNode } = selection;
|
|
1503
|
+
if (!anchorNode || !focusNode || !container.contains(anchorNode) || !container.contains(focusNode)) return "";
|
|
1504
|
+
return selection.toString().trim();
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
// src/participant/participantMessageInfoModel.ts
|
|
1508
|
+
function statusLabel(status, labels) {
|
|
1509
|
+
const byStatus = {
|
|
1510
|
+
queued: labels.statusQueued,
|
|
1511
|
+
sent: labels.statusSent,
|
|
1512
|
+
delivered: labels.statusDelivered,
|
|
1513
|
+
read: labels.statusRead,
|
|
1514
|
+
failed: labels.statusFailedShort,
|
|
1515
|
+
bounced: labels.statusFailedShort
|
|
1516
|
+
};
|
|
1517
|
+
return byStatus[status];
|
|
1518
|
+
}
|
|
1519
|
+
function messageInfoRows(message, labels, locale) {
|
|
1520
|
+
const author = message.isMine ? labels.me : message.authorName ?? "";
|
|
1521
|
+
const rows = [];
|
|
1522
|
+
if (author !== "") rows.push({ id: "author", label: labels.infoAuthor ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.infoAuthor ?? "", value: author });
|
|
1523
|
+
rows.push({
|
|
1524
|
+
id: "sent",
|
|
1525
|
+
label: labels.infoSentAt ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.infoSentAt ?? "",
|
|
1526
|
+
value: formatMessageMoment(message.createdAt, locale)
|
|
1527
|
+
});
|
|
1528
|
+
if (!message.isMine) return rows;
|
|
1529
|
+
if (message.status) {
|
|
1530
|
+
rows.push({
|
|
1531
|
+
id: "status",
|
|
1532
|
+
label: labels.infoStatus ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.infoStatus ?? "",
|
|
1533
|
+
value: statusLabel(message.status, labels)
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
if (message.readAt) {
|
|
1537
|
+
rows.push({
|
|
1538
|
+
id: "read",
|
|
1539
|
+
label: labels.infoReadAt ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.infoReadAt ?? "",
|
|
1540
|
+
value: formatMessageMoment(message.readAt, locale)
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
return rows;
|
|
1544
|
+
}
|
|
1545
|
+
function conversationExportText({ title, messages, ...format }) {
|
|
1546
|
+
return `${title}
|
|
1547
|
+
|
|
1548
|
+
${formatMessagesForCopy(messages, format)}
|
|
1549
|
+
`;
|
|
1550
|
+
}
|
|
1551
|
+
function conversationExportFileName(title) {
|
|
1552
|
+
const safe = title.replace(/[\\/:*?"<>|]+/g, " ").replace(/\s+/g, " ").trim();
|
|
1553
|
+
return `${safe === "" ? "conversation" : safe}.txt`;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// src/participant/useParticipantFailedMenu.ts
|
|
1557
|
+
import { useReducer, useRef as useRef8 } from "react";
|
|
1558
|
+
|
|
1559
|
+
// src/participant/participantFailedMenuState.ts
|
|
1560
|
+
var CLOSED_FAILED_MENU = { isOpen: false, initialFocus: "first" };
|
|
1561
|
+
function failedMenuReducer(state, action) {
|
|
1562
|
+
if (action.type === "open") return { isOpen: true, initialFocus: action.initialFocus };
|
|
1563
|
+
if (action.type === "close") return state.isOpen ? CLOSED_FAILED_MENU : state;
|
|
1564
|
+
return state.isOpen ? CLOSED_FAILED_MENU : { isOpen: true, initialFocus: "first" };
|
|
1565
|
+
}
|
|
1566
|
+
function resolveFailedMenuKey({ key, index, count }) {
|
|
1567
|
+
if (key === "Escape") return { type: "close", shouldRestoreFocus: true };
|
|
1568
|
+
if (key === "Tab") return { type: "close", shouldRestoreFocus: false };
|
|
1569
|
+
if (key === "ArrowDown") return { type: "focus", index: (index + 1) % count };
|
|
1570
|
+
if (key === "ArrowUp") return { type: "focus", index: (index - 1 + count) % count };
|
|
1571
|
+
if (key === "Home") return { type: "focus", index: 0 };
|
|
1572
|
+
if (key === "End") return { type: "focus", index: count - 1 };
|
|
1573
|
+
return void 0;
|
|
1574
|
+
}
|
|
1575
|
+
function resolveFailedMenuTriggerKey(key) {
|
|
1576
|
+
if (key === "ArrowDown") return "first";
|
|
1577
|
+
if (key === "ArrowUp") return "last";
|
|
1578
|
+
return void 0;
|
|
1579
|
+
}
|
|
1580
|
+
var MENU_GAP_PX = 8;
|
|
1581
|
+
function resolveFailedMenuPlacement({ anchorTop, scrollerTop, menuHeight }) {
|
|
1582
|
+
return anchorTop - scrollerTop >= menuHeight + MENU_GAP_PX ? "above" : "below";
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// src/participant/failedMenuKeyboard.ts
|
|
1586
|
+
function handleFailedMenuKeyDown({ event, menuRef, triggerRef, dispatch }) {
|
|
1587
|
+
const items = Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]') ?? []);
|
|
1588
|
+
const index = items.findIndex((item) => item === document.activeElement);
|
|
1589
|
+
const result = resolveFailedMenuKey({ key: event.key, index, count: items.length });
|
|
1590
|
+
if (!result) return;
|
|
1591
|
+
if (result.type === "focus") {
|
|
1592
|
+
event.preventDefault();
|
|
1593
|
+
items[result.index]?.focus();
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
if (result.shouldRestoreFocus) {
|
|
1597
|
+
event.preventDefault();
|
|
1598
|
+
event.stopPropagation();
|
|
1599
|
+
triggerRef.current?.focus();
|
|
1600
|
+
}
|
|
1601
|
+
dispatch({ type: "close" });
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// src/participant/useFailedMenuEffects.ts
|
|
1605
|
+
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useState as useState4 } from "react";
|
|
1606
|
+
var SCROLLER_SELECTOR = ".cv-p-thread__scroll";
|
|
1607
|
+
function useFailedMenuInitialFocus(state, menuRef) {
|
|
1608
|
+
useEffect6(() => {
|
|
1609
|
+
if (!state.isOpen) return;
|
|
1610
|
+
const items = Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]') ?? []);
|
|
1611
|
+
const target = state.initialFocus === "last" ? items[items.length - 1] : items[0];
|
|
1612
|
+
target?.focus();
|
|
1613
|
+
}, [state, menuRef]);
|
|
1614
|
+
}
|
|
1615
|
+
function useFailedMenuOutsideClose(isOpen, containerRef, dispatch) {
|
|
1616
|
+
useEffect6(() => {
|
|
1617
|
+
if (!isOpen) return;
|
|
1618
|
+
function handlePointerDown(event) {
|
|
1619
|
+
if (event.target instanceof Node && containerRef.current?.contains(event.target)) return;
|
|
1620
|
+
dispatch({ type: "close" });
|
|
1621
|
+
}
|
|
1622
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
1623
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
1624
|
+
}, [isOpen, containerRef, dispatch]);
|
|
1625
|
+
}
|
|
1626
|
+
function useFailedMenuPlacement(isOpen, { triggerRef, menuRef }) {
|
|
1627
|
+
const [placement, setPlacement] = useState4("above");
|
|
1628
|
+
useLayoutEffect2(() => {
|
|
1629
|
+
const trigger = triggerRef.current;
|
|
1630
|
+
const menu = menuRef.current;
|
|
1631
|
+
if (!isOpen || !trigger || !menu) return;
|
|
1632
|
+
const anchor = trigger.closest(".cv-p-bubble") ?? trigger;
|
|
1633
|
+
const scroller = trigger.closest(SCROLLER_SELECTOR);
|
|
1634
|
+
setPlacement(
|
|
1635
|
+
resolveFailedMenuPlacement({
|
|
1636
|
+
anchorTop: anchor.getBoundingClientRect().top,
|
|
1637
|
+
scrollerTop: scroller?.getBoundingClientRect().top ?? 0,
|
|
1638
|
+
menuHeight: menu.getBoundingClientRect().height
|
|
1639
|
+
})
|
|
1640
|
+
);
|
|
1641
|
+
}, [isOpen, triggerRef, menuRef]);
|
|
1642
|
+
return placement;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// src/participant/useParticipantFailedMenu.ts
|
|
1646
|
+
function useParticipantFailedMenu() {
|
|
1647
|
+
const [state, dispatch] = useReducer(failedMenuReducer, CLOSED_FAILED_MENU);
|
|
1648
|
+
const containerRef = useRef8(null);
|
|
1649
|
+
const triggerRef = useRef8(null);
|
|
1650
|
+
const menuRef = useRef8(null);
|
|
1651
|
+
useFailedMenuInitialFocus(state, menuRef);
|
|
1652
|
+
useFailedMenuOutsideClose(state.isOpen, containerRef, dispatch);
|
|
1653
|
+
const placement = useFailedMenuPlacement(state.isOpen, { triggerRef, menuRef });
|
|
1654
|
+
function handleTriggerKeyDown(event) {
|
|
1655
|
+
const initialFocus = resolveFailedMenuTriggerKey(event.key);
|
|
1656
|
+
if (!initialFocus) return;
|
|
1657
|
+
event.preventDefault();
|
|
1658
|
+
dispatch({ type: "open", initialFocus });
|
|
1659
|
+
}
|
|
1660
|
+
function handleBlur(event) {
|
|
1661
|
+
const next = event.relatedTarget;
|
|
1662
|
+
if (next instanceof Node && !containerRef.current?.contains(next)) dispatch({ type: "close" });
|
|
1663
|
+
}
|
|
1664
|
+
return {
|
|
1665
|
+
state,
|
|
1666
|
+
placement,
|
|
1667
|
+
containerRef,
|
|
1668
|
+
triggerRef,
|
|
1669
|
+
menuRef,
|
|
1670
|
+
handleTriggerClick: () => dispatch({ type: "toggle" }),
|
|
1671
|
+
handleTriggerKeyDown,
|
|
1672
|
+
handleMenuKeyDown: (event) => handleFailedMenuKeyDown({ event, menuRef, triggerRef, dispatch }),
|
|
1673
|
+
handleBlur,
|
|
1674
|
+
close: () => dispatch({ type: "close" }),
|
|
1675
|
+
open: () => dispatch({ type: "open", initialFocus: "first" })
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// src/participant/ParticipantConversationMenu.tsx
|
|
1680
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1681
|
+
function ParticipantConversationMenu({ state, title }) {
|
|
1682
|
+
const menu = useParticipantFailedMenu();
|
|
1683
|
+
const triggerId = useId2();
|
|
1684
|
+
const { labels } = state;
|
|
1685
|
+
const listClass = `cv-p-failed-menu__list cv-p-message-menu__list cv-p-conversation-menu__list${menu.placement === "below" ? " cv-p-failed-menu__list--below" : ""}`;
|
|
1686
|
+
const entries = [
|
|
1687
|
+
{ id: "select", label: labels.selectMessages ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.selectMessages, run: () => state.start() },
|
|
1688
|
+
{
|
|
1689
|
+
id: "download",
|
|
1690
|
+
label: labels.downloadConversation ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.downloadConversation,
|
|
1691
|
+
disabled: state.messages.length === 0,
|
|
1692
|
+
run: () => downloadTextFile(
|
|
1693
|
+
conversationExportFileName(title),
|
|
1694
|
+
conversationExportText({
|
|
1695
|
+
title,
|
|
1696
|
+
messages: state.messages,
|
|
1697
|
+
locale: state.locale,
|
|
1698
|
+
meLabel: labels.me,
|
|
1699
|
+
transcriptionLabel: labels.transcriptionPrefix ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.transcriptionPrefix
|
|
1700
|
+
})
|
|
1701
|
+
)
|
|
1702
|
+
}
|
|
1703
|
+
];
|
|
1704
|
+
return /* @__PURE__ */ jsxs12("span", { className: "cv-p-conversation-menu", ref: menu.containerRef, onBlur: menu.handleBlur, children: [
|
|
1705
|
+
/* @__PURE__ */ jsx14(
|
|
1706
|
+
"button",
|
|
1707
|
+
{
|
|
1708
|
+
type: "button",
|
|
1709
|
+
id: triggerId,
|
|
1710
|
+
ref: menu.triggerRef,
|
|
1711
|
+
className: "cv-p-conversation-menu__trigger",
|
|
1712
|
+
"aria-haspopup": "menu",
|
|
1713
|
+
"aria-expanded": menu.state.isOpen,
|
|
1714
|
+
"aria-label": labels.conversationOptions ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.conversationOptions,
|
|
1715
|
+
onClick: menu.handleTriggerClick,
|
|
1716
|
+
onKeyDown: menu.handleTriggerKeyDown,
|
|
1717
|
+
children: /* @__PURE__ */ jsx14(EllipsisVertical, { size: 18, "aria-hidden": true })
|
|
1718
|
+
}
|
|
1719
|
+
),
|
|
1720
|
+
menu.state.isOpen ? /* @__PURE__ */ jsx14(
|
|
1721
|
+
"div",
|
|
1722
|
+
{
|
|
1723
|
+
role: "menu",
|
|
1724
|
+
"aria-labelledby": triggerId,
|
|
1725
|
+
ref: menu.menuRef,
|
|
1726
|
+
className: listClass,
|
|
1727
|
+
onKeyDown: menu.handleMenuKeyDown,
|
|
1728
|
+
children: entries.map((entry) => /* @__PURE__ */ jsx14(
|
|
1729
|
+
"button",
|
|
1730
|
+
{
|
|
1731
|
+
type: "button",
|
|
1732
|
+
role: "menuitem",
|
|
1733
|
+
tabIndex: -1,
|
|
1734
|
+
className: "cv-p-failed-menu__item cv-p-message-menu__item",
|
|
1735
|
+
disabled: entry.disabled,
|
|
1736
|
+
onClick: () => {
|
|
1737
|
+
menu.close();
|
|
1738
|
+
entry.run();
|
|
1739
|
+
},
|
|
1740
|
+
children: entry.label
|
|
1741
|
+
},
|
|
1742
|
+
entry.id
|
|
1743
|
+
))
|
|
1744
|
+
}
|
|
1745
|
+
) : null
|
|
1746
|
+
] });
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1427
1749
|
// src/participant/useSuppressTouchTooltips.ts
|
|
1428
|
-
import { useEffect as
|
|
1750
|
+
import { useEffect as useEffect7 } from "react";
|
|
1429
1751
|
function useSuppressTouchTooltips(rootRef) {
|
|
1430
|
-
|
|
1752
|
+
useEffect7(() => {
|
|
1431
1753
|
const root = rootRef.current;
|
|
1432
1754
|
if (!root) return void 0;
|
|
1433
1755
|
let isTouching = false;
|
|
@@ -1457,7 +1779,7 @@ function useSuppressTouchTooltips(rootRef) {
|
|
|
1457
1779
|
}
|
|
1458
1780
|
|
|
1459
1781
|
// src/participant/ParticipantCopyAnnouncer.tsx
|
|
1460
|
-
import { createContext, useCallback as useCallback4, useContext, useEffect as
|
|
1782
|
+
import { createContext, useCallback as useCallback4, useContext, useEffect as useEffect8, useReducer as useReducer2, useRef as useRef9 } from "react";
|
|
1461
1783
|
|
|
1462
1784
|
// src/participant/participantCopyAnnouncement.ts
|
|
1463
1785
|
var INITIAL_COPY_ANNOUNCEMENT = { text: "", tick: 0 };
|
|
@@ -1477,7 +1799,7 @@ function resolveLiveText(params) {
|
|
|
1477
1799
|
}
|
|
1478
1800
|
|
|
1479
1801
|
// src/participant/ParticipantCopyAnnouncer.tsx
|
|
1480
|
-
import { jsx as
|
|
1802
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1481
1803
|
var ANNOUNCEMENT_MS = 3e3;
|
|
1482
1804
|
var AnnounceContext = createContext(() => void 0);
|
|
1483
1805
|
var AnnouncementContext = createContext(INITIAL_COPY_ANNOUNCEMENT);
|
|
@@ -1485,34 +1807,34 @@ function useAnnounceCopy() {
|
|
|
1485
1807
|
return useContext(AnnounceContext);
|
|
1486
1808
|
}
|
|
1487
1809
|
function ParticipantCopyAnnouncer({ children }) {
|
|
1488
|
-
const [announcement, dispatch] =
|
|
1810
|
+
const [announcement, dispatch] = useReducer2(copyAnnouncementReducer, INITIAL_COPY_ANNOUNCEMENT);
|
|
1489
1811
|
const announce = useCallback4((text) => dispatch({ type: "announce", text }), []);
|
|
1490
|
-
|
|
1812
|
+
useEffect8(() => {
|
|
1491
1813
|
if (announcement.text === "") return void 0;
|
|
1492
1814
|
const timer = setTimeout(() => dispatch({ type: "expired" }), ANNOUNCEMENT_MS);
|
|
1493
1815
|
return () => clearTimeout(timer);
|
|
1494
1816
|
}, [announcement.tick, announcement.text]);
|
|
1495
|
-
return /* @__PURE__ */
|
|
1817
|
+
return /* @__PURE__ */ jsx15(AnnounceContext.Provider, { value: announce, children: /* @__PURE__ */ jsx15(AnnouncementContext.Provider, { value: announcement, children }) });
|
|
1496
1818
|
}
|
|
1497
1819
|
function ParticipantThreadLive({ notice }) {
|
|
1498
1820
|
const copy = useContext(AnnouncementContext);
|
|
1499
|
-
const baselineNotice =
|
|
1821
|
+
const baselineNotice = useRef9(void 0);
|
|
1500
1822
|
const resolution = resolveLiveText({ copy, notice, baselineNotice: baselineNotice.current });
|
|
1501
1823
|
baselineNotice.current = resolution.baselineNotice;
|
|
1502
|
-
return /* @__PURE__ */
|
|
1824
|
+
return /* @__PURE__ */ jsx15("div", { className: "cv-p-thread__live", role: "status", "aria-live": "polite", children: resolution.text });
|
|
1503
1825
|
}
|
|
1504
1826
|
|
|
1505
1827
|
// src/participant/ParticipantScrollToLatest.tsx
|
|
1506
1828
|
import { ChevronDown } from "lucide-react";
|
|
1507
1829
|
|
|
1508
1830
|
// src/participant/participantScrollFocus.ts
|
|
1509
|
-
var
|
|
1831
|
+
var SCROLLER_SELECTOR2 = ".cv-p-thread__scroll";
|
|
1510
1832
|
function focusScrollerOf(button) {
|
|
1511
|
-
button.closest(
|
|
1833
|
+
button.closest(SCROLLER_SELECTOR2)?.focus({ preventScroll: true });
|
|
1512
1834
|
}
|
|
1513
1835
|
|
|
1514
1836
|
// src/participant/ParticipantScrollToLatest.tsx
|
|
1515
|
-
import { jsx as
|
|
1837
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1516
1838
|
function ParticipantScrollToLatest({
|
|
1517
1839
|
label,
|
|
1518
1840
|
isAwayFromBottom,
|
|
@@ -1522,7 +1844,7 @@ function ParticipantScrollToLatest({
|
|
|
1522
1844
|
}) {
|
|
1523
1845
|
const { isVisible, badge } = resolveScrollToLatestView({ isAwayFromBottom, newMessagesCount, hasMessages });
|
|
1524
1846
|
if (!isVisible || onClick === void 0) return null;
|
|
1525
|
-
return /* @__PURE__ */
|
|
1847
|
+
return /* @__PURE__ */ jsx16("div", { className: "cv-p-thread__latest", children: /* @__PURE__ */ jsxs13(
|
|
1526
1848
|
"button",
|
|
1527
1849
|
{
|
|
1528
1850
|
type: "button",
|
|
@@ -1534,8 +1856,8 @@ function ParticipantScrollToLatest({
|
|
|
1534
1856
|
onClick();
|
|
1535
1857
|
},
|
|
1536
1858
|
children: [
|
|
1537
|
-
/* @__PURE__ */
|
|
1538
|
-
badge ? /* @__PURE__ */
|
|
1859
|
+
/* @__PURE__ */ jsx16(ChevronDown, { size: 24, "aria-hidden": "true", focusable: "false" }),
|
|
1860
|
+
badge ? /* @__PURE__ */ jsx16("span", { className: "cv-p-thread__latest-badge", "aria-hidden": "true", children: badge }) : null
|
|
1539
1861
|
]
|
|
1540
1862
|
}
|
|
1541
1863
|
) });
|
|
@@ -1581,13 +1903,13 @@ function shouldShowAvatar({ previousAuthor, author, isMine }) {
|
|
|
1581
1903
|
}
|
|
1582
1904
|
|
|
1583
1905
|
// src/participant/ParticipantAuthorAvatar.tsx
|
|
1584
|
-
import { jsx as
|
|
1906
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
1585
1907
|
function ParticipantAuthorAvatar({ name, render }) {
|
|
1586
1908
|
const fromHost = render?.({ name });
|
|
1587
1909
|
const hasHostContent = !(fromHost == null || fromHost === false || fromHost === "");
|
|
1588
1910
|
const initials = authorInitials(name);
|
|
1589
|
-
const fallback = initials ?? /* @__PURE__ */
|
|
1590
|
-
return /* @__PURE__ */
|
|
1911
|
+
const fallback = initials ?? /* @__PURE__ */ jsx17(User, { size: 16, "aria-hidden": true });
|
|
1912
|
+
return /* @__PURE__ */ jsx17("span", { className: "cv-p-avatar", "aria-hidden": "true", children: hasHostContent ? fromHost : fallback });
|
|
1591
1913
|
}
|
|
1592
1914
|
|
|
1593
1915
|
// src/participant/participantDay.ts
|
|
@@ -1599,27 +1921,27 @@ function toDateTimeAttribute(date) {
|
|
|
1599
1921
|
}
|
|
1600
1922
|
|
|
1601
1923
|
// src/participant/ParticipantDayDivider.tsx
|
|
1602
|
-
import { jsx as
|
|
1924
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1603
1925
|
function ParticipantDayDivider({ day, locale }) {
|
|
1604
1926
|
const label = day.toLocaleDateString(locale, { weekday: "short", day: "2-digit", month: "2-digit" });
|
|
1605
|
-
return /* @__PURE__ */
|
|
1927
|
+
return /* @__PURE__ */ jsx18("div", { className: "cv-p-day", role: "separator", "aria-label": label, children: /* @__PURE__ */ jsx18("time", { className: "cv-p-day__label", dateTime: toDateTimeAttribute(day), children: label }) });
|
|
1606
1928
|
}
|
|
1607
1929
|
|
|
1608
1930
|
// src/participant/ParticipantMessageBubble.tsx
|
|
1609
|
-
import { useId as
|
|
1931
|
+
import { useId as useId5 } from "react";
|
|
1610
1932
|
|
|
1611
1933
|
// src/participant/ParticipantAttachmentItem.tsx
|
|
1612
|
-
import { useEffect as
|
|
1934
|
+
import { useEffect as useEffect12, useState as useState8 } from "react";
|
|
1613
1935
|
|
|
1614
1936
|
// src/participant/ParticipantAudioTranscription.tsx
|
|
1615
|
-
import { useState as
|
|
1937
|
+
import { useState as useState6 } from "react";
|
|
1616
1938
|
|
|
1617
1939
|
// src/participant/useTranscriptionOverflow.ts
|
|
1618
|
-
import { useEffect as
|
|
1940
|
+
import { useEffect as useEffect9, useRef as useRef10, useState as useState5 } from "react";
|
|
1619
1941
|
function useTranscriptionOverflow(text, isExpanded) {
|
|
1620
|
-
const textRef =
|
|
1621
|
-
const [isOverflowing, setIsOverflowing] =
|
|
1622
|
-
|
|
1942
|
+
const textRef = useRef10(null);
|
|
1943
|
+
const [isOverflowing, setIsOverflowing] = useState5(false);
|
|
1944
|
+
useEffect9(() => {
|
|
1623
1945
|
const element = textRef.current;
|
|
1624
1946
|
if (!element || isExpanded) return void 0;
|
|
1625
1947
|
const measure = () => setIsOverflowing(element.scrollHeight > element.clientHeight);
|
|
@@ -1633,11 +1955,11 @@ function useTranscriptionOverflow(text, isExpanded) {
|
|
|
1633
1955
|
}
|
|
1634
1956
|
|
|
1635
1957
|
// src/participant/useTranscriptionReadyAnnouncement.ts
|
|
1636
|
-
import { useEffect as
|
|
1958
|
+
import { useEffect as useEffect10, useRef as useRef11 } from "react";
|
|
1637
1959
|
function useTranscriptionReadyAnnouncement({ attachmentId, status, text }) {
|
|
1638
1960
|
const announce = useAnnounceCopy();
|
|
1639
|
-
const previous =
|
|
1640
|
-
|
|
1961
|
+
const previous = useRef11({ attachmentId, status });
|
|
1962
|
+
useEffect10(() => {
|
|
1641
1963
|
const before = previous.current;
|
|
1642
1964
|
previous.current = { attachmentId, status };
|
|
1643
1965
|
if (before.attachmentId !== attachmentId) return;
|
|
@@ -1646,7 +1968,7 @@ function useTranscriptionReadyAnnouncement({ attachmentId, status, text }) {
|
|
|
1646
1968
|
}
|
|
1647
1969
|
|
|
1648
1970
|
// src/participant/ParticipantAudioTranscription.tsx
|
|
1649
|
-
import { jsx as
|
|
1971
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1650
1972
|
var MILLISECONDS_PER_MINUTE = 6e4;
|
|
1651
1973
|
function resolveTooLongMinutes(transcription) {
|
|
1652
1974
|
if (transcription?.status !== "failed" || transcription.reason !== "too_long") return void 0;
|
|
@@ -1666,21 +1988,21 @@ function ParticipantAudioTranscription({ attachmentId, transcription: received,
|
|
|
1666
1988
|
text: labels.transcriptionReady
|
|
1667
1989
|
});
|
|
1668
1990
|
if (transcription?.status === "pending") {
|
|
1669
|
-
return /* @__PURE__ */
|
|
1991
|
+
return /* @__PURE__ */ jsx19("p", { className: "cv-p-transcription cv-p-transcription--pending", children: labels.transcribing });
|
|
1670
1992
|
}
|
|
1671
|
-
if (transcription?.status === "ready") return /* @__PURE__ */
|
|
1993
|
+
if (transcription?.status === "ready") return /* @__PURE__ */ jsx19(ReadyTranscriptionBlock, { transcription, labels });
|
|
1672
1994
|
const minutes = resolveTooLongMinutes(transcription);
|
|
1673
1995
|
if (minutes === void 0) return null;
|
|
1674
|
-
return /* @__PURE__ */
|
|
1996
|
+
return /* @__PURE__ */ jsx19("p", { className: "cv-p-transcription cv-p-transcription--too-long", children: labels.transcriptionTooLong.replace("{minutes}", String(minutes)) });
|
|
1675
1997
|
}
|
|
1676
1998
|
function ReadyTranscriptionBlock({ transcription, labels }) {
|
|
1677
|
-
const [isExpanded, setIsExpanded] =
|
|
1999
|
+
const [isExpanded, setIsExpanded] = useState6(false);
|
|
1678
2000
|
const { textRef, isOverflowing } = useTranscriptionOverflow(transcription.text, isExpanded);
|
|
1679
2001
|
const hasText = transcription.text !== "";
|
|
1680
2002
|
const showsToggle = hasText && (isOverflowing || isExpanded);
|
|
1681
|
-
return /* @__PURE__ */
|
|
1682
|
-
/* @__PURE__ */
|
|
1683
|
-
hasText ? /* @__PURE__ */
|
|
2003
|
+
return /* @__PURE__ */ jsxs14("div", { className: "cv-p-transcription", children: [
|
|
2004
|
+
/* @__PURE__ */ jsx19("p", { className: "cv-p-transcription__notice", children: labels.machineTranscription }),
|
|
2005
|
+
hasText ? /* @__PURE__ */ jsx19(
|
|
1684
2006
|
"p",
|
|
1685
2007
|
{
|
|
1686
2008
|
ref: textRef,
|
|
@@ -1689,8 +2011,8 @@ function ReadyTranscriptionBlock({ transcription, labels }) {
|
|
|
1689
2011
|
"data-expanded": isExpanded ? "true" : void 0,
|
|
1690
2012
|
children: transcription.text
|
|
1691
2013
|
}
|
|
1692
|
-
) : /* @__PURE__ */
|
|
1693
|
-
showsToggle ? /* @__PURE__ */
|
|
2014
|
+
) : /* @__PURE__ */ jsx19("p", { className: "cv-p-transcription__empty", children: labels.emptyTranscription }),
|
|
2015
|
+
showsToggle ? /* @__PURE__ */ jsx19(
|
|
1694
2016
|
"button",
|
|
1695
2017
|
{
|
|
1696
2018
|
type: "button",
|
|
@@ -1704,7 +2026,7 @@ function ReadyTranscriptionBlock({ transcription, labels }) {
|
|
|
1704
2026
|
}
|
|
1705
2027
|
|
|
1706
2028
|
// src/participant/useStableAttachmentUrl.ts
|
|
1707
|
-
import { useCallback as useCallback5, useEffect as
|
|
2029
|
+
import { useCallback as useCallback5, useEffect as useEffect11, useRef as useRef12, useState as useState7 } from "react";
|
|
1708
2030
|
|
|
1709
2031
|
// src/participant/participantAttachmentUrl.ts
|
|
1710
2032
|
var SAFE_ATTACHMENT_PROTOCOLS = /* @__PURE__ */ new Set(["https:", "http:", "blob:"]);
|
|
@@ -1728,14 +2050,14 @@ async function resolveSafely({ attachment, resolveAttachmentUrl }) {
|
|
|
1728
2050
|
}
|
|
1729
2051
|
}
|
|
1730
2052
|
function useStableAttachmentUrl(params) {
|
|
1731
|
-
const latest =
|
|
1732
|
-
const currentId =
|
|
1733
|
-
const [state, setState2] =
|
|
2053
|
+
const latest = useRef12(params);
|
|
2054
|
+
const currentId = useRef12(params.attachment.id);
|
|
2055
|
+
const [state, setState2] = useState7(PENDING_STATE);
|
|
1734
2056
|
const { id } = params.attachment;
|
|
1735
|
-
|
|
2057
|
+
useEffect11(() => {
|
|
1736
2058
|
latest.current = params;
|
|
1737
2059
|
});
|
|
1738
|
-
|
|
2060
|
+
useEffect11(() => {
|
|
1739
2061
|
currentId.current = id;
|
|
1740
2062
|
setState2(PENDING_STATE);
|
|
1741
2063
|
let retryTimer;
|
|
@@ -1761,12 +2083,12 @@ function useStableAttachmentUrl(params) {
|
|
|
1761
2083
|
}
|
|
1762
2084
|
|
|
1763
2085
|
// src/participant/ParticipantAudioAttachment.tsx
|
|
1764
|
-
import { jsx as
|
|
2086
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1765
2087
|
function ParticipantAudioAttachment({ attachment, resolveAttachmentUrl, labels }) {
|
|
1766
2088
|
const source = useStableAttachmentUrl({ attachment, resolveAttachmentUrl });
|
|
1767
2089
|
const audioLabels = resolveParticipantAudioLabels(labels);
|
|
1768
|
-
return /* @__PURE__ */
|
|
1769
|
-
/* @__PURE__ */
|
|
2090
|
+
return /* @__PURE__ */ jsxs15("div", { className: "cv-p-attachment cv-p-attachment--audio", children: [
|
|
2091
|
+
/* @__PURE__ */ jsx20(
|
|
1770
2092
|
ParticipantAudioPlayer,
|
|
1771
2093
|
{
|
|
1772
2094
|
labels: audioLabels,
|
|
@@ -1778,17 +2100,17 @@ function ParticipantAudioAttachment({ attachment, resolveAttachmentUrl, labels }
|
|
|
1778
2100
|
onSourceError: source.resolveAgain
|
|
1779
2101
|
}
|
|
1780
2102
|
),
|
|
1781
|
-
/* @__PURE__ */
|
|
1782
|
-
/* @__PURE__ */
|
|
2103
|
+
/* @__PURE__ */ jsx20("span", { className: "cv-p-attachment__name", children: attachment.filename }),
|
|
2104
|
+
/* @__PURE__ */ jsx20(ParticipantAudioTranscription, { attachmentId: attachment.id, transcription: attachment.transcription, labels: audioLabels })
|
|
1783
2105
|
] });
|
|
1784
2106
|
}
|
|
1785
2107
|
|
|
1786
2108
|
// src/participant/ParticipantAttachmentItem.tsx
|
|
1787
|
-
import { jsx as
|
|
2109
|
+
import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1788
2110
|
function useAttachmentUrl(attachment, resolveAttachmentUrl) {
|
|
1789
|
-
const [url, setUrl] =
|
|
2111
|
+
const [url, setUrl] = useState8(void 0);
|
|
1790
2112
|
const disposition = attachment.kind === "document" ? "attachment" : "inline";
|
|
1791
|
-
|
|
2113
|
+
useEffect12(() => {
|
|
1792
2114
|
let isCurrent = true;
|
|
1793
2115
|
resolveAttachmentUrl(attachment, disposition).then(
|
|
1794
2116
|
(resolved) => {
|
|
@@ -1805,18 +2127,18 @@ function useAttachmentUrl(attachment, resolveAttachmentUrl) {
|
|
|
1805
2127
|
return url;
|
|
1806
2128
|
}
|
|
1807
2129
|
function ParticipantAttachmentItem(props) {
|
|
1808
|
-
if (props.attachment.kind === "audio") return /* @__PURE__ */
|
|
1809
|
-
return /* @__PURE__ */
|
|
2130
|
+
if (props.attachment.kind === "audio") return /* @__PURE__ */ jsx21(ParticipantAudioAttachment, { ...props });
|
|
2131
|
+
return /* @__PURE__ */ jsx21(ParticipantFileAttachment, { ...props });
|
|
1810
2132
|
}
|
|
1811
2133
|
function ParticipantFileAttachment({ attachment, resolveAttachmentUrl, labels }) {
|
|
1812
2134
|
const url = useAttachmentUrl(attachment, resolveAttachmentUrl);
|
|
1813
2135
|
if (attachment.kind === "image") {
|
|
1814
|
-
return /* @__PURE__ */
|
|
1815
|
-
url ? /* @__PURE__ */
|
|
1816
|
-
/* @__PURE__ */
|
|
2136
|
+
return /* @__PURE__ */ jsxs16("figure", { className: "cv-p-attachment cv-p-attachment--image", children: [
|
|
2137
|
+
url ? /* @__PURE__ */ jsx21("img", { className: "cv-p-attachment__image", src: url, alt: attachment.filename }) : null,
|
|
2138
|
+
/* @__PURE__ */ jsx21("figcaption", { className: "cv-p-attachment__name", children: attachment.filename })
|
|
1817
2139
|
] });
|
|
1818
2140
|
}
|
|
1819
|
-
return /* @__PURE__ */
|
|
2141
|
+
return /* @__PURE__ */ jsxs16(
|
|
1820
2142
|
"a",
|
|
1821
2143
|
{
|
|
1822
2144
|
className: "cv-p-attachment cv-p-attachment--document",
|
|
@@ -1824,15 +2146,15 @@ function ParticipantFileAttachment({ attachment, resolveAttachmentUrl, labels })
|
|
|
1824
2146
|
download: attachment.filename,
|
|
1825
2147
|
"aria-label": `${labels.downloadAttachment}: ${attachment.filename}`,
|
|
1826
2148
|
children: [
|
|
1827
|
-
/* @__PURE__ */
|
|
1828
|
-
/* @__PURE__ */
|
|
2149
|
+
/* @__PURE__ */ jsx21("span", { className: "cv-p-attachment__name", children: attachment.filename }),
|
|
2150
|
+
/* @__PURE__ */ jsx21("span", { className: "cv-p-attachment__size", children: formatFileSize(attachment.sizeBytes) })
|
|
1829
2151
|
]
|
|
1830
2152
|
}
|
|
1831
2153
|
);
|
|
1832
2154
|
}
|
|
1833
2155
|
|
|
1834
2156
|
// src/participant/ParticipantBubbleStatus.tsx
|
|
1835
|
-
import { jsx as
|
|
2157
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1836
2158
|
function statusText(status, labels) {
|
|
1837
2159
|
const byStatus = {
|
|
1838
2160
|
sending: labels.statusSending,
|
|
@@ -1849,13 +2171,13 @@ function OwnStatus({ status, labels, onRetry }) {
|
|
|
1849
2171
|
const isVisibleText = status === "sending" || status === "queued" || status === "failed";
|
|
1850
2172
|
const text = status === "failed" ? labels.statusFailedShort : statusText(status, labels);
|
|
1851
2173
|
const isTextVisible = isVisibleText && !(status === "failed" && onRetry);
|
|
1852
|
-
return /* @__PURE__ */
|
|
1853
|
-
/* @__PURE__ */
|
|
1854
|
-
/* @__PURE__ */
|
|
2174
|
+
return /* @__PURE__ */ jsxs17("span", { className: "cv-p-bubble__status", children: [
|
|
2175
|
+
/* @__PURE__ */ jsx22(StatusTicks, { status: ticksStatus, appearance: "stylesheet", title: isTextVisible ? void 0 : text }),
|
|
2176
|
+
/* @__PURE__ */ jsx22("span", { className: isTextVisible ? "cv-p-bubble__status-text" : "cv-p-sr-only", children: text })
|
|
1855
2177
|
] });
|
|
1856
2178
|
}
|
|
1857
2179
|
function RetryButton({ labels, onRetry, describedBy }) {
|
|
1858
|
-
return /* @__PURE__ */
|
|
2180
|
+
return /* @__PURE__ */ jsx22(
|
|
1859
2181
|
"button",
|
|
1860
2182
|
{
|
|
1861
2183
|
type: "button",
|
|
@@ -1864,16 +2186,16 @@ function RetryButton({ labels, onRetry, describedBy }) {
|
|
|
1864
2186
|
"aria-describedby": describedBy,
|
|
1865
2187
|
"data-cv-tooltip": labels.statusFailed,
|
|
1866
2188
|
onClick: onRetry,
|
|
1867
|
-
children: /* @__PURE__ */
|
|
2189
|
+
children: /* @__PURE__ */ jsx22("span", { className: "cv-p-failed-retry__icon", children: /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 16 16", width: "14", height: "14", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx22("path", { d: "M13 8a5 5 0 1 1-1.6-3.7M13 2.5v3h-3", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) }) })
|
|
1868
2190
|
}
|
|
1869
2191
|
);
|
|
1870
2192
|
}
|
|
1871
2193
|
|
|
1872
2194
|
// src/participant/ParticipantFailedMenu.tsx
|
|
1873
|
-
import { useId as
|
|
2195
|
+
import { useId as useId3 } from "react";
|
|
1874
2196
|
|
|
1875
2197
|
// src/participant/ParticipantFailedMenuList.tsx
|
|
1876
|
-
import { jsx as
|
|
2198
|
+
import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1877
2199
|
function ParticipantFailedMenuList({ menu, labelledBy, labels, onEdit, onDiscard }) {
|
|
1878
2200
|
function runAndClose(action) {
|
|
1879
2201
|
return () => {
|
|
@@ -1882,9 +2204,9 @@ function ParticipantFailedMenuList({ menu, labelledBy, labels, onEdit, onDiscard
|
|
|
1882
2204
|
};
|
|
1883
2205
|
}
|
|
1884
2206
|
const className = `cv-p-failed-menu__list${menu.placement === "below" ? " cv-p-failed-menu__list--below" : ""}`;
|
|
1885
|
-
return /* @__PURE__ */
|
|
1886
|
-
onEdit ? /* @__PURE__ */
|
|
1887
|
-
onDiscard ? /* @__PURE__ */
|
|
2207
|
+
return /* @__PURE__ */ jsxs18("div", { role: "menu", "aria-labelledby": labelledBy, ref: menu.menuRef, className, onKeyDown: menu.handleMenuKeyDown, children: [
|
|
2208
|
+
onEdit ? /* @__PURE__ */ jsx23("button", { type: "button", role: "menuitem", tabIndex: -1, className: "cv-p-failed-menu__item", onClick: runAndClose(onEdit), children: labels.edit }) : null,
|
|
2209
|
+
onDiscard ? /* @__PURE__ */ jsx23(
|
|
1888
2210
|
"button",
|
|
1889
2211
|
{
|
|
1890
2212
|
type: "button",
|
|
@@ -1898,137 +2220,15 @@ function ParticipantFailedMenuList({ menu, labelledBy, labels, onEdit, onDiscard
|
|
|
1898
2220
|
] });
|
|
1899
2221
|
}
|
|
1900
2222
|
|
|
1901
|
-
// src/participant/useParticipantFailedMenu.ts
|
|
1902
|
-
import { useReducer as useReducer2, useRef as useRef12 } from "react";
|
|
1903
|
-
|
|
1904
|
-
// src/participant/participantFailedMenuState.ts
|
|
1905
|
-
var CLOSED_FAILED_MENU = { isOpen: false, initialFocus: "first" };
|
|
1906
|
-
function failedMenuReducer(state, action) {
|
|
1907
|
-
if (action.type === "open") return { isOpen: true, initialFocus: action.initialFocus };
|
|
1908
|
-
if (action.type === "close") return state.isOpen ? CLOSED_FAILED_MENU : state;
|
|
1909
|
-
return state.isOpen ? CLOSED_FAILED_MENU : { isOpen: true, initialFocus: "first" };
|
|
1910
|
-
}
|
|
1911
|
-
function resolveFailedMenuKey({ key, index, count }) {
|
|
1912
|
-
if (key === "Escape") return { type: "close", shouldRestoreFocus: true };
|
|
1913
|
-
if (key === "Tab") return { type: "close", shouldRestoreFocus: false };
|
|
1914
|
-
if (key === "ArrowDown") return { type: "focus", index: (index + 1) % count };
|
|
1915
|
-
if (key === "ArrowUp") return { type: "focus", index: (index - 1 + count) % count };
|
|
1916
|
-
if (key === "Home") return { type: "focus", index: 0 };
|
|
1917
|
-
if (key === "End") return { type: "focus", index: count - 1 };
|
|
1918
|
-
return void 0;
|
|
1919
|
-
}
|
|
1920
|
-
function resolveFailedMenuTriggerKey(key) {
|
|
1921
|
-
if (key === "ArrowDown") return "first";
|
|
1922
|
-
if (key === "ArrowUp") return "last";
|
|
1923
|
-
return void 0;
|
|
1924
|
-
}
|
|
1925
|
-
var MENU_GAP_PX = 8;
|
|
1926
|
-
function resolveFailedMenuPlacement({ anchorTop, scrollerTop, menuHeight }) {
|
|
1927
|
-
return anchorTop - scrollerTop >= menuHeight + MENU_GAP_PX ? "above" : "below";
|
|
1928
|
-
}
|
|
1929
|
-
|
|
1930
|
-
// src/participant/failedMenuKeyboard.ts
|
|
1931
|
-
function handleFailedMenuKeyDown({ event, menuRef, triggerRef, dispatch }) {
|
|
1932
|
-
const items = Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]') ?? []);
|
|
1933
|
-
const index = items.findIndex((item) => item === document.activeElement);
|
|
1934
|
-
const result = resolveFailedMenuKey({ key: event.key, index, count: items.length });
|
|
1935
|
-
if (!result) return;
|
|
1936
|
-
if (result.type === "focus") {
|
|
1937
|
-
event.preventDefault();
|
|
1938
|
-
items[result.index]?.focus();
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
if (result.shouldRestoreFocus) {
|
|
1942
|
-
event.preventDefault();
|
|
1943
|
-
event.stopPropagation();
|
|
1944
|
-
triggerRef.current?.focus();
|
|
1945
|
-
}
|
|
1946
|
-
dispatch({ type: "close" });
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
// src/participant/useFailedMenuEffects.ts
|
|
1950
|
-
import { useEffect as useEffect12, useLayoutEffect as useLayoutEffect2, useState as useState8 } from "react";
|
|
1951
|
-
var SCROLLER_SELECTOR2 = ".cv-p-thread__scroll";
|
|
1952
|
-
function useFailedMenuInitialFocus(state, menuRef) {
|
|
1953
|
-
useEffect12(() => {
|
|
1954
|
-
if (!state.isOpen) return;
|
|
1955
|
-
const items = Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]') ?? []);
|
|
1956
|
-
const target = state.initialFocus === "last" ? items[items.length - 1] : items[0];
|
|
1957
|
-
target?.focus();
|
|
1958
|
-
}, [state, menuRef]);
|
|
1959
|
-
}
|
|
1960
|
-
function useFailedMenuOutsideClose(isOpen, containerRef, dispatch) {
|
|
1961
|
-
useEffect12(() => {
|
|
1962
|
-
if (!isOpen) return;
|
|
1963
|
-
function handlePointerDown(event) {
|
|
1964
|
-
if (event.target instanceof Node && containerRef.current?.contains(event.target)) return;
|
|
1965
|
-
dispatch({ type: "close" });
|
|
1966
|
-
}
|
|
1967
|
-
document.addEventListener("pointerdown", handlePointerDown);
|
|
1968
|
-
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
1969
|
-
}, [isOpen, containerRef, dispatch]);
|
|
1970
|
-
}
|
|
1971
|
-
function useFailedMenuPlacement(isOpen, { triggerRef, menuRef }) {
|
|
1972
|
-
const [placement, setPlacement] = useState8("above");
|
|
1973
|
-
useLayoutEffect2(() => {
|
|
1974
|
-
const trigger = triggerRef.current;
|
|
1975
|
-
const menu = menuRef.current;
|
|
1976
|
-
if (!isOpen || !trigger || !menu) return;
|
|
1977
|
-
const anchor = trigger.closest(".cv-p-bubble") ?? trigger;
|
|
1978
|
-
const scroller = trigger.closest(SCROLLER_SELECTOR2);
|
|
1979
|
-
setPlacement(
|
|
1980
|
-
resolveFailedMenuPlacement({
|
|
1981
|
-
anchorTop: anchor.getBoundingClientRect().top,
|
|
1982
|
-
scrollerTop: scroller?.getBoundingClientRect().top ?? 0,
|
|
1983
|
-
menuHeight: menu.getBoundingClientRect().height
|
|
1984
|
-
})
|
|
1985
|
-
);
|
|
1986
|
-
}, [isOpen, triggerRef, menuRef]);
|
|
1987
|
-
return placement;
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
|
-
// src/participant/useParticipantFailedMenu.ts
|
|
1991
|
-
function useParticipantFailedMenu() {
|
|
1992
|
-
const [state, dispatch] = useReducer2(failedMenuReducer, CLOSED_FAILED_MENU);
|
|
1993
|
-
const containerRef = useRef12(null);
|
|
1994
|
-
const triggerRef = useRef12(null);
|
|
1995
|
-
const menuRef = useRef12(null);
|
|
1996
|
-
useFailedMenuInitialFocus(state, menuRef);
|
|
1997
|
-
useFailedMenuOutsideClose(state.isOpen, containerRef, dispatch);
|
|
1998
|
-
const placement = useFailedMenuPlacement(state.isOpen, { triggerRef, menuRef });
|
|
1999
|
-
function handleTriggerKeyDown(event) {
|
|
2000
|
-
const initialFocus = resolveFailedMenuTriggerKey(event.key);
|
|
2001
|
-
if (!initialFocus) return;
|
|
2002
|
-
event.preventDefault();
|
|
2003
|
-
dispatch({ type: "open", initialFocus });
|
|
2004
|
-
}
|
|
2005
|
-
function handleBlur(event) {
|
|
2006
|
-
const next = event.relatedTarget;
|
|
2007
|
-
if (next instanceof Node && !containerRef.current?.contains(next)) dispatch({ type: "close" });
|
|
2008
|
-
}
|
|
2009
|
-
return {
|
|
2010
|
-
state,
|
|
2011
|
-
placement,
|
|
2012
|
-
containerRef,
|
|
2013
|
-
triggerRef,
|
|
2014
|
-
menuRef,
|
|
2015
|
-
handleTriggerClick: () => dispatch({ type: "toggle" }),
|
|
2016
|
-
handleTriggerKeyDown,
|
|
2017
|
-
handleMenuKeyDown: (event) => handleFailedMenuKeyDown({ event, menuRef, triggerRef, dispatch }),
|
|
2018
|
-
handleBlur,
|
|
2019
|
-
close: () => dispatch({ type: "close" })
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
2223
|
// src/participant/ParticipantFailedMenu.tsx
|
|
2024
|
-
import { jsx as
|
|
2224
|
+
import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2025
2225
|
function ParticipantFailedMenu({ labels, onEdit, onDiscard }) {
|
|
2026
2226
|
const menu = useParticipantFailedMenu();
|
|
2027
|
-
const triggerId =
|
|
2227
|
+
const triggerId = useId3();
|
|
2028
2228
|
if (!onEdit && !onDiscard) return null;
|
|
2029
2229
|
const label = labels.messageOptions ?? DEFAULT_MESSAGE_OPTIONS_LABEL;
|
|
2030
|
-
return /* @__PURE__ */
|
|
2031
|
-
/* @__PURE__ */
|
|
2230
|
+
return /* @__PURE__ */ jsxs19("span", { className: "cv-p-failed-menu", ref: menu.containerRef, onBlur: menu.handleBlur, children: [
|
|
2231
|
+
/* @__PURE__ */ jsx24(
|
|
2032
2232
|
"button",
|
|
2033
2233
|
{
|
|
2034
2234
|
type: "button",
|
|
@@ -2041,10 +2241,10 @@ function ParticipantFailedMenu({ labels, onEdit, onDiscard }) {
|
|
|
2041
2241
|
"data-cv-tooltip": menu.state.isOpen ? void 0 : label,
|
|
2042
2242
|
onClick: menu.handleTriggerClick,
|
|
2043
2243
|
onKeyDown: menu.handleTriggerKeyDown,
|
|
2044
|
-
children: /* @__PURE__ */
|
|
2244
|
+
children: /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", children: "\u22EF" })
|
|
2045
2245
|
}
|
|
2046
2246
|
),
|
|
2047
|
-
menu.state.isOpen ? /* @__PURE__ */
|
|
2247
|
+
menu.state.isOpen ? /* @__PURE__ */ jsx24(ParticipantFailedMenuList, { menu, labelledBy: triggerId, labels, onEdit, onDiscard }) : null
|
|
2048
2248
|
] });
|
|
2049
2249
|
}
|
|
2050
2250
|
|
|
@@ -2094,10 +2294,10 @@ function useParticipantCopyFeedback() {
|
|
|
2094
2294
|
}
|
|
2095
2295
|
|
|
2096
2296
|
// src/participant/ParticipantCopyableToken.tsx
|
|
2097
|
-
import { jsx as
|
|
2297
|
+
import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2098
2298
|
function ParticipantCopyableToken({ copyKind, value, labels }) {
|
|
2099
2299
|
const { isCopied, copy } = useParticipantCopyFeedback();
|
|
2100
|
-
return /* @__PURE__ */
|
|
2300
|
+
return /* @__PURE__ */ jsxs20(
|
|
2101
2301
|
"button",
|
|
2102
2302
|
{
|
|
2103
2303
|
type: "button",
|
|
@@ -2106,8 +2306,8 @@ function ParticipantCopyableToken({ copyKind, value, labels }) {
|
|
|
2106
2306
|
onClick: () => copy(value, labels.valueCopied),
|
|
2107
2307
|
children: [
|
|
2108
2308
|
value,
|
|
2109
|
-
isCopied ? /* @__PURE__ */
|
|
2110
|
-
/* @__PURE__ */
|
|
2309
|
+
isCopied ? /* @__PURE__ */ jsxs20("span", { className: "cv-p-copyable__done", "aria-hidden": "true", children: [
|
|
2310
|
+
/* @__PURE__ */ jsx25(Check, { size: 12, "aria-hidden": true }),
|
|
2111
2311
|
" ",
|
|
2112
2312
|
labels.valueCopied
|
|
2113
2313
|
] }) : null
|
|
@@ -2117,7 +2317,7 @@ function ParticipantCopyableToken({ copyKind, value, labels }) {
|
|
|
2117
2317
|
}
|
|
2118
2318
|
function ParticipantLinkCopyButton({ value, labels }) {
|
|
2119
2319
|
const { isCopied, copy } = useParticipantCopyFeedback();
|
|
2120
|
-
return /* @__PURE__ */
|
|
2320
|
+
return /* @__PURE__ */ jsx25(
|
|
2121
2321
|
"button",
|
|
2122
2322
|
{
|
|
2123
2323
|
type: "button",
|
|
@@ -2125,26 +2325,26 @@ function ParticipantLinkCopyButton({ value, labels }) {
|
|
|
2125
2325
|
"aria-label": `${labels.copyValue} ${value}`,
|
|
2126
2326
|
"data-cv-tooltip": labels.copyValue,
|
|
2127
2327
|
onClick: () => copy(value, labels.valueCopied),
|
|
2128
|
-
children: isCopied ? /* @__PURE__ */
|
|
2328
|
+
children: isCopied ? /* @__PURE__ */ jsx25(Check, { size: 14, "aria-hidden": true }) : /* @__PURE__ */ jsx25(Copy, { size: 14, "aria-hidden": true })
|
|
2129
2329
|
}
|
|
2130
2330
|
);
|
|
2131
2331
|
}
|
|
2132
2332
|
|
|
2133
2333
|
// src/participant/ParticipantMessageNodes.tsx
|
|
2134
|
-
import { jsx as
|
|
2334
|
+
import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2135
2335
|
var LINK_REL = "noopener noreferrer nofollow ugc";
|
|
2136
2336
|
function renderLink(node, labels, key) {
|
|
2137
2337
|
const isExternal = /^https?:/i.test(node.href);
|
|
2138
|
-
return /* @__PURE__ */
|
|
2139
|
-
/* @__PURE__ */
|
|
2338
|
+
return /* @__PURE__ */ jsxs21("span", { className: "cv-p-text__link-group", children: [
|
|
2339
|
+
/* @__PURE__ */ jsxs21("a", { className: "cv-p-text__link", href: node.href, rel: LINK_REL, target: isExternal ? "_blank" : void 0, children: [
|
|
2140
2340
|
node.label,
|
|
2141
|
-
isExternal ? /* @__PURE__ */
|
|
2341
|
+
isExternal ? /* @__PURE__ */ jsxs21("span", { className: "cv-p-sr-only", children: [
|
|
2142
2342
|
" (",
|
|
2143
2343
|
labels.externalLinkHint,
|
|
2144
2344
|
")"
|
|
2145
2345
|
] }) : null
|
|
2146
2346
|
] }),
|
|
2147
|
-
/* @__PURE__ */
|
|
2347
|
+
/* @__PURE__ */ jsx26(ParticipantLinkCopyButton, { value: node.href, labels })
|
|
2148
2348
|
] }, key);
|
|
2149
2349
|
}
|
|
2150
2350
|
function renderInlineNodes(nodes, labels) {
|
|
@@ -2153,17 +2353,17 @@ function renderInlineNodes(nodes, labels) {
|
|
|
2153
2353
|
case "text":
|
|
2154
2354
|
return node.value;
|
|
2155
2355
|
case "strong":
|
|
2156
|
-
return /* @__PURE__ */
|
|
2356
|
+
return /* @__PURE__ */ jsx26("strong", { children: renderInlineNodes(node.children, labels) }, key);
|
|
2157
2357
|
case "em":
|
|
2158
|
-
return /* @__PURE__ */
|
|
2358
|
+
return /* @__PURE__ */ jsx26("em", { children: renderInlineNodes(node.children, labels) }, key);
|
|
2159
2359
|
case "del":
|
|
2160
|
-
return /* @__PURE__ */
|
|
2360
|
+
return /* @__PURE__ */ jsx26("del", { children: renderInlineNodes(node.children, labels) }, key);
|
|
2161
2361
|
case "code":
|
|
2162
|
-
return /* @__PURE__ */
|
|
2362
|
+
return /* @__PURE__ */ jsx26("code", { className: "cv-p-text__code", children: node.value }, key);
|
|
2163
2363
|
case "link":
|
|
2164
2364
|
return renderLink(node, labels, key);
|
|
2165
2365
|
case "copyable":
|
|
2166
|
-
return /* @__PURE__ */
|
|
2366
|
+
return /* @__PURE__ */ jsx26(ParticipantCopyableToken, { copyKind: node.copyKind, value: node.value, labels }, key);
|
|
2167
2367
|
}
|
|
2168
2368
|
});
|
|
2169
2369
|
}
|
|
@@ -2549,35 +2749,35 @@ function startsFenceLine(line) {
|
|
|
2549
2749
|
}
|
|
2550
2750
|
|
|
2551
2751
|
// src/participant/ParticipantMessageText.tsx
|
|
2552
|
-
import { jsx as
|
|
2752
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
2553
2753
|
function renderBlock(block, labels, key) {
|
|
2554
|
-
if (block.kind === "paragraph") return /* @__PURE__ */
|
|
2754
|
+
if (block.kind === "paragraph") return /* @__PURE__ */ jsx27("span", { children: renderInlineNodes(block.children, labels) }, key);
|
|
2555
2755
|
if (block.kind === "pre") {
|
|
2556
|
-
return /* @__PURE__ */
|
|
2756
|
+
return /* @__PURE__ */ jsx27("pre", { className: "cv-p-text__pre", children: /* @__PURE__ */ jsx27("code", { children: block.value }) }, key);
|
|
2557
2757
|
}
|
|
2558
2758
|
if (block.kind === "quote") {
|
|
2559
|
-
return /* @__PURE__ */
|
|
2759
|
+
return /* @__PURE__ */ jsx27("blockquote", { className: "cv-p-text__quote", children: renderInlineNodes(block.children, labels) }, key);
|
|
2560
2760
|
}
|
|
2561
|
-
const items = block.items.map((item, index) => /* @__PURE__ */
|
|
2761
|
+
const items = block.items.map((item, index) => /* @__PURE__ */ jsx27("li", { children: renderInlineNodes(item, labels) }, index));
|
|
2562
2762
|
if (block.ordered) {
|
|
2563
|
-
return /* @__PURE__ */
|
|
2763
|
+
return /* @__PURE__ */ jsx27("ol", { className: "cv-p-text__list", start: block.start === 1 ? void 0 : block.start, children: items }, key);
|
|
2564
2764
|
}
|
|
2565
|
-
return /* @__PURE__ */
|
|
2765
|
+
return /* @__PURE__ */ jsx27("ul", { className: "cv-p-text__list", children: items }, key);
|
|
2566
2766
|
}
|
|
2567
2767
|
function ParticipantMessageText({ text, labels, id }) {
|
|
2568
2768
|
const blocks = useMemo2(() => parseParticipantMessage(text), [text]);
|
|
2569
2769
|
if (blocks.length === 0) return null;
|
|
2570
2770
|
const [only] = blocks;
|
|
2571
2771
|
const children = blocks.length === 1 && only?.kind === "paragraph" ? renderInlineNodes(only.children, labels) : blocks.map((block, key) => renderBlock(block, labels, key));
|
|
2572
|
-
return /* @__PURE__ */
|
|
2772
|
+
return /* @__PURE__ */ jsx27("div", { className: "cv-p-text", id, children });
|
|
2573
2773
|
}
|
|
2574
2774
|
|
|
2575
2775
|
// src/participant/ParticipantBubbleBody.tsx
|
|
2576
|
-
import { Fragment as Fragment6, jsx as
|
|
2776
|
+
import { Fragment as Fragment6, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2577
2777
|
function BubbleHeading({ content, labels }) {
|
|
2578
|
-
if (content.isMine) return /* @__PURE__ */
|
|
2778
|
+
if (content.isMine) return /* @__PURE__ */ jsx28("span", { className: "cv-p-sr-only", children: labels.me });
|
|
2579
2779
|
if (!content.authorName) return null;
|
|
2580
|
-
return /* @__PURE__ */
|
|
2780
|
+
return /* @__PURE__ */ jsx28("span", { className: "cv-p-bubble__author", children: content.authorName });
|
|
2581
2781
|
}
|
|
2582
2782
|
function hostActions(content, render) {
|
|
2583
2783
|
if (!render || content.messageId === void 0) return null;
|
|
@@ -2586,23 +2786,34 @@ function hostActions(content, render) {
|
|
|
2586
2786
|
text: content.text ?? null,
|
|
2587
2787
|
authorName: content.authorName ?? null,
|
|
2588
2788
|
isMine: content.isMine,
|
|
2589
|
-
attachments: content.attachments
|
|
2789
|
+
attachments: content.attachments,
|
|
2790
|
+
createdAt: content.createdAt
|
|
2590
2791
|
});
|
|
2591
2792
|
}
|
|
2592
2793
|
function OriginalText({ content, labels }) {
|
|
2593
2794
|
const original = content.originalText?.trim();
|
|
2594
2795
|
if (!original || original === content.text?.trim()) return null;
|
|
2595
|
-
return /* @__PURE__ */
|
|
2596
|
-
/* @__PURE__ */
|
|
2597
|
-
/* @__PURE__ */
|
|
2796
|
+
return /* @__PURE__ */ jsxs22("details", { className: "cv-p-bubble__original", children: [
|
|
2797
|
+
/* @__PURE__ */ jsx28("summary", { className: "cv-p-bubble__original-toggle", children: labels.showOriginalText ?? DEFAULT_SHOW_ORIGINAL_TEXT_LABEL }),
|
|
2798
|
+
/* @__PURE__ */ jsx28("div", { className: "cv-p-bubble__original-text", children: original })
|
|
2598
2799
|
] });
|
|
2599
2800
|
}
|
|
2600
|
-
function BubbleBody({
|
|
2801
|
+
function BubbleBody({
|
|
2802
|
+
content,
|
|
2803
|
+
labels,
|
|
2804
|
+
ownStatus,
|
|
2805
|
+
textId,
|
|
2806
|
+
resolveAttachmentUrl,
|
|
2807
|
+
onRetry,
|
|
2808
|
+
onDiscard,
|
|
2809
|
+
onEdit,
|
|
2810
|
+
renderMessageActions
|
|
2811
|
+
}) {
|
|
2601
2812
|
const actions = hostActions(content, renderMessageActions);
|
|
2602
|
-
return /* @__PURE__ */
|
|
2603
|
-
/* @__PURE__ */
|
|
2604
|
-
content.text ? /* @__PURE__ */
|
|
2605
|
-
content.attachments.map((attachment) => /* @__PURE__ */
|
|
2813
|
+
return /* @__PURE__ */ jsxs22(Fragment6, { children: [
|
|
2814
|
+
/* @__PURE__ */ jsx28(BubbleHeading, { content, labels }),
|
|
2815
|
+
content.text ? /* @__PURE__ */ jsx28(ParticipantMessageText, { text: content.text, labels, id: textId }) : null,
|
|
2816
|
+
content.attachments.map((attachment) => /* @__PURE__ */ jsx28(
|
|
2606
2817
|
ParticipantAttachmentItem,
|
|
2607
2818
|
{
|
|
2608
2819
|
attachment,
|
|
@@ -2611,17 +2822,319 @@ function BubbleBody({ content, labels, ownStatus, textId, resolveAttachmentUrl,
|
|
|
2611
2822
|
},
|
|
2612
2823
|
attachment.id
|
|
2613
2824
|
)),
|
|
2614
|
-
content.pendingFilenames.map((filename, index) => /* @__PURE__ */
|
|
2615
|
-
/* @__PURE__ */
|
|
2616
|
-
/* @__PURE__ */
|
|
2617
|
-
ownStatus ? /* @__PURE__ */
|
|
2618
|
-
ownStatus === "failed" ? /* @__PURE__ */
|
|
2619
|
-
actions ? /* @__PURE__ */
|
|
2825
|
+
content.pendingFilenames.map((filename, index) => /* @__PURE__ */ jsx28("span", { className: "cv-p-attachment cv-p-attachment--pending", children: filename }, `${filename}-${index}`)),
|
|
2826
|
+
/* @__PURE__ */ jsxs22("span", { className: "cv-p-bubble__meta", children: [
|
|
2827
|
+
/* @__PURE__ */ jsx28("time", { className: "cv-p-bubble__time", dateTime: content.createdAt, children: formatTimestamp(content.createdAt) }),
|
|
2828
|
+
ownStatus ? /* @__PURE__ */ jsx28(OwnStatus, { status: ownStatus, labels, onRetry }) : null,
|
|
2829
|
+
ownStatus === "failed" ? /* @__PURE__ */ jsx28(ParticipantFailedMenu, { labels, onEdit, onDiscard }) : null,
|
|
2830
|
+
actions ? /* @__PURE__ */ jsx28("span", { className: "cv-p-bubble__host-actions", children: actions }) : null
|
|
2620
2831
|
] }),
|
|
2621
|
-
/* @__PURE__ */
|
|
2832
|
+
/* @__PURE__ */ jsx28(OriginalText, { content, labels })
|
|
2622
2833
|
] });
|
|
2623
2834
|
}
|
|
2624
2835
|
|
|
2836
|
+
// src/participant/ParticipantMenuBubble.tsx
|
|
2837
|
+
import { useRef as useRef14, useState as useState11 } from "react";
|
|
2838
|
+
|
|
2839
|
+
// src/participant/ParticipantMessageMenu.tsx
|
|
2840
|
+
import { ChevronDown as ChevronDown2 } from "lucide-react";
|
|
2841
|
+
import { useCallback as useCallback6, useId as useId4, useState as useState10 } from "react";
|
|
2842
|
+
|
|
2843
|
+
// src/participant/ParticipantMessageInfo.tsx
|
|
2844
|
+
import { X } from "lucide-react";
|
|
2845
|
+
import { useEffect as useEffect14, useRef as useRef13 } from "react";
|
|
2846
|
+
|
|
2847
|
+
// src/participant/useOpenUpwards.ts
|
|
2848
|
+
import { useLayoutEffect as useLayoutEffect3, useState as useState9 } from "react";
|
|
2849
|
+
var SCROLLER_SELECTOR3 = ".cv-p-thread__scroll";
|
|
2850
|
+
function useOpenUpwards(isOpen, panelRef) {
|
|
2851
|
+
const [isUpwards, setUpwards] = useState9(false);
|
|
2852
|
+
useLayoutEffect3(() => {
|
|
2853
|
+
const panel = panelRef.current;
|
|
2854
|
+
if (!isOpen || !panel) {
|
|
2855
|
+
setUpwards(false);
|
|
2856
|
+
return;
|
|
2857
|
+
}
|
|
2858
|
+
const scroller = panel.closest(SCROLLER_SELECTOR3);
|
|
2859
|
+
if (!scroller) return;
|
|
2860
|
+
setUpwards(panel.getBoundingClientRect().bottom > scroller.getBoundingClientRect().bottom);
|
|
2861
|
+
}, [isOpen, panelRef]);
|
|
2862
|
+
return isUpwards;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
// src/participant/ParticipantMessageInfo.tsx
|
|
2866
|
+
import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2867
|
+
function ParticipantMessageInfo({ state, message, onClose }) {
|
|
2868
|
+
const panelRef = useRef13(null);
|
|
2869
|
+
const closeRef = useRef13(null);
|
|
2870
|
+
const { labels } = state;
|
|
2871
|
+
const title = labels.messageInfo ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.messageInfo ?? "";
|
|
2872
|
+
const rows = messageInfoRows(message, labels, state.locale);
|
|
2873
|
+
const isUpwards = useOpenUpwards(true, panelRef);
|
|
2874
|
+
useEffect14(() => {
|
|
2875
|
+
closeRef.current?.focus();
|
|
2876
|
+
function handleKeyDown(event) {
|
|
2877
|
+
if (event.key === "Escape") onClose();
|
|
2878
|
+
}
|
|
2879
|
+
function handlePointerDown(event) {
|
|
2880
|
+
if (event.target instanceof Node && !panelRef.current?.contains(event.target)) onClose();
|
|
2881
|
+
}
|
|
2882
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2883
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
2884
|
+
return () => {
|
|
2885
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
2886
|
+
document.removeEventListener("pointerdown", handlePointerDown);
|
|
2887
|
+
};
|
|
2888
|
+
}, [onClose]);
|
|
2889
|
+
return /* @__PURE__ */ jsxs23(
|
|
2890
|
+
"div",
|
|
2891
|
+
{
|
|
2892
|
+
ref: panelRef,
|
|
2893
|
+
role: "dialog",
|
|
2894
|
+
"aria-label": title,
|
|
2895
|
+
className: `cv-p-failed-menu__list cv-p-message-menu__list cv-p-message-info${isUpwards ? " cv-p-message-menu__list--up" : ""}`,
|
|
2896
|
+
children: [
|
|
2897
|
+
/* @__PURE__ */ jsxs23("div", { className: "cv-p-message-info__head", children: [
|
|
2898
|
+
/* @__PURE__ */ jsx29("span", { className: "cv-p-message-info__title", children: title }),
|
|
2899
|
+
/* @__PURE__ */ jsx29(
|
|
2900
|
+
"button",
|
|
2901
|
+
{
|
|
2902
|
+
ref: closeRef,
|
|
2903
|
+
type: "button",
|
|
2904
|
+
className: "cv-p-message-info__close",
|
|
2905
|
+
"aria-label": labels.closeMessageInfo ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.closeMessageInfo,
|
|
2906
|
+
onClick: onClose,
|
|
2907
|
+
children: /* @__PURE__ */ jsx29(X, { size: 14, "aria-hidden": true })
|
|
2908
|
+
}
|
|
2909
|
+
)
|
|
2910
|
+
] }),
|
|
2911
|
+
/* @__PURE__ */ jsx29("dl", { className: "cv-p-message-info__rows", children: rows.map((row) => /* @__PURE__ */ jsxs23("div", { className: "cv-p-message-info__row", children: [
|
|
2912
|
+
/* @__PURE__ */ jsx29("dt", { children: row.label }),
|
|
2913
|
+
/* @__PURE__ */ jsx29("dd", { children: row.value })
|
|
2914
|
+
] }, row.id)) })
|
|
2915
|
+
]
|
|
2916
|
+
}
|
|
2917
|
+
);
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
// src/participant/ParticipantMessageMenu.tsx
|
|
2921
|
+
import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2922
|
+
function transcriptionEntries(props, copy) {
|
|
2923
|
+
const { state, message } = props;
|
|
2924
|
+
const { labels } = state;
|
|
2925
|
+
const ready = readyTranscriptions(message.attachments);
|
|
2926
|
+
const [first] = ready;
|
|
2927
|
+
if (!first) return [];
|
|
2928
|
+
const prefix = labels.transcriptionPrefix ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.transcriptionPrefix ?? "";
|
|
2929
|
+
const text = ready.map((entry) => entry.text).join("\n\n");
|
|
2930
|
+
const author = message.isMine ? labels.me : message.authorName ?? "";
|
|
2931
|
+
const head = `[${formatMessageMoment(message.createdAt, state.locale)}]${author === "" ? "" : ` ${author}`}`;
|
|
2932
|
+
return [
|
|
2933
|
+
{
|
|
2934
|
+
id: "copy-transcription",
|
|
2935
|
+
label: labels.copyTranscription ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.copyTranscription ?? "",
|
|
2936
|
+
run: () => copy(text)
|
|
2937
|
+
},
|
|
2938
|
+
{
|
|
2939
|
+
id: "download-transcription",
|
|
2940
|
+
label: labels.downloadTranscription ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.downloadTranscription ?? "",
|
|
2941
|
+
run: () => downloadTextFile(transcriptionFileName(first.filename, prefix), `${head}
|
|
2942
|
+
|
|
2943
|
+
${text}
|
|
2944
|
+
`)
|
|
2945
|
+
}
|
|
2946
|
+
];
|
|
2947
|
+
}
|
|
2948
|
+
function entriesFor(props, copy, showInfo) {
|
|
2949
|
+
const { state, message, selectedText } = props;
|
|
2950
|
+
const { labels } = state;
|
|
2951
|
+
const text = copyableText(message);
|
|
2952
|
+
const own = [];
|
|
2953
|
+
if (selectedText !== "" && selectedText !== text) {
|
|
2954
|
+
own.push({
|
|
2955
|
+
id: "copy-selection",
|
|
2956
|
+
label: labels.copySelection ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.copySelection ?? "",
|
|
2957
|
+
run: () => copy(selectedText)
|
|
2958
|
+
});
|
|
2959
|
+
}
|
|
2960
|
+
if (text !== null)
|
|
2961
|
+
own.push({ id: "copy", label: labels.copyMessage ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.copyMessage ?? "", run: () => copy(text) });
|
|
2962
|
+
own.push({
|
|
2963
|
+
id: "select",
|
|
2964
|
+
label: labels.selectMessages ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.selectMessages ?? "",
|
|
2965
|
+
run: () => state.start(message.id)
|
|
2966
|
+
});
|
|
2967
|
+
own.push(...transcriptionEntries(props, copy));
|
|
2968
|
+
own.push({ id: "info", label: labels.messageInfo ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.messageInfo ?? "", run: showInfo });
|
|
2969
|
+
return [...own, ...(state.options.items?.(message) ?? []).map((item) => ({ ...item, run: item.onSelect }))];
|
|
2970
|
+
}
|
|
2971
|
+
function ParticipantMessageMenu(props) {
|
|
2972
|
+
const { menu, state } = props;
|
|
2973
|
+
const triggerId = useId4();
|
|
2974
|
+
const { copy } = useParticipantCopyFeedback();
|
|
2975
|
+
const label = state.labels.messageOptions ?? DEFAULT_MESSAGE_OPTIONS_LABEL;
|
|
2976
|
+
const [isInfoOpen, setInfoOpen] = useState10(false);
|
|
2977
|
+
const closeInfo = useCallback6(() => setInfoOpen(false), []);
|
|
2978
|
+
const entries = entriesFor(
|
|
2979
|
+
props,
|
|
2980
|
+
(value) => copy(value, state.labels.valueCopied),
|
|
2981
|
+
() => setInfoOpen(true)
|
|
2982
|
+
);
|
|
2983
|
+
const isUpwards = useOpenUpwards(menu.state.isOpen, menu.menuRef);
|
|
2984
|
+
const listClass = `cv-p-failed-menu__list cv-p-message-menu__list${isUpwards ? " cv-p-message-menu__list--up" : ""}`;
|
|
2985
|
+
function choose(entry) {
|
|
2986
|
+
menu.close();
|
|
2987
|
+
entry.run?.();
|
|
2988
|
+
}
|
|
2989
|
+
return /* @__PURE__ */ jsxs24(
|
|
2990
|
+
"span",
|
|
2991
|
+
{
|
|
2992
|
+
className: "cv-p-message-menu",
|
|
2993
|
+
"data-open": menu.state.isOpen || isInfoOpen ? "true" : void 0,
|
|
2994
|
+
ref: menu.containerRef,
|
|
2995
|
+
onBlur: menu.handleBlur,
|
|
2996
|
+
children: [
|
|
2997
|
+
/* @__PURE__ */ jsx30(
|
|
2998
|
+
"button",
|
|
2999
|
+
{
|
|
3000
|
+
type: "button",
|
|
3001
|
+
id: triggerId,
|
|
3002
|
+
ref: menu.triggerRef,
|
|
3003
|
+
className: "cv-p-message-menu__trigger",
|
|
3004
|
+
"aria-haspopup": "menu",
|
|
3005
|
+
"aria-expanded": menu.state.isOpen,
|
|
3006
|
+
"aria-label": label,
|
|
3007
|
+
onPointerDown: props.onTriggerPointerDown,
|
|
3008
|
+
onClick: menu.handleTriggerClick,
|
|
3009
|
+
onKeyDown: menu.handleTriggerKeyDown,
|
|
3010
|
+
children: /* @__PURE__ */ jsx30(ChevronDown2, { size: 16, "aria-hidden": true })
|
|
3011
|
+
}
|
|
3012
|
+
),
|
|
3013
|
+
menu.state.isOpen ? /* @__PURE__ */ jsx30(
|
|
3014
|
+
"div",
|
|
3015
|
+
{
|
|
3016
|
+
role: "menu",
|
|
3017
|
+
"aria-labelledby": triggerId,
|
|
3018
|
+
ref: menu.menuRef,
|
|
3019
|
+
className: listClass,
|
|
3020
|
+
onKeyDown: menu.handleMenuKeyDown,
|
|
3021
|
+
children: entries.map(
|
|
3022
|
+
(entry) => entry.href ? /* @__PURE__ */ jsx30(
|
|
3023
|
+
"a",
|
|
3024
|
+
{
|
|
3025
|
+
role: "menuitem",
|
|
3026
|
+
tabIndex: -1,
|
|
3027
|
+
className: "cv-p-failed-menu__item cv-p-message-menu__item",
|
|
3028
|
+
href: entry.href,
|
|
3029
|
+
download: entry.download,
|
|
3030
|
+
target: "_blank",
|
|
3031
|
+
rel: "noopener noreferrer",
|
|
3032
|
+
"aria-disabled": entry.disabled ? "true" : void 0,
|
|
3033
|
+
onClick: () => choose(entry),
|
|
3034
|
+
children: entry.label
|
|
3035
|
+
},
|
|
3036
|
+
entry.id
|
|
3037
|
+
) : /* @__PURE__ */ jsx30(
|
|
3038
|
+
"button",
|
|
3039
|
+
{
|
|
3040
|
+
type: "button",
|
|
3041
|
+
role: "menuitem",
|
|
3042
|
+
tabIndex: -1,
|
|
3043
|
+
className: "cv-p-failed-menu__item cv-p-message-menu__item",
|
|
3044
|
+
disabled: entry.disabled,
|
|
3045
|
+
onClick: () => choose(entry),
|
|
3046
|
+
children: entry.label
|
|
3047
|
+
},
|
|
3048
|
+
entry.id
|
|
3049
|
+
)
|
|
3050
|
+
)
|
|
3051
|
+
}
|
|
3052
|
+
) : null,
|
|
3053
|
+
isInfoOpen ? /* @__PURE__ */ jsx30(ParticipantMessageInfo, { state, message: props.message, onClose: closeInfo }) : null
|
|
3054
|
+
]
|
|
3055
|
+
}
|
|
3056
|
+
);
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
// src/participant/ParticipantMenuBubble.tsx
|
|
3060
|
+
import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3061
|
+
var LONG_PRESS_MS = 500;
|
|
3062
|
+
function ParticipantMenuBubble({ props, content, textId, state }) {
|
|
3063
|
+
const menu = useParticipantFailedMenu();
|
|
3064
|
+
const bubbleRef = useRef14(null);
|
|
3065
|
+
const pressTimer = useRef14(void 0);
|
|
3066
|
+
const [selectedText, setSelectedText] = useState11("");
|
|
3067
|
+
const id = content.messageId ?? "";
|
|
3068
|
+
const message = state.messages.find((candidate) => candidate.id === id);
|
|
3069
|
+
function captureSelection() {
|
|
3070
|
+
if (!menu.state.isOpen) setSelectedText(selectedTextWithin(bubbleRef.current));
|
|
3071
|
+
}
|
|
3072
|
+
function openFromGesture() {
|
|
3073
|
+
captureSelection();
|
|
3074
|
+
if (!menu.state.isOpen) menu.open();
|
|
3075
|
+
}
|
|
3076
|
+
function cancelPress() {
|
|
3077
|
+
clearTimeout(pressTimer.current);
|
|
3078
|
+
}
|
|
3079
|
+
if (state.isSelecting)
|
|
3080
|
+
return /* @__PURE__ */ jsx31(SelectRow, { state, id, children: renderParticipantBubble({ props, content, textId }) });
|
|
3081
|
+
if (!message) return renderParticipantBubble({ props, content, textId });
|
|
3082
|
+
return renderParticipantBubble({
|
|
3083
|
+
props,
|
|
3084
|
+
content,
|
|
3085
|
+
textId,
|
|
3086
|
+
frame: {
|
|
3087
|
+
ref: bubbleRef,
|
|
3088
|
+
className: menu.state.isOpen ? "cv-p-bubble--menu-open" : void 0,
|
|
3089
|
+
handlers: {
|
|
3090
|
+
onContextMenu: (event) => {
|
|
3091
|
+
event.preventDefault();
|
|
3092
|
+
openFromGesture();
|
|
3093
|
+
},
|
|
3094
|
+
onPointerDown: (event) => {
|
|
3095
|
+
if (event.pointerType !== "touch") return;
|
|
3096
|
+
cancelPress();
|
|
3097
|
+
pressTimer.current = setTimeout(openFromGesture, LONG_PRESS_MS);
|
|
3098
|
+
},
|
|
3099
|
+
onPointerUp: cancelPress,
|
|
3100
|
+
onPointerCancel: cancelPress,
|
|
3101
|
+
onPointerMove: cancelPress
|
|
3102
|
+
},
|
|
3103
|
+
corner: /* @__PURE__ */ jsx31(
|
|
3104
|
+
ParticipantMessageMenu,
|
|
3105
|
+
{
|
|
3106
|
+
menu,
|
|
3107
|
+
state,
|
|
3108
|
+
message,
|
|
3109
|
+
selectedText,
|
|
3110
|
+
onTriggerPointerDown: captureSelection
|
|
3111
|
+
}
|
|
3112
|
+
)
|
|
3113
|
+
}
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
function SelectRow({ state, id, children }) {
|
|
3117
|
+
const isSelected = state.selectedIds.includes(id);
|
|
3118
|
+
const label = state.labels.selectMessage ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.selectMessage ?? "";
|
|
3119
|
+
return /* @__PURE__ */ jsxs25("div", { className: "cv-p-select-row", "data-selected": isSelected ? "true" : void 0, onClick: () => state.toggle(id), children: [
|
|
3120
|
+
/* @__PURE__ */ jsx31(
|
|
3121
|
+
"input",
|
|
3122
|
+
{
|
|
3123
|
+
type: "checkbox",
|
|
3124
|
+
className: "cv-p-select-row__check",
|
|
3125
|
+
checked: isSelected,
|
|
3126
|
+
"aria-label": label,
|
|
3127
|
+
onChange: () => state.toggle(id),
|
|
3128
|
+
onClick: (event) => event.stopPropagation()
|
|
3129
|
+
}
|
|
3130
|
+
),
|
|
3131
|
+
children
|
|
3132
|
+
] });
|
|
3133
|
+
}
|
|
3134
|
+
|
|
3135
|
+
// src/participant/ParticipantMessageMenuContext.tsx
|
|
3136
|
+
import { createContext as createContext2, useCallback as useCallback7, useContext as useContext2, useEffect as useEffect15, useMemo as useMemo3, useState as useState12 } from "react";
|
|
3137
|
+
|
|
2625
3138
|
// src/participant/participantPerspective.ts
|
|
2626
3139
|
var DEFAULT_PARTICIPANT_PERSPECTIVE = "participant";
|
|
2627
3140
|
var OWN_DIRECTION = {
|
|
@@ -2683,8 +3196,74 @@ function resolveOwnMessageStatus(params) {
|
|
|
2683
3196
|
return serverStatus;
|
|
2684
3197
|
}
|
|
2685
3198
|
|
|
3199
|
+
// src/participant/ParticipantMessageMenuContext.tsx
|
|
3200
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
3201
|
+
var MessageMenuContext = createContext2(null);
|
|
3202
|
+
function useParticipantMessageMenu() {
|
|
3203
|
+
return useContext2(MessageMenuContext);
|
|
3204
|
+
}
|
|
3205
|
+
function toMessageTargets(items, perspective) {
|
|
3206
|
+
return items.flatMap(
|
|
3207
|
+
(item) => item.kind === "server" ? [
|
|
3208
|
+
{
|
|
3209
|
+
id: item.message.id,
|
|
3210
|
+
text: item.message.text ?? null,
|
|
3211
|
+
authorName: item.message.authorName ?? null,
|
|
3212
|
+
isMine: isOwnMessage(item.message, perspective),
|
|
3213
|
+
attachments: item.message.attachments,
|
|
3214
|
+
createdAt: item.message.createdAt,
|
|
3215
|
+
status: item.message.status,
|
|
3216
|
+
readAt: item.message.readAt ?? null
|
|
3217
|
+
}
|
|
3218
|
+
] : []
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
function ParticipantMessageMenuProvider({
|
|
3222
|
+
options,
|
|
3223
|
+
labels,
|
|
3224
|
+
locale,
|
|
3225
|
+
items,
|
|
3226
|
+
perspective,
|
|
3227
|
+
children
|
|
3228
|
+
}) {
|
|
3229
|
+
const [selection, setSelection] = useState12({
|
|
3230
|
+
isSelecting: false,
|
|
3231
|
+
ids: []
|
|
3232
|
+
});
|
|
3233
|
+
const clear = useCallback7(() => setSelection({ isSelecting: false, ids: [] }), []);
|
|
3234
|
+
const start = useCallback7((id) => setSelection({ isSelecting: true, ids: id ? [id] : [] }), []);
|
|
3235
|
+
const toggle = useCallback7(
|
|
3236
|
+
(id) => setSelection((current) => ({ ...current, ids: toggleSelectedId(current.ids, id) })),
|
|
3237
|
+
[]
|
|
3238
|
+
);
|
|
3239
|
+
const messages = useMemo3(() => toMessageTargets(items, perspective), [items, perspective]);
|
|
3240
|
+
useEffect15(() => {
|
|
3241
|
+
if (!selection.isSelecting) return void 0;
|
|
3242
|
+
function handleKeyDown(event) {
|
|
3243
|
+
if (event.key === "Escape") clear();
|
|
3244
|
+
}
|
|
3245
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3246
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
3247
|
+
}, [selection.isSelecting, clear]);
|
|
3248
|
+
const value = useMemo3(
|
|
3249
|
+
() => options ? {
|
|
3250
|
+
options,
|
|
3251
|
+
labels,
|
|
3252
|
+
locale,
|
|
3253
|
+
messages,
|
|
3254
|
+
selectedIds: selection.ids,
|
|
3255
|
+
isSelecting: selection.isSelecting,
|
|
3256
|
+
start,
|
|
3257
|
+
toggle,
|
|
3258
|
+
clear
|
|
3259
|
+
} : null,
|
|
3260
|
+
[options, labels, locale, messages, selection, start, toggle, clear]
|
|
3261
|
+
);
|
|
3262
|
+
return /* @__PURE__ */ jsx32(MessageMenuContext.Provider, { value, children });
|
|
3263
|
+
}
|
|
3264
|
+
|
|
2686
3265
|
// src/participant/ParticipantMessageBubble.tsx
|
|
2687
|
-
import { jsx as
|
|
3266
|
+
import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2688
3267
|
function describeItem(item, perspective) {
|
|
2689
3268
|
if (item.kind === "server") {
|
|
2690
3269
|
const { message } = item;
|
|
@@ -2716,23 +3295,32 @@ function bubbleClassName(isMine, hasTail, hasFailed) {
|
|
|
2716
3295
|
if (!hasTail) classes.push("cv-p-bubble--no-tail");
|
|
2717
3296
|
return classes.join(" ");
|
|
2718
3297
|
}
|
|
2719
|
-
function
|
|
2720
|
-
const {
|
|
2721
|
-
const textId = useId3();
|
|
2722
|
-
const content = describeItem(item, perspective);
|
|
3298
|
+
function renderParticipantBubble({ props, content, textId, frame }) {
|
|
3299
|
+
const { confirmsRead, onRetry, avatar, tail = true } = props;
|
|
2723
3300
|
const ownStatus = content.isMine ? resolveOwnMessageStatus({ displayState: content.displayState, serverStatus: content.serverStatus, confirmsRead }) : void 0;
|
|
2724
3301
|
const isFailed = ownStatus === "failed";
|
|
2725
3302
|
const canRetry = isFailed && onRetry !== void 0;
|
|
2726
|
-
const
|
|
2727
|
-
|
|
2728
|
-
/* @__PURE__ */
|
|
3303
|
+
const className = [bubbleClassName(content.isMine, tail, isFailed), frame?.className].filter(Boolean).join(" ");
|
|
3304
|
+
const bubble = /* @__PURE__ */ jsxs26("div", { className, ref: frame?.ref, ...frame?.handlers, children: [
|
|
3305
|
+
canRetry ? /* @__PURE__ */ jsx33(RetryButton, { labels: props.labels, onRetry, describedBy: textId }) : null,
|
|
3306
|
+
/* @__PURE__ */ jsx33(BubbleBody, { ...props, content, ownStatus, textId: canRetry ? textId : void 0 }),
|
|
3307
|
+
frame?.corner
|
|
2729
3308
|
] });
|
|
2730
3309
|
if (avatar === void 0 || content.isMine) return bubble;
|
|
2731
|
-
return /* @__PURE__ */
|
|
2732
|
-
/* @__PURE__ */
|
|
3310
|
+
return /* @__PURE__ */ jsxs26("div", { className: "cv-p-bubble-row", children: [
|
|
3311
|
+
/* @__PURE__ */ jsx33("span", { className: "cv-p-bubble-row__avatar", children: avatar }),
|
|
2733
3312
|
bubble
|
|
2734
3313
|
] });
|
|
2735
3314
|
}
|
|
3315
|
+
function ParticipantMessageBubble(props) {
|
|
3316
|
+
const textId = useId5();
|
|
3317
|
+
const menu = useParticipantMessageMenu();
|
|
3318
|
+
const content = describeItem(props.item, props.perspective);
|
|
3319
|
+
if (menu && content.messageId !== void 0) {
|
|
3320
|
+
return /* @__PURE__ */ jsx33(ParticipantMenuBubble, { props, content, textId, state: menu });
|
|
3321
|
+
}
|
|
3322
|
+
return renderParticipantBubble({ props, content, textId });
|
|
3323
|
+
}
|
|
2736
3324
|
|
|
2737
3325
|
// src/participant/participantBubbleActions.ts
|
|
2738
3326
|
function bindPendingActions(actions, clientMessageId) {
|
|
@@ -2751,7 +3339,7 @@ function resolveParticipantBubbleActions(item, handlers) {
|
|
|
2751
3339
|
}
|
|
2752
3340
|
|
|
2753
3341
|
// src/participant/ParticipantTimeline.tsx
|
|
2754
|
-
import { jsx as
|
|
3342
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
2755
3343
|
function createdAtOf2(item) {
|
|
2756
3344
|
return item.kind === "server" ? item.message.createdAt : item.pending.createdAt;
|
|
2757
3345
|
}
|
|
@@ -2768,7 +3356,7 @@ function resolveActions(item, pendingActions) {
|
|
|
2768
3356
|
}
|
|
2769
3357
|
function resolveAvatarSlot({ isEnabled, isMine, showAvatar, author, render }) {
|
|
2770
3358
|
if (!isEnabled || isMine) return void 0;
|
|
2771
|
-
return showAvatar ? /* @__PURE__ */
|
|
3359
|
+
return showAvatar ? /* @__PURE__ */ jsx34(ParticipantAuthorAvatar, { name: author, render }) : null;
|
|
2772
3360
|
}
|
|
2773
3361
|
function ParticipantTimeline(props) {
|
|
2774
3362
|
const { items, labels, locale, resolveAttachmentUrl, pendingActions } = props;
|
|
@@ -2780,7 +3368,7 @@ function ParticipantTimeline(props) {
|
|
|
2780
3368
|
for (const item of items) {
|
|
2781
3369
|
const day = new Date(createdAtOf2(item));
|
|
2782
3370
|
if (previousDay === void 0 || !isSameDay(previousDay, day)) {
|
|
2783
|
-
nodes.push(/* @__PURE__ */
|
|
3371
|
+
nodes.push(/* @__PURE__ */ jsx34(ParticipantDayDivider, { day, locale }, `day:${keyOf(item)}`));
|
|
2784
3372
|
previousAuthor = void 0;
|
|
2785
3373
|
}
|
|
2786
3374
|
previousDay = day;
|
|
@@ -2795,7 +3383,7 @@ function ParticipantTimeline(props) {
|
|
|
2795
3383
|
render: props.renderAuthorAvatar
|
|
2796
3384
|
});
|
|
2797
3385
|
nodes.push(
|
|
2798
|
-
/* @__PURE__ */
|
|
3386
|
+
/* @__PURE__ */ jsx34(
|
|
2799
3387
|
ParticipantMessageBubble,
|
|
2800
3388
|
{
|
|
2801
3389
|
item,
|
|
@@ -2812,19 +3400,70 @@ function ParticipantTimeline(props) {
|
|
|
2812
3400
|
)
|
|
2813
3401
|
);
|
|
2814
3402
|
}
|
|
2815
|
-
return /* @__PURE__ */
|
|
3403
|
+
return /* @__PURE__ */ jsx34("div", { className: "cv-p-thread__messages", children: nodes });
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
// src/participant/ParticipantSelectionBar.tsx
|
|
3407
|
+
import { X as X2 } from "lucide-react";
|
|
3408
|
+
import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
3409
|
+
function ParticipantSelectionBar({ state }) {
|
|
3410
|
+
const { labels } = state;
|
|
3411
|
+
const { copy } = useParticipantCopyFeedback();
|
|
3412
|
+
const selected = selectedInOrder(state.messages, state.selectedIds);
|
|
3413
|
+
const count = formatParticipantLabel(labels.selectedCount ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.selectedCount ?? "{count}", selected.length);
|
|
3414
|
+
const actions = state.options.selectionActions?.(selected) ?? [];
|
|
3415
|
+
const isEmpty = selected.length === 0;
|
|
3416
|
+
function copySelected() {
|
|
3417
|
+
copy(
|
|
3418
|
+
formatMessagesForCopy(selected, {
|
|
3419
|
+
locale: state.locale,
|
|
3420
|
+
meLabel: labels.me,
|
|
3421
|
+
transcriptionLabel: labels.transcriptionPrefix ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.transcriptionPrefix
|
|
3422
|
+
}),
|
|
3423
|
+
labels.valueCopied
|
|
3424
|
+
);
|
|
3425
|
+
state.clear();
|
|
3426
|
+
}
|
|
3427
|
+
return /* @__PURE__ */ jsxs27("div", { className: "cv-p-selection-bar", role: "toolbar", "aria-label": count, children: [
|
|
3428
|
+
/* @__PURE__ */ jsx35(
|
|
3429
|
+
"button",
|
|
3430
|
+
{
|
|
3431
|
+
type: "button",
|
|
3432
|
+
className: "cv-p-selection-bar__close",
|
|
3433
|
+
"aria-label": labels.cancelSelection ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.cancelSelection,
|
|
3434
|
+
onClick: state.clear,
|
|
3435
|
+
children: /* @__PURE__ */ jsx35(X2, { size: 18, "aria-hidden": true })
|
|
3436
|
+
}
|
|
3437
|
+
),
|
|
3438
|
+
/* @__PURE__ */ jsx35("span", { className: "cv-p-selection-bar__count", "aria-live": "polite", children: count }),
|
|
3439
|
+
/* @__PURE__ */ jsx35("button", { type: "button", className: "cv-p-selection-bar__action", disabled: isEmpty, onClick: copySelected, children: labels.copyMessage ?? DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS.copyMessage }),
|
|
3440
|
+
actions.map((action) => /* @__PURE__ */ jsx35(
|
|
3441
|
+
"button",
|
|
3442
|
+
{
|
|
3443
|
+
type: "button",
|
|
3444
|
+
className: "cv-p-selection-bar__action",
|
|
3445
|
+
disabled: isEmpty || action.disabled,
|
|
3446
|
+
onClick: () => {
|
|
3447
|
+
action.onSelect(selected);
|
|
3448
|
+
state.clear();
|
|
3449
|
+
},
|
|
3450
|
+
children: action.label
|
|
3451
|
+
},
|
|
3452
|
+
action.id
|
|
3453
|
+
))
|
|
3454
|
+
] });
|
|
2816
3455
|
}
|
|
2817
3456
|
|
|
2818
3457
|
// src/participant/ParticipantConversationAvatar.tsx
|
|
2819
|
-
import { jsx as
|
|
3458
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2820
3459
|
function ParticipantConversationAvatar({ icon }) {
|
|
2821
|
-
return /* @__PURE__ */
|
|
3460
|
+
return /* @__PURE__ */ jsx36("span", { className: "cv-p-thread__avatar", "aria-hidden": "true", children: icon });
|
|
2822
3461
|
}
|
|
2823
3462
|
|
|
2824
3463
|
// src/participant/ParticipantProtocolBadge.tsx
|
|
2825
3464
|
import { Check as Check2, Copy as Copy2 } from "lucide-react";
|
|
2826
|
-
import { useEffect as
|
|
2827
|
-
import { Fragment as Fragment7, jsx as
|
|
3465
|
+
import { useEffect as useEffect16, useReducer as useReducer4 } from "react";
|
|
3466
|
+
import { Fragment as Fragment7, jsx as jsx37, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2828
3467
|
var COPIED_ANNOUNCEMENT_MS = 3e3;
|
|
2829
3468
|
function ParticipantProtocolBadgeView({
|
|
2830
3469
|
protocol,
|
|
@@ -2833,16 +3472,16 @@ function ParticipantProtocolBadgeView({
|
|
|
2833
3472
|
onCopy
|
|
2834
3473
|
}) {
|
|
2835
3474
|
const { isCopied, copyCount } = copyState;
|
|
2836
|
-
return /* @__PURE__ */
|
|
2837
|
-
/* @__PURE__ */
|
|
2838
|
-
/* @__PURE__ */
|
|
2839
|
-
/* @__PURE__ */
|
|
3475
|
+
return /* @__PURE__ */ jsxs28(Fragment7, { children: [
|
|
3476
|
+
/* @__PURE__ */ jsxs28("span", { className: "cv-p-protocol cv-p-protocol--header", children: [
|
|
3477
|
+
/* @__PURE__ */ jsxs28("span", { className: "cv-p-protocol__code", children: [
|
|
3478
|
+
/* @__PURE__ */ jsxs28("span", { className: "cv-p-sr-only", children: [
|
|
2840
3479
|
labels.protocolPrefix,
|
|
2841
3480
|
" "
|
|
2842
3481
|
] }),
|
|
2843
3482
|
protocol
|
|
2844
3483
|
] }),
|
|
2845
|
-
/* @__PURE__ */
|
|
3484
|
+
/* @__PURE__ */ jsxs28(
|
|
2846
3485
|
"button",
|
|
2847
3486
|
{
|
|
2848
3487
|
type: "button",
|
|
@@ -2851,19 +3490,19 @@ function ParticipantProtocolBadgeView({
|
|
|
2851
3490
|
"data-cv-tooltip": labels.copyProtocol,
|
|
2852
3491
|
onClick: onCopy,
|
|
2853
3492
|
children: [
|
|
2854
|
-
isCopied ? /* @__PURE__ */
|
|
2855
|
-
isCopied ? /* @__PURE__ */
|
|
3493
|
+
isCopied ? /* @__PURE__ */ jsx37(Check2, { size: 18, "aria-hidden": true }) : /* @__PURE__ */ jsx37(Copy2, { size: 18, "aria-hidden": true }),
|
|
3494
|
+
isCopied ? /* @__PURE__ */ jsx37("span", { className: "cv-p-protocol__copied", "aria-hidden": "true", children: labels.protocolCopied }) : null
|
|
2856
3495
|
]
|
|
2857
3496
|
}
|
|
2858
3497
|
)
|
|
2859
3498
|
] }),
|
|
2860
|
-
/* @__PURE__ */
|
|
3499
|
+
/* @__PURE__ */ jsx37("div", { className: "cv-p-sr-only", role: "status", "aria-live": "polite", children: isCopied ? /* @__PURE__ */ jsx37("span", { children: labels.protocolCopied }, copyCount) : null })
|
|
2861
3500
|
] });
|
|
2862
3501
|
}
|
|
2863
3502
|
function ParticipantProtocolBadge({ protocol, labels }) {
|
|
2864
3503
|
const [copyState, dispatch] = useReducer4(protocolCopyReducer, INITIAL_PROTOCOL_COPY_STATE);
|
|
2865
3504
|
const { isCopied, copyCount } = copyState;
|
|
2866
|
-
|
|
3505
|
+
useEffect16(() => {
|
|
2867
3506
|
if (!isCopied) return void 0;
|
|
2868
3507
|
const timer = setTimeout(() => dispatch({ type: "expired" }), COPIED_ANNOUNCEMENT_MS);
|
|
2869
3508
|
return () => clearTimeout(timer);
|
|
@@ -2872,7 +3511,7 @@ function ParticipantProtocolBadge({ protocol, labels }) {
|
|
|
2872
3511
|
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
2873
3512
|
if (await copyProtocolToClipboard(protocol, clipboard)) dispatch({ type: "copied" });
|
|
2874
3513
|
}
|
|
2875
|
-
return /* @__PURE__ */
|
|
3514
|
+
return /* @__PURE__ */ jsx37(
|
|
2876
3515
|
ParticipantProtocolBadgeView,
|
|
2877
3516
|
{
|
|
2878
3517
|
protocol,
|
|
@@ -2884,19 +3523,19 @@ function ParticipantProtocolBadge({ protocol, labels }) {
|
|
|
2884
3523
|
}
|
|
2885
3524
|
|
|
2886
3525
|
// src/participant/ParticipantThreadHeader.tsx
|
|
2887
|
-
import { jsx as
|
|
3526
|
+
import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2888
3527
|
function Title({ conversation, labels, onOpenSubject }) {
|
|
2889
3528
|
const { subjectType, subjectId, subjectLabel } = conversation;
|
|
2890
|
-
return /* @__PURE__ */
|
|
3529
|
+
return /* @__PURE__ */ jsx38("h2", { className: "cv-p-thread__title", children: onOpenSubject ? /* @__PURE__ */ jsx38(
|
|
2891
3530
|
"button",
|
|
2892
3531
|
{
|
|
2893
3532
|
type: "button",
|
|
2894
3533
|
className: "cv-p-thread__title-button",
|
|
2895
3534
|
"aria-label": `${labels.openSubject}: ${subjectLabel}`,
|
|
2896
3535
|
onClick: () => onOpenSubject({ subjectType, subjectId }),
|
|
2897
|
-
children: /* @__PURE__ */
|
|
3536
|
+
children: /* @__PURE__ */ jsx38("span", { className: "cv-p-thread__title-text", children: subjectLabel })
|
|
2898
3537
|
}
|
|
2899
|
-
) : /* @__PURE__ */
|
|
3538
|
+
) : /* @__PURE__ */ jsx38("span", { className: "cv-p-thread__title-text", children: subjectLabel }) });
|
|
2900
3539
|
}
|
|
2901
3540
|
function ParticipantThreadHeader({
|
|
2902
3541
|
conversation,
|
|
@@ -2911,34 +3550,36 @@ function ParticipantThreadHeader({
|
|
|
2911
3550
|
const group = subjectGroups?.find((candidate) => candidate.subjectType === conversation.subjectType);
|
|
2912
3551
|
const icon = resolveConversationIcon({ conversation, group, renderSubjectIcon });
|
|
2913
3552
|
const hasChannels = resolveParticipantChannels(conversation.channels).length > 0;
|
|
2914
|
-
return /* @__PURE__ */
|
|
2915
|
-
onBack ? /* @__PURE__ */
|
|
2916
|
-
icon !== void 0 ? /* @__PURE__ */
|
|
2917
|
-
/* @__PURE__ */
|
|
2918
|
-
icon === void 0 && group ? /* @__PURE__ */
|
|
2919
|
-
/* @__PURE__ */
|
|
2920
|
-
counterpartLabel ? /* @__PURE__ */
|
|
2921
|
-
conversation.protocol || hasChannels ? /* @__PURE__ */
|
|
2922
|
-
conversation.protocol ? /* @__PURE__ */
|
|
2923
|
-
/* @__PURE__ */
|
|
3553
|
+
return /* @__PURE__ */ jsxs29("header", { className: "cv-p-thread__header", children: [
|
|
3554
|
+
onBack ? /* @__PURE__ */ jsx38(ParticipantBackButton, { label: labels.back, onBack }) : null,
|
|
3555
|
+
icon !== void 0 ? /* @__PURE__ */ jsx38(ParticipantConversationAvatar, { icon }) : null,
|
|
3556
|
+
/* @__PURE__ */ jsxs29("div", { className: "cv-p-thread__heading", children: [
|
|
3557
|
+
icon === void 0 && group ? /* @__PURE__ */ jsx38("p", { className: "cv-p-thread__eyebrow", children: group.label }) : null,
|
|
3558
|
+
/* @__PURE__ */ jsx38(Title, { conversation, labels, onOpenSubject }),
|
|
3559
|
+
counterpartLabel ? /* @__PURE__ */ jsx38("p", { className: "cv-p-thread__counterpart", children: counterpartLabel }) : null,
|
|
3560
|
+
conversation.protocol || hasChannels ? /* @__PURE__ */ jsxs29("div", { className: "cv-p-thread__meta", children: [
|
|
3561
|
+
conversation.protocol ? /* @__PURE__ */ jsx38(ParticipantProtocolBadge, { protocol: conversation.protocol, labels }) : null,
|
|
3562
|
+
/* @__PURE__ */ jsx38(ParticipantChannelBadges, { channels: conversation.channels, labels, variant: "inline", iconSize: 16 })
|
|
2924
3563
|
] }) : null
|
|
2925
3564
|
] }),
|
|
2926
|
-
actions ? /* @__PURE__ */
|
|
3565
|
+
actions ? /* @__PURE__ */ jsx38("div", { className: "cv-p-thread__actions", children: actions }) : null
|
|
2927
3566
|
] });
|
|
2928
3567
|
}
|
|
2929
3568
|
|
|
2930
3569
|
// src/participant/ParticipantThread.tsx
|
|
2931
|
-
import { jsx as
|
|
3570
|
+
import { Fragment as Fragment8, jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2932
3571
|
function Footer({ props }) {
|
|
2933
3572
|
const { conversation, labels, draft } = props;
|
|
3573
|
+
const menu = useParticipantMessageMenu();
|
|
3574
|
+
if (menu?.isSelecting) return /* @__PURE__ */ jsx39(ParticipantSelectionBar, { state: menu });
|
|
2934
3575
|
if (conversation.status === "closed") {
|
|
2935
|
-
return /* @__PURE__ */
|
|
3576
|
+
return /* @__PURE__ */ jsx39("p", { className: "cv-p-thread__closed", children: labels.closedNotice });
|
|
2936
3577
|
}
|
|
2937
3578
|
const lock = props.renderComposerLock?.(conversation);
|
|
2938
3579
|
if (lock !== void 0 && lock !== null) {
|
|
2939
|
-
return /* @__PURE__ */
|
|
3580
|
+
return /* @__PURE__ */ jsx39("div", { className: "cv-p-thread__lock", role: "region", "aria-live": "polite", children: lock });
|
|
2940
3581
|
}
|
|
2941
|
-
return /* @__PURE__ */
|
|
3582
|
+
return /* @__PURE__ */ jsx39(
|
|
2942
3583
|
ParticipantComposer,
|
|
2943
3584
|
{
|
|
2944
3585
|
value: draft.value,
|
|
@@ -2958,7 +3599,12 @@ function Footer({ props }) {
|
|
|
2958
3599
|
);
|
|
2959
3600
|
}
|
|
2960
3601
|
function Header({ props }) {
|
|
2961
|
-
|
|
3602
|
+
const menu = useParticipantMessageMenu();
|
|
3603
|
+
const actions = menu ? /* @__PURE__ */ jsxs30(Fragment8, { children: [
|
|
3604
|
+
props.headerActions,
|
|
3605
|
+
/* @__PURE__ */ jsx39(ParticipantConversationMenu, { state: menu, title: props.conversation.subjectLabel })
|
|
3606
|
+
] }) : props.headerActions;
|
|
3607
|
+
return /* @__PURE__ */ jsx39(
|
|
2962
3608
|
ParticipantThreadHeader,
|
|
2963
3609
|
{
|
|
2964
3610
|
conversation: props.conversation,
|
|
@@ -2968,7 +3614,7 @@ function Header({ props }) {
|
|
|
2968
3614
|
onBack: props.onBack,
|
|
2969
3615
|
onOpenSubject: props.onOpenSubject,
|
|
2970
3616
|
counterpartLabel: props.counterpartLabel,
|
|
2971
|
-
actions
|
|
3617
|
+
actions
|
|
2972
3618
|
}
|
|
2973
3619
|
);
|
|
2974
3620
|
}
|
|
@@ -2976,17 +3622,17 @@ function ThreadBody(props) {
|
|
|
2976
3622
|
const { conversation, labels, hasMore, onLoadOlder, renderSubjectCard } = props;
|
|
2977
3623
|
const subjectCard = renderSubjectCard?.(conversation);
|
|
2978
3624
|
const loadView = resolveLoadView({ status: props.status, hasItems: props.items.length > 0 });
|
|
2979
|
-
const rootRef =
|
|
3625
|
+
const rootRef = useRef15(null);
|
|
2980
3626
|
useSuppressTouchTooltips(rootRef);
|
|
2981
|
-
return /* @__PURE__ */
|
|
2982
|
-
/* @__PURE__ */
|
|
2983
|
-
subjectCard ? /* @__PURE__ */
|
|
2984
|
-
/* @__PURE__ */
|
|
2985
|
-
loadView.isLoading ? /* @__PURE__ */
|
|
2986
|
-
loadView.hasError ? /* @__PURE__ */
|
|
2987
|
-
hasMore && onLoadOlder ? /* @__PURE__ */
|
|
2988
|
-
/* @__PURE__ */
|
|
2989
|
-
/* @__PURE__ */
|
|
3627
|
+
return /* @__PURE__ */ jsxs30("div", { className: "cv-p cv-p-thread", "aria-busy": loadView.isLoading, ref: rootRef, children: [
|
|
3628
|
+
/* @__PURE__ */ jsx39(Header, { props }),
|
|
3629
|
+
subjectCard ? /* @__PURE__ */ jsx39("div", { className: "cv-p-thread__subject-card", children: subjectCard }) : null,
|
|
3630
|
+
/* @__PURE__ */ jsxs30("div", { className: "cv-p-thread__scroll", ref: props.scroll.ref, onScroll: props.scroll.onScroll, tabIndex: -1, children: [
|
|
3631
|
+
loadView.isLoading ? /* @__PURE__ */ jsx39(ParticipantLoading, { labels }) : null,
|
|
3632
|
+
loadView.hasError ? /* @__PURE__ */ jsx39(ParticipantLoadError, { labels, onRetry: props.refresh }) : null,
|
|
3633
|
+
hasMore && onLoadOlder ? /* @__PURE__ */ jsx39("button", { type: "button", className: "cv-p-thread__older", onClick: onLoadOlder, children: labels.loadOlder }) : null,
|
|
3634
|
+
/* @__PURE__ */ jsx39(ParticipantTimeline, { ...props }),
|
|
3635
|
+
/* @__PURE__ */ jsx39(
|
|
2990
3636
|
ParticipantScrollToLatest,
|
|
2991
3637
|
{
|
|
2992
3638
|
label: labels.scrollToLatest,
|
|
@@ -2997,19 +3643,29 @@ function ThreadBody(props) {
|
|
|
2997
3643
|
}
|
|
2998
3644
|
)
|
|
2999
3645
|
] }),
|
|
3000
|
-
/* @__PURE__ */
|
|
3001
|
-
/* @__PURE__ */
|
|
3646
|
+
/* @__PURE__ */ jsx39(ParticipantThreadLive, { notice: props.newMessagesCount > 0 ? labels.newMessages : "" }),
|
|
3647
|
+
/* @__PURE__ */ jsx39(Footer, { props })
|
|
3002
3648
|
] });
|
|
3003
3649
|
}
|
|
3004
3650
|
function ParticipantThread(props) {
|
|
3005
|
-
return /* @__PURE__ */
|
|
3006
|
-
/* @__PURE__ */
|
|
3007
|
-
|
|
3651
|
+
return /* @__PURE__ */ jsxs30(ParticipantCopyAnnouncer, { children: [
|
|
3652
|
+
/* @__PURE__ */ jsx39(
|
|
3653
|
+
ParticipantMessageMenuProvider,
|
|
3654
|
+
{
|
|
3655
|
+
options: props.messageMenu,
|
|
3656
|
+
labels: props.labels,
|
|
3657
|
+
locale: props.locale,
|
|
3658
|
+
items: props.items,
|
|
3659
|
+
perspective: props.perspective,
|
|
3660
|
+
children: /* @__PURE__ */ jsx39(ThreadBody, { ...props })
|
|
3661
|
+
}
|
|
3662
|
+
),
|
|
3663
|
+
/* @__PURE__ */ jsx39(TooltipLayer, {})
|
|
3008
3664
|
] });
|
|
3009
3665
|
}
|
|
3010
3666
|
|
|
3011
3667
|
// src/participant/useParticipantThreadController.ts
|
|
3012
|
-
import { useMemo as
|
|
3668
|
+
import { useMemo as useMemo6 } from "react";
|
|
3013
3669
|
|
|
3014
3670
|
// src/participant/bindAttachmentUrlResolver.ts
|
|
3015
3671
|
function bindAttachmentUrlResolver(api) {
|
|
@@ -3093,7 +3749,7 @@ function appendEntryToDraft(entry, current) {
|
|
|
3093
3749
|
}
|
|
3094
3750
|
|
|
3095
3751
|
// src/participant/useParticipantConversationView.ts
|
|
3096
|
-
import { useCallback as
|
|
3752
|
+
import { useCallback as useCallback11, useMemo as useMemo5 } from "react";
|
|
3097
3753
|
|
|
3098
3754
|
// src/participant/participantDrafts.ts
|
|
3099
3755
|
var EMPTY_PARTICIPANT_DRAFT = { text: "", files: [] };
|
|
@@ -3137,7 +3793,7 @@ function participantSendStatesReducer(states, { subject, action }) {
|
|
|
3137
3793
|
}
|
|
3138
3794
|
|
|
3139
3795
|
// src/participant/useParticipantThread.ts
|
|
3140
|
-
import { useCallback as
|
|
3796
|
+
import { useCallback as useCallback10, useEffect as useEffect20, useMemo as useMemo4, useReducer as useReducer5, useRef as useRef19 } from "react";
|
|
3141
3797
|
|
|
3142
3798
|
// src/participant/participantThreadState.ts
|
|
3143
3799
|
var INITIAL_PARTICIPANT_THREAD_STATE = {
|
|
@@ -3173,7 +3829,7 @@ function participantThreadReducer(state, action) {
|
|
|
3173
3829
|
}
|
|
3174
3830
|
|
|
3175
3831
|
// src/participant/useParticipantMarkRead.ts
|
|
3176
|
-
import { useEffect as
|
|
3832
|
+
import { useEffect as useEffect18 } from "react";
|
|
3177
3833
|
|
|
3178
3834
|
// src/participant/shouldMarkRead.ts
|
|
3179
3835
|
function shouldMarkParticipantRead(params) {
|
|
@@ -3185,12 +3841,12 @@ function shouldMarkParticipantRead(params) {
|
|
|
3185
3841
|
}
|
|
3186
3842
|
|
|
3187
3843
|
// src/participant/useDocumentVisibility.ts
|
|
3188
|
-
import { useEffect as
|
|
3844
|
+
import { useEffect as useEffect17, useState as useState13 } from "react";
|
|
3189
3845
|
function useDocumentVisibility() {
|
|
3190
|
-
const [visibilityState, setVisibilityState] =
|
|
3846
|
+
const [visibilityState, setVisibilityState] = useState13(
|
|
3191
3847
|
() => typeof document === "undefined" ? "visible" : document.visibilityState
|
|
3192
3848
|
);
|
|
3193
|
-
|
|
3849
|
+
useEffect17(() => {
|
|
3194
3850
|
function handleVisibilityChange() {
|
|
3195
3851
|
setVisibilityState(document.visibilityState);
|
|
3196
3852
|
}
|
|
@@ -3205,18 +3861,18 @@ function useDocumentVisibility() {
|
|
|
3205
3861
|
function useParticipantMarkRead(params) {
|
|
3206
3862
|
const { apiRef, subject, unreadCount, messageCount, onMarkedRead } = params;
|
|
3207
3863
|
const visibilityState = useDocumentVisibility();
|
|
3208
|
-
|
|
3864
|
+
useEffect18(() => {
|
|
3209
3865
|
if (!shouldMarkParticipantRead({ selected: subject, subject, visibilityState, unreadCount })) return;
|
|
3210
3866
|
void apiRef.current.markRead(subject).then(() => onMarkedRead?.(subject), () => void 0);
|
|
3211
3867
|
}, [apiRef, subject, visibilityState, unreadCount, messageCount, onMarkedRead]);
|
|
3212
3868
|
}
|
|
3213
3869
|
|
|
3214
3870
|
// src/participant/useParticipantRevalidation.ts
|
|
3215
|
-
import { useEffect as
|
|
3871
|
+
import { useEffect as useEffect19, useRef as useRef16 } from "react";
|
|
3216
3872
|
function useParticipantRevalidation(api, onRevalidate) {
|
|
3217
|
-
const handlerRef =
|
|
3873
|
+
const handlerRef = useRef16(onRevalidate);
|
|
3218
3874
|
handlerRef.current = onRevalidate;
|
|
3219
|
-
|
|
3875
|
+
useEffect19(() => {
|
|
3220
3876
|
function handleFocus() {
|
|
3221
3877
|
handlerRef.current();
|
|
3222
3878
|
}
|
|
@@ -3243,12 +3899,12 @@ function useParticipantRevalidation(api, onRevalidate) {
|
|
|
3243
3899
|
}
|
|
3244
3900
|
|
|
3245
3901
|
// src/participant/useParticipantSend.ts
|
|
3246
|
-
import { useCallback as
|
|
3902
|
+
import { useCallback as useCallback8, useRef as useRef17 } from "react";
|
|
3247
3903
|
function useParticipantSend(params) {
|
|
3248
3904
|
const { apiRef, subject, dispatchSend, onSentMessage } = params;
|
|
3249
|
-
const entriesRef =
|
|
3905
|
+
const entriesRef = useRef17(params.entries);
|
|
3250
3906
|
entriesRef.current = params.entries;
|
|
3251
|
-
const deliver =
|
|
3907
|
+
const deliver = useCallback8(
|
|
3252
3908
|
async (entry) => {
|
|
3253
3909
|
const { clientMessageId } = entry;
|
|
3254
3910
|
try {
|
|
@@ -3272,7 +3928,7 @@ function useParticipantSend(params) {
|
|
|
3272
3928
|
},
|
|
3273
3929
|
[apiRef, dispatchSend, onSentMessage]
|
|
3274
3930
|
);
|
|
3275
|
-
const send =
|
|
3931
|
+
const send = useCallback8(
|
|
3276
3932
|
async (content) => {
|
|
3277
3933
|
const entry = createParticipantSendEntry({
|
|
3278
3934
|
clientMessageId: crypto.randomUUID(),
|
|
@@ -3285,7 +3941,7 @@ function useParticipantSend(params) {
|
|
|
3285
3941
|
},
|
|
3286
3942
|
[deliver, dispatchSend, subject]
|
|
3287
3943
|
);
|
|
3288
|
-
const retry =
|
|
3944
|
+
const retry = useCallback8(
|
|
3289
3945
|
async (clientMessageId) => {
|
|
3290
3946
|
const entry = findFailedEntry(entriesRef.current, clientMessageId);
|
|
3291
3947
|
if (!entry) return;
|
|
@@ -3298,13 +3954,13 @@ function useParticipantSend(params) {
|
|
|
3298
3954
|
}
|
|
3299
3955
|
|
|
3300
3956
|
// src/participant/useParticipantUnread.ts
|
|
3301
|
-
import { useCallback as
|
|
3957
|
+
import { useCallback as useCallback9, useRef as useRef18, useState as useState14 } from "react";
|
|
3302
3958
|
function useParticipantUnread(params) {
|
|
3303
3959
|
const { messages, perspective, reportedUnreadCount, onMarkedRead } = params;
|
|
3304
|
-
const [acknowledgedCount, setAcknowledgedCount] =
|
|
3305
|
-
const messagesRef =
|
|
3960
|
+
const [acknowledgedCount, setAcknowledgedCount] = useState14(0);
|
|
3961
|
+
const messagesRef = useRef18(messages);
|
|
3306
3962
|
messagesRef.current = messages;
|
|
3307
|
-
const handleMarkedRead =
|
|
3963
|
+
const handleMarkedRead = useCallback9(
|
|
3308
3964
|
(subject) => {
|
|
3309
3965
|
if (perspective) setAcknowledgedCount(countOtherSideMessages(messagesRef.current, perspective));
|
|
3310
3966
|
onMarkedRead?.(subject);
|
|
@@ -3327,13 +3983,13 @@ function isForSubject(event, subject) {
|
|
|
3327
3983
|
function useParticipantThread(params) {
|
|
3328
3984
|
const { api, subject, channel, unreadCount = 0, onMarkedRead, sendEntries, dispatchSend, hostPending = [], perspective } = params;
|
|
3329
3985
|
const { subjectType, subjectId } = subject;
|
|
3330
|
-
const stableSubject =
|
|
3986
|
+
const stableSubject = useMemo4(() => ({ subjectType, subjectId }), [subjectType, subjectId]);
|
|
3331
3987
|
const [state, dispatch] = useReducer5(participantThreadReducer, INITIAL_PARTICIPANT_THREAD_STATE);
|
|
3332
|
-
const apiRef =
|
|
3988
|
+
const apiRef = useRef19(api);
|
|
3333
3989
|
apiRef.current = api;
|
|
3334
|
-
const activeSubjectKey =
|
|
3990
|
+
const activeSubjectKey = useRef19("");
|
|
3335
3991
|
const confirmsRead = channelCapabilityFor(channel).confirmsRead;
|
|
3336
|
-
const refresh =
|
|
3992
|
+
const refresh = useCallback10(async () => {
|
|
3337
3993
|
const requestKey = `${subjectType}:${subjectId}`;
|
|
3338
3994
|
dispatch({ type: "started" });
|
|
3339
3995
|
try {
|
|
@@ -3346,7 +4002,7 @@ function useParticipantThread(params) {
|
|
|
3346
4002
|
}
|
|
3347
4003
|
}, [stableSubject, subjectType, subjectId]);
|
|
3348
4004
|
const oldestMessageId = state.messages[0]?.id;
|
|
3349
|
-
const loadOlder =
|
|
4005
|
+
const loadOlder = useCallback10(async () => {
|
|
3350
4006
|
if (!oldestMessageId) return;
|
|
3351
4007
|
const requestKey = `${subjectType}:${subjectId}`;
|
|
3352
4008
|
try {
|
|
@@ -3358,9 +4014,9 @@ function useParticipantThread(params) {
|
|
|
3358
4014
|
if (activeSubjectKey.current === requestKey) dispatch({ type: "failed", error: describeError(error) });
|
|
3359
4015
|
}
|
|
3360
4016
|
}, [stableSubject, subjectType, subjectId, oldestMessageId]);
|
|
3361
|
-
const handleSentMessage =
|
|
4017
|
+
const handleSentMessage = useCallback10((message) => dispatch({ type: "sendConfirmed", message }), []);
|
|
3362
4018
|
const sending = useParticipantSend({ apiRef, subject: stableSubject, entries: sendEntries, dispatchSend, onSentMessage: handleSentMessage });
|
|
3363
|
-
|
|
4019
|
+
useEffect20(() => {
|
|
3364
4020
|
activeSubjectKey.current = `${subjectType}:${subjectId}`;
|
|
3365
4021
|
dispatch({ type: "reset" });
|
|
3366
4022
|
void refresh();
|
|
@@ -3368,7 +4024,7 @@ function useParticipantThread(params) {
|
|
|
3368
4024
|
useParticipantRevalidation(api, (event) => {
|
|
3369
4025
|
if (isForSubject(event, stableSubject)) void refresh();
|
|
3370
4026
|
});
|
|
3371
|
-
|
|
4027
|
+
useEffect20(() => {
|
|
3372
4028
|
if (sendEntries.length === 0) return;
|
|
3373
4029
|
dispatchSend({ type: "reflected", knownClientMessageIds: collectKnownClientMessageIds({ messages: state.messages, hostPending }) });
|
|
3374
4030
|
}, [sendEntries, state.messages, hostPending, dispatchSend]);
|
|
@@ -3380,7 +4036,7 @@ function useParticipantThread(params) {
|
|
|
3380
4036
|
messageCount: state.messages.length,
|
|
3381
4037
|
onMarkedRead: unread.handleMarkedRead
|
|
3382
4038
|
});
|
|
3383
|
-
const localPending =
|
|
4039
|
+
const localPending = useMemo4(() => toLocalPending(sendEntries), [sendEntries]);
|
|
3384
4040
|
return { ...state, localPending, confirmsRead, refresh, loadOlder, ...sending };
|
|
3385
4041
|
}
|
|
3386
4042
|
|
|
@@ -3389,7 +4045,7 @@ function useParticipantConversationView(params) {
|
|
|
3389
4045
|
const { api, subject, conversation, pendingMessages, sendStates, dispatchSendStates } = params;
|
|
3390
4046
|
const { subjectType, subjectId } = subject;
|
|
3391
4047
|
const sendEntries = selectSendEntries(sendStates, subject);
|
|
3392
|
-
const dispatchSend =
|
|
4048
|
+
const dispatchSend = useCallback11(
|
|
3393
4049
|
(action) => dispatchSendStates({ subject: { subjectType, subjectId }, action }),
|
|
3394
4050
|
[dispatchSendStates, subjectType, subjectId]
|
|
3395
4051
|
);
|
|
@@ -3404,7 +4060,7 @@ function useParticipantConversationView(params) {
|
|
|
3404
4060
|
hostPending: pendingMessages,
|
|
3405
4061
|
perspective: params.perspective
|
|
3406
4062
|
});
|
|
3407
|
-
const items =
|
|
4063
|
+
const items = useMemo5(
|
|
3408
4064
|
() => mergeParticipantMessages({
|
|
3409
4065
|
serverMessages: thread.messages,
|
|
3410
4066
|
hostPending: pendingMessages ?? [],
|
|
@@ -3417,9 +4073,9 @@ function useParticipantConversationView(params) {
|
|
|
3417
4073
|
}
|
|
3418
4074
|
|
|
3419
4075
|
// src/participant/useParticipantDraft.ts
|
|
3420
|
-
import { useState as
|
|
4076
|
+
import { useState as useState15 } from "react";
|
|
3421
4077
|
function useParticipantDraft(draftsRef, subject) {
|
|
3422
|
-
const [draft, setDraftState] =
|
|
4078
|
+
const [draft, setDraftState] = useState15(() => getDraft(draftsRef.current, subject));
|
|
3423
4079
|
function replaceDrafts(next) {
|
|
3424
4080
|
draftsRef.current = next;
|
|
3425
4081
|
setDraftState(getDraft(next, subject));
|
|
@@ -3439,12 +4095,12 @@ function useParticipantDraft(draftsRef, subject) {
|
|
|
3439
4095
|
}
|
|
3440
4096
|
|
|
3441
4097
|
// src/participant/useParticipantSendAudio.ts
|
|
3442
|
-
import { useCallback as
|
|
4098
|
+
import { useCallback as useCallback12, useRef as useRef20 } from "react";
|
|
3443
4099
|
function useParticipantSendAudio(send) {
|
|
3444
|
-
const sendRef =
|
|
4100
|
+
const sendRef = useRef20(send);
|
|
3445
4101
|
sendRef.current = send;
|
|
3446
|
-
const inFlightRef =
|
|
3447
|
-
return
|
|
4102
|
+
const inFlightRef = useRef20(/* @__PURE__ */ new Set());
|
|
4103
|
+
return useCallback12((file) => {
|
|
3448
4104
|
const inFlight = inFlightRef.current;
|
|
3449
4105
|
if (inFlight.has(file)) return;
|
|
3450
4106
|
inFlight.add(file);
|
|
@@ -3460,7 +4116,7 @@ function useParticipantSendAudio(send) {
|
|
|
3460
4116
|
}
|
|
3461
4117
|
|
|
3462
4118
|
// src/participant/useParticipantThreadScroll.ts
|
|
3463
|
-
import { useEffect as
|
|
4119
|
+
import { useEffect as useEffect21, useRef as useRef22 } from "react";
|
|
3464
4120
|
|
|
3465
4121
|
// src/participant/participantNewMessages.ts
|
|
3466
4122
|
function collectServerMessageIds(items) {
|
|
@@ -3485,9 +4141,9 @@ function countNewIncomingMessages(previousIds, currentItems, perspective) {
|
|
|
3485
4141
|
}
|
|
3486
4142
|
|
|
3487
4143
|
// src/participant/useScrollAwayState.ts
|
|
3488
|
-
import { useCallback as
|
|
4144
|
+
import { useCallback as useCallback13 } from "react";
|
|
3489
4145
|
function useScrollAwayState(ref, isNearBottom) {
|
|
3490
|
-
const scrollToLatest =
|
|
4146
|
+
const scrollToLatest = useCallback13(() => {
|
|
3491
4147
|
const element = ref.current;
|
|
3492
4148
|
if (!element) return;
|
|
3493
4149
|
element.scrollTo(
|
|
@@ -3498,7 +4154,7 @@ function useScrollAwayState(ref, isNearBottom) {
|
|
|
3498
4154
|
}
|
|
3499
4155
|
|
|
3500
4156
|
// src/participant/useStickToBottom.ts
|
|
3501
|
-
import { useCallback as
|
|
4157
|
+
import { useCallback as useCallback14, useLayoutEffect as useLayoutEffect4, useRef as useRef21, useState as useState16 } from "react";
|
|
3502
4158
|
|
|
3503
4159
|
// src/participant/participantScroll.ts
|
|
3504
4160
|
var NEAR_BOTTOM_THRESHOLD_PX = 80;
|
|
@@ -3520,17 +4176,17 @@ function resolveScrollAction(params) {
|
|
|
3520
4176
|
|
|
3521
4177
|
// src/participant/useStickToBottom.ts
|
|
3522
4178
|
function useStickToBottom({ firstKey, lastKey }) {
|
|
3523
|
-
const ref =
|
|
3524
|
-
const snapshot =
|
|
4179
|
+
const ref = useRef21(null);
|
|
4180
|
+
const snapshot = useRef21({
|
|
3525
4181
|
hasPositioned: false,
|
|
3526
4182
|
firstKey: void 0,
|
|
3527
4183
|
lastKey: void 0,
|
|
3528
4184
|
scrollTop: 0,
|
|
3529
4185
|
scrollHeight: 0
|
|
3530
4186
|
});
|
|
3531
|
-
const [isNearBottom, setIsNearBottom] =
|
|
3532
|
-
const isNearBottomRef =
|
|
3533
|
-
const onScroll =
|
|
4187
|
+
const [isNearBottom, setIsNearBottom] = useState16(true);
|
|
4188
|
+
const isNearBottomRef = useRef21(true);
|
|
4189
|
+
const onScroll = useCallback14((event) => {
|
|
3534
4190
|
const { scrollTop, clientHeight, scrollHeight } = event.currentTarget;
|
|
3535
4191
|
const isNear = shouldStickToBottom({ scrollTop, clientHeight, scrollHeight, threshold: NEAR_BOTTOM_THRESHOLD_PX });
|
|
3536
4192
|
snapshot.current.scrollTop = scrollTop;
|
|
@@ -3538,7 +4194,7 @@ function useStickToBottom({ firstKey, lastKey }) {
|
|
|
3538
4194
|
isNearBottomRef.current = isNear;
|
|
3539
4195
|
setIsNearBottom(isNear);
|
|
3540
4196
|
}, []);
|
|
3541
|
-
|
|
4197
|
+
useLayoutEffect4(() => {
|
|
3542
4198
|
const element = ref.current;
|
|
3543
4199
|
const previous = snapshot.current;
|
|
3544
4200
|
if (!element) return;
|
|
@@ -3585,8 +4241,8 @@ function useParticipantThreadScroll(items, perspective) {
|
|
|
3585
4241
|
});
|
|
3586
4242
|
const { isAwayFromBottom, scrollToLatest } = useScrollAwayState(base.ref, isNearBottom);
|
|
3587
4243
|
const scroll = { ...base, isAwayFromBottom, scrollToLatest };
|
|
3588
|
-
const seenIds =
|
|
3589
|
-
|
|
4244
|
+
const seenIds = useRef22(/* @__PURE__ */ new Set());
|
|
4245
|
+
useEffect21(() => {
|
|
3590
4246
|
if (isNearBottom) seenIds.current = collectServerMessageIds(items);
|
|
3591
4247
|
}, [isNearBottom, items]);
|
|
3592
4248
|
return { scroll, newMessagesCount: isNearBottom ? 0 : countNewIncomingMessages(seenIds.current, items, perspective) };
|
|
@@ -3613,7 +4269,7 @@ function useParticipantThreadController(params) {
|
|
|
3613
4269
|
const { api, subject, conversation, draftsRef } = params;
|
|
3614
4270
|
const { draft, setText, setFiles, takeForSend, restoreFromEntry } = useParticipantDraft(draftsRef, subject);
|
|
3615
4271
|
const { thread, items, sendEntries, dispatchSend } = useParticipantConversationView(params);
|
|
3616
|
-
const resolveAttachmentUrl =
|
|
4272
|
+
const resolveAttachmentUrl = useMemo6(() => bindAttachmentUrlResolver(api), [api]);
|
|
3617
4273
|
const { scroll, newMessagesCount } = useParticipantThreadScroll(items, params.perspective);
|
|
3618
4274
|
const handleSendAudio = useParticipantSendAudio(thread.send);
|
|
3619
4275
|
const handleEdit = buildEditHandler({ sendEntries, dispatchSend, restoreFromEntry });
|
|
@@ -3644,12 +4300,12 @@ function useParticipantThreadController(params) {
|
|
|
3644
4300
|
}
|
|
3645
4301
|
|
|
3646
4302
|
// src/participant/ParticipantThreadScreen.tsx
|
|
3647
|
-
import { jsx as
|
|
4303
|
+
import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3648
4304
|
function useOpenedConversation(api, subject, isNeeded) {
|
|
3649
|
-
const [opened, setOpened] =
|
|
4305
|
+
const [opened, setOpened] = useState17({ status: "idle" });
|
|
3650
4306
|
const { subjectType, subjectId } = subject;
|
|
3651
4307
|
const canOpen = api.openConversation !== void 0;
|
|
3652
|
-
|
|
4308
|
+
useEffect22(() => {
|
|
3653
4309
|
if (!isNeeded || !canOpen) return;
|
|
3654
4310
|
let isCurrent = true;
|
|
3655
4311
|
setOpened({ status: "loading" });
|
|
@@ -3667,9 +4323,9 @@ function NotFound({
|
|
|
3667
4323
|
labels,
|
|
3668
4324
|
onBack
|
|
3669
4325
|
}) {
|
|
3670
|
-
return /* @__PURE__ */
|
|
3671
|
-
/* @__PURE__ */
|
|
3672
|
-
/* @__PURE__ */
|
|
4326
|
+
return /* @__PURE__ */ jsxs31("div", { className: "cv-p cv-p-thread", children: [
|
|
4327
|
+
/* @__PURE__ */ jsx40("header", { className: "cv-p-thread__header", children: onBack ? /* @__PURE__ */ jsx40(ParticipantBackButton, { label: labels.back, onBack }) : null }),
|
|
4328
|
+
/* @__PURE__ */ jsx40("p", { className: "cv-p-empty", children: labels.notFound })
|
|
3673
4329
|
] });
|
|
3674
4330
|
}
|
|
3675
4331
|
function ParticipantThreadScreen(props) {
|
|
@@ -3679,12 +4335,12 @@ function ParticipantThreadScreen(props) {
|
|
|
3679
4335
|
);
|
|
3680
4336
|
const opened = useOpenedConversation(api, selected, listed === void 0);
|
|
3681
4337
|
const conversation = listed ?? (opened.status === "found" ? opened.conversation : void 0);
|
|
3682
|
-
if (conversation) return /* @__PURE__ */
|
|
4338
|
+
if (conversation) return /* @__PURE__ */ jsx40(LoadedThread, { ...props, conversation });
|
|
3683
4339
|
const isResolving = opened.status === "loading" || api.openConversation !== void 0 && opened.status === "idle";
|
|
3684
4340
|
const isListLoading = inbox.status === "idle" || inbox.status === "loading";
|
|
3685
4341
|
if (isResolving || api.openConversation === void 0 && isListLoading)
|
|
3686
|
-
return /* @__PURE__ */
|
|
3687
|
-
return /* @__PURE__ */
|
|
4342
|
+
return /* @__PURE__ */ jsx40("div", { className: "cv-p cv-p-thread", "aria-busy": "true" });
|
|
4343
|
+
return /* @__PURE__ */ jsx40(NotFound, { labels, onBack: props.onBack });
|
|
3688
4344
|
}
|
|
3689
4345
|
function LoadedThread(props) {
|
|
3690
4346
|
const core = useParticipantThreadController({
|
|
@@ -3692,7 +4348,7 @@ function LoadedThread(props) {
|
|
|
3692
4348
|
subject: props.selected,
|
|
3693
4349
|
onMarkedRead: props.inbox.markSubjectRead
|
|
3694
4350
|
});
|
|
3695
|
-
return /* @__PURE__ */
|
|
4351
|
+
return /* @__PURE__ */ jsx40(
|
|
3696
4352
|
ParticipantThread,
|
|
3697
4353
|
{
|
|
3698
4354
|
...core,
|
|
@@ -3705,6 +4361,7 @@ function LoadedThread(props) {
|
|
|
3705
4361
|
avatars: props.avatars,
|
|
3706
4362
|
renderAuthorAvatar: props.renderAuthorAvatar,
|
|
3707
4363
|
renderMessageActions: props.renderMessageActions,
|
|
4364
|
+
messageMenu: props.messageMenu,
|
|
3708
4365
|
tail: props.tail,
|
|
3709
4366
|
audioRecording: props.audioRecording,
|
|
3710
4367
|
renderComposerLock: props.renderComposerLock
|
|
@@ -3833,16 +4490,16 @@ function buildParticipantThemeStyle(theme) {
|
|
|
3833
4490
|
}
|
|
3834
4491
|
|
|
3835
4492
|
// src/participant/ParticipantConversationsScreen.tsx
|
|
3836
|
-
import { jsx as
|
|
4493
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
3837
4494
|
var ALL_FILTER3 = "all";
|
|
3838
4495
|
function ParticipantConversationsScreen(props) {
|
|
3839
4496
|
const { selected, inbox, subjectGroups, classNames } = props;
|
|
3840
|
-
const [filter, setFilter] =
|
|
3841
|
-
const [searchQuery, setSearchQuery] =
|
|
3842
|
-
const draftsRef =
|
|
4497
|
+
const [filter, setFilter] = useState18(ALL_FILTER3);
|
|
4498
|
+
const [searchQuery, setSearchQuery] = useState18("");
|
|
4499
|
+
const draftsRef = useRef23(/* @__PURE__ */ new Map());
|
|
3843
4500
|
const [sendStates, dispatchSendStates] = useReducer6(participantSendStatesReducer, /* @__PURE__ */ new Map());
|
|
3844
|
-
const labels =
|
|
3845
|
-
const view =
|
|
4501
|
+
const labels = useMemo7(() => ({ ...DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS, ...props.labels }), [props.labels]);
|
|
4502
|
+
const view = useMemo7(
|
|
3846
4503
|
() => groupParticipantConversations({
|
|
3847
4504
|
conversations: inbox.conversations,
|
|
3848
4505
|
subjectGroups,
|
|
@@ -3853,7 +4510,7 @@ function ParticipantConversationsScreen(props) {
|
|
|
3853
4510
|
);
|
|
3854
4511
|
const isSearchVisible = isInboxSearchVisible({ conversations: inbox.conversations, searchQuery });
|
|
3855
4512
|
const rootClassName = ["cv-p-root", props.className, classNames?.root].filter(Boolean).join(" ");
|
|
3856
|
-
return /* @__PURE__ */
|
|
4513
|
+
return /* @__PURE__ */ jsx41("div", { className: rootClassName, style: buildParticipantThemeStyle(props.theme), children: selected === void 0 ? /* @__PURE__ */ jsx41("div", { className: ["cv-p-root__screen", classNames?.inbox].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx41(
|
|
3857
4514
|
ParticipantInbox,
|
|
3858
4515
|
{
|
|
3859
4516
|
view,
|
|
@@ -3870,7 +4527,7 @@ function ParticipantConversationsScreen(props) {
|
|
|
3870
4527
|
renderSubjectIcon: props.renderSubjectIcon,
|
|
3871
4528
|
search: { value: searchQuery, onChange: setSearchQuery, isVisible: isSearchVisible }
|
|
3872
4529
|
}
|
|
3873
|
-
) }) : /* @__PURE__ */
|
|
4530
|
+
) }) : /* @__PURE__ */ jsx41("div", { className: ["cv-p-root__screen", classNames?.thread].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx41(
|
|
3874
4531
|
ParticipantThreadScreen,
|
|
3875
4532
|
{
|
|
3876
4533
|
...props,
|
|
@@ -3885,7 +4542,7 @@ function ParticipantConversationsScreen(props) {
|
|
|
3885
4542
|
}
|
|
3886
4543
|
|
|
3887
4544
|
// src/participant/useParticipantInbox.ts
|
|
3888
|
-
import { useCallback as
|
|
4545
|
+
import { useCallback as useCallback15, useEffect as useEffect23, useReducer as useReducer7 } from "react";
|
|
3889
4546
|
|
|
3890
4547
|
// src/participant/participantInboxState.ts
|
|
3891
4548
|
var INITIAL_PARTICIPANT_INBOX_STATE = { status: "idle", conversations: [] };
|
|
@@ -3936,7 +4593,7 @@ function describeError2(error) {
|
|
|
3936
4593
|
}
|
|
3937
4594
|
function useParticipantInbox(api) {
|
|
3938
4595
|
const [state, dispatch] = useReducer7(participantInboxReducer, INITIAL_PARTICIPANT_INBOX_STATE);
|
|
3939
|
-
const refresh =
|
|
4596
|
+
const refresh = useCallback15(async () => {
|
|
3940
4597
|
dispatch({ type: "started" });
|
|
3941
4598
|
try {
|
|
3942
4599
|
const page = await api.listConversations();
|
|
@@ -3946,7 +4603,7 @@ function useParticipantInbox(api) {
|
|
|
3946
4603
|
}
|
|
3947
4604
|
}, [api]);
|
|
3948
4605
|
const { nextCursor } = state;
|
|
3949
|
-
const loadMore =
|
|
4606
|
+
const loadMore = useCallback15(async () => {
|
|
3950
4607
|
if (!nextCursor) return;
|
|
3951
4608
|
try {
|
|
3952
4609
|
const page = await api.listConversations({ cursor: nextCursor });
|
|
@@ -3955,10 +4612,10 @@ function useParticipantInbox(api) {
|
|
|
3955
4612
|
dispatch({ type: "failed", error: describeError2(error) });
|
|
3956
4613
|
}
|
|
3957
4614
|
}, [api, nextCursor]);
|
|
3958
|
-
const markSubjectRead =
|
|
4615
|
+
const markSubjectRead = useCallback15((subject) => {
|
|
3959
4616
|
dispatch({ type: "markedRead", subject });
|
|
3960
4617
|
}, []);
|
|
3961
|
-
|
|
4618
|
+
useEffect23(() => {
|
|
3962
4619
|
void refresh();
|
|
3963
4620
|
}, [refresh]);
|
|
3964
4621
|
useParticipantRevalidation(api, () => {
|
|
@@ -3968,11 +4625,11 @@ function useParticipantInbox(api) {
|
|
|
3968
4625
|
}
|
|
3969
4626
|
|
|
3970
4627
|
// src/participant/ParticipantConversations.tsx
|
|
3971
|
-
import { jsx as
|
|
4628
|
+
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
3972
4629
|
function ParticipantConversations(props) {
|
|
3973
4630
|
const inbox = useParticipantInbox(props.api);
|
|
3974
4631
|
const { status, conversations, hasMore, refresh, loadMore, markSubjectRead } = inbox;
|
|
3975
|
-
return /* @__PURE__ */
|
|
4632
|
+
return /* @__PURE__ */ jsx42(
|
|
3976
4633
|
ParticipantConversationsScreen,
|
|
3977
4634
|
{
|
|
3978
4635
|
...props,
|
|
@@ -3982,7 +4639,7 @@ function ParticipantConversations(props) {
|
|
|
3982
4639
|
}
|
|
3983
4640
|
|
|
3984
4641
|
// src/participant/ConversationThread.tsx
|
|
3985
|
-
import { useCallback as
|
|
4642
|
+
import { useCallback as useCallback16, useMemo as useMemo8, useReducer as useReducer8, useRef as useRef24 } from "react";
|
|
3986
4643
|
|
|
3987
4644
|
// src/participant/conversationThreadApi.ts
|
|
3988
4645
|
function toParticipantApi(api) {
|
|
@@ -4022,20 +4679,20 @@ function buildThreadSummary({
|
|
|
4022
4679
|
}
|
|
4023
4680
|
|
|
4024
4681
|
// src/participant/ConversationThread.tsx
|
|
4025
|
-
import { jsx as
|
|
4682
|
+
import { jsx as jsx43 } from "react/jsx-runtime";
|
|
4026
4683
|
function useConversationThreadCore(props) {
|
|
4027
4684
|
const { perspective = "participant" } = props;
|
|
4028
4685
|
const { subjectType, subjectId } = props.subject;
|
|
4029
|
-
const subject =
|
|
4030
|
-
const onMarkedReadRef =
|
|
4686
|
+
const subject = useMemo8(() => ({ subjectType, subjectId }), [subjectType, subjectId]);
|
|
4687
|
+
const onMarkedReadRef = useRef24(props.onMarkedRead);
|
|
4031
4688
|
onMarkedReadRef.current = props.onMarkedRead;
|
|
4032
|
-
const handleMarkedRead =
|
|
4033
|
-
const api =
|
|
4034
|
-
const draftsRef =
|
|
4689
|
+
const handleMarkedRead = useCallback16((marked) => onMarkedReadRef.current?.(marked), []);
|
|
4690
|
+
const api = useMemo8(() => toParticipantApi(props.api), [props.api]);
|
|
4691
|
+
const draftsRef = useRef24(/* @__PURE__ */ new Map());
|
|
4035
4692
|
const [sendStates, dispatchSendStates] = useReducer8(participantSendStatesReducer, /* @__PURE__ */ new Map());
|
|
4036
|
-
const labels =
|
|
4693
|
+
const labels = useMemo8(() => ({ ...DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS, ...props.labels }), [props.labels]);
|
|
4037
4694
|
const { title, protocol, channels, status } = props;
|
|
4038
|
-
const conversation =
|
|
4695
|
+
const conversation = useMemo8(
|
|
4039
4696
|
() => buildThreadSummary({ subject, title, protocol, channels, status }),
|
|
4040
4697
|
[subject, title, protocol, channels, status]
|
|
4041
4698
|
);
|
|
@@ -4057,12 +4714,12 @@ function useConversationThreadCore(props) {
|
|
|
4057
4714
|
}
|
|
4058
4715
|
function ConversationThreadBody(props) {
|
|
4059
4716
|
const core = useConversationThreadCore(props);
|
|
4060
|
-
return /* @__PURE__ */
|
|
4717
|
+
return /* @__PURE__ */ jsx43(
|
|
4061
4718
|
"div",
|
|
4062
4719
|
{
|
|
4063
4720
|
className: ["cv-p-root", props.className].filter(Boolean).join(" "),
|
|
4064
4721
|
style: buildParticipantThemeStyle(props.theme),
|
|
4065
|
-
children: /* @__PURE__ */
|
|
4722
|
+
children: /* @__PURE__ */ jsx43("div", { className: "cv-p-root__screen", children: /* @__PURE__ */ jsx43(
|
|
4066
4723
|
ParticipantThread,
|
|
4067
4724
|
{
|
|
4068
4725
|
...core,
|
|
@@ -4073,6 +4730,7 @@ function ConversationThreadBody(props) {
|
|
|
4073
4730
|
avatars: props.avatars,
|
|
4074
4731
|
renderAuthorAvatar: props.renderAuthorAvatar,
|
|
4075
4732
|
renderMessageActions: props.renderMessageActions,
|
|
4733
|
+
messageMenu: props.messageMenu,
|
|
4076
4734
|
tail: props.tail,
|
|
4077
4735
|
counterpartLabel: props.counterpartLabel,
|
|
4078
4736
|
headerActions: props.headerActions,
|
|
@@ -4084,7 +4742,7 @@ function ConversationThreadBody(props) {
|
|
|
4084
4742
|
);
|
|
4085
4743
|
}
|
|
4086
4744
|
function ConversationThread(props) {
|
|
4087
|
-
return /* @__PURE__ */
|
|
4745
|
+
return /* @__PURE__ */ jsx43(ConversationThreadBody, { ...props }, draftKey(props.subject));
|
|
4088
4746
|
}
|
|
4089
4747
|
export {
|
|
4090
4748
|
ConversationThread,
|
|
@@ -4092,5 +4750,6 @@ export {
|
|
|
4092
4750
|
DEFAULT_PARTICIPANT_AUDIO_LABELS,
|
|
4093
4751
|
DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS,
|
|
4094
4752
|
ParticipantConversations,
|
|
4753
|
+
formatMessagesForCopy,
|
|
4095
4754
|
groupParticipantConversations
|
|
4096
4755
|
};
|