@adatechnology/conversations-ui 0.12.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 +87 -2
- package/dist/participant/index.js +1073 -398
- package/dist/styles.css +218 -0
- package/package.json +1 -1
- package/src/conversationTranscript.ts +6 -2
- package/src/participant/ConversationThread.tsx +8 -1
- package/src/participant/ParticipantAuthorAvatar.tsx +18 -0
- package/src/participant/ParticipantBubbleBody.tsx +33 -3
- package/src/participant/ParticipantConversationMenu.tsx +89 -0
- package/src/participant/ParticipantConversations.tsx +6 -1
- package/src/participant/ParticipantMenuBubble.tsx +106 -0
- package/src/participant/ParticipantMessageBubble.test.tsx +51 -0
- package/src/participant/ParticipantMessageBubble.tsx +39 -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 +30 -3
- package/src/participant/ParticipantThreadScreen.tsx +2 -0
- package/src/participant/ParticipantTimeline.tsx +7 -1
- package/src/participant/index.ts +11 -1
- 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 +41 -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,49 +2749,71 @@ 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 });
|
|
2781
|
+
}
|
|
2782
|
+
function hostActions(content, render) {
|
|
2783
|
+
if (!render || content.messageId === void 0) return null;
|
|
2784
|
+
return render({
|
|
2785
|
+
id: content.messageId,
|
|
2786
|
+
text: content.text ?? null,
|
|
2787
|
+
authorName: content.authorName ?? null,
|
|
2788
|
+
isMine: content.isMine,
|
|
2789
|
+
attachments: content.attachments,
|
|
2790
|
+
createdAt: content.createdAt
|
|
2791
|
+
});
|
|
2581
2792
|
}
|
|
2582
2793
|
function OriginalText({ content, labels }) {
|
|
2583
2794
|
const original = content.originalText?.trim();
|
|
2584
2795
|
if (!original || original === content.text?.trim()) return null;
|
|
2585
|
-
return /* @__PURE__ */
|
|
2586
|
-
/* @__PURE__ */
|
|
2587
|
-
/* @__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 })
|
|
2588
2799
|
] });
|
|
2589
2800
|
}
|
|
2590
|
-
function BubbleBody({
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2801
|
+
function BubbleBody({
|
|
2802
|
+
content,
|
|
2803
|
+
labels,
|
|
2804
|
+
ownStatus,
|
|
2805
|
+
textId,
|
|
2806
|
+
resolveAttachmentUrl,
|
|
2807
|
+
onRetry,
|
|
2808
|
+
onDiscard,
|
|
2809
|
+
onEdit,
|
|
2810
|
+
renderMessageActions
|
|
2811
|
+
}) {
|
|
2812
|
+
const actions = hostActions(content, renderMessageActions);
|
|
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(
|
|
2595
2817
|
ParticipantAttachmentItem,
|
|
2596
2818
|
{
|
|
2597
2819
|
attachment,
|
|
@@ -2600,16 +2822,319 @@ function BubbleBody({ content, labels, ownStatus, textId, resolveAttachmentUrl,
|
|
|
2600
2822
|
},
|
|
2601
2823
|
attachment.id
|
|
2602
2824
|
)),
|
|
2603
|
-
content.pendingFilenames.map((filename, index) => /* @__PURE__ */
|
|
2604
|
-
/* @__PURE__ */
|
|
2605
|
-
/* @__PURE__ */
|
|
2606
|
-
ownStatus ? /* @__PURE__ */
|
|
2607
|
-
ownStatus === "failed" ? /* @__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
|
|
2608
2831
|
] }),
|
|
2609
|
-
/* @__PURE__ */
|
|
2832
|
+
/* @__PURE__ */ jsx28(OriginalText, { content, labels })
|
|
2833
|
+
] });
|
|
2834
|
+
}
|
|
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
|
|
2610
3132
|
] });
|
|
2611
3133
|
}
|
|
2612
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
|
+
|
|
2613
3138
|
// src/participant/participantPerspective.ts
|
|
2614
3139
|
var DEFAULT_PARTICIPANT_PERSPECTIVE = "participant";
|
|
2615
3140
|
var OWN_DIRECTION = {
|
|
@@ -2671,8 +3196,74 @@ function resolveOwnMessageStatus(params) {
|
|
|
2671
3196
|
return serverStatus;
|
|
2672
3197
|
}
|
|
2673
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
|
+
|
|
2674
3265
|
// src/participant/ParticipantMessageBubble.tsx
|
|
2675
|
-
import { jsx as
|
|
3266
|
+
import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2676
3267
|
function describeItem(item, perspective) {
|
|
2677
3268
|
if (item.kind === "server") {
|
|
2678
3269
|
const { message } = item;
|
|
@@ -2684,7 +3275,8 @@ function describeItem(item, perspective) {
|
|
|
2684
3275
|
createdAt: message.createdAt,
|
|
2685
3276
|
attachments: message.attachments,
|
|
2686
3277
|
pendingFilenames: [],
|
|
2687
|
-
serverStatus: message.status
|
|
3278
|
+
serverStatus: message.status,
|
|
3279
|
+
messageId: message.id
|
|
2688
3280
|
};
|
|
2689
3281
|
}
|
|
2690
3282
|
return {
|
|
@@ -2703,23 +3295,32 @@ function bubbleClassName(isMine, hasTail, hasFailed) {
|
|
|
2703
3295
|
if (!hasTail) classes.push("cv-p-bubble--no-tail");
|
|
2704
3296
|
return classes.join(" ");
|
|
2705
3297
|
}
|
|
2706
|
-
function
|
|
2707
|
-
const {
|
|
2708
|
-
const textId = useId3();
|
|
2709
|
-
const content = describeItem(item, perspective);
|
|
3298
|
+
function renderParticipantBubble({ props, content, textId, frame }) {
|
|
3299
|
+
const { confirmsRead, onRetry, avatar, tail = true } = props;
|
|
2710
3300
|
const ownStatus = content.isMine ? resolveOwnMessageStatus({ displayState: content.displayState, serverStatus: content.serverStatus, confirmsRead }) : void 0;
|
|
2711
3301
|
const isFailed = ownStatus === "failed";
|
|
2712
3302
|
const canRetry = isFailed && onRetry !== void 0;
|
|
2713
|
-
const
|
|
2714
|
-
|
|
2715
|
-
/* @__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
|
|
2716
3308
|
] });
|
|
2717
3309
|
if (avatar === void 0 || content.isMine) return bubble;
|
|
2718
|
-
return /* @__PURE__ */
|
|
2719
|
-
/* @__PURE__ */
|
|
3310
|
+
return /* @__PURE__ */ jsxs26("div", { className: "cv-p-bubble-row", children: [
|
|
3311
|
+
/* @__PURE__ */ jsx33("span", { className: "cv-p-bubble-row__avatar", children: avatar }),
|
|
2720
3312
|
bubble
|
|
2721
3313
|
] });
|
|
2722
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
|
+
}
|
|
2723
3324
|
|
|
2724
3325
|
// src/participant/participantBubbleActions.ts
|
|
2725
3326
|
function bindPendingActions(actions, clientMessageId) {
|
|
@@ -2738,7 +3339,7 @@ function resolveParticipantBubbleActions(item, handlers) {
|
|
|
2738
3339
|
}
|
|
2739
3340
|
|
|
2740
3341
|
// src/participant/ParticipantTimeline.tsx
|
|
2741
|
-
import { jsx as
|
|
3342
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
2742
3343
|
function createdAtOf2(item) {
|
|
2743
3344
|
return item.kind === "server" ? item.message.createdAt : item.pending.createdAt;
|
|
2744
3345
|
}
|
|
@@ -2755,7 +3356,7 @@ function resolveActions(item, pendingActions) {
|
|
|
2755
3356
|
}
|
|
2756
3357
|
function resolveAvatarSlot({ isEnabled, isMine, showAvatar, author, render }) {
|
|
2757
3358
|
if (!isEnabled || isMine) return void 0;
|
|
2758
|
-
return showAvatar ? /* @__PURE__ */
|
|
3359
|
+
return showAvatar ? /* @__PURE__ */ jsx34(ParticipantAuthorAvatar, { name: author, render }) : null;
|
|
2759
3360
|
}
|
|
2760
3361
|
function ParticipantTimeline(props) {
|
|
2761
3362
|
const { items, labels, locale, resolveAttachmentUrl, pendingActions } = props;
|
|
@@ -2767,7 +3368,7 @@ function ParticipantTimeline(props) {
|
|
|
2767
3368
|
for (const item of items) {
|
|
2768
3369
|
const day = new Date(createdAtOf2(item));
|
|
2769
3370
|
if (previousDay === void 0 || !isSameDay(previousDay, day)) {
|
|
2770
|
-
nodes.push(/* @__PURE__ */
|
|
3371
|
+
nodes.push(/* @__PURE__ */ jsx34(ParticipantDayDivider, { day, locale }, `day:${keyOf(item)}`));
|
|
2771
3372
|
previousAuthor = void 0;
|
|
2772
3373
|
}
|
|
2773
3374
|
previousDay = day;
|
|
@@ -2782,7 +3383,7 @@ function ParticipantTimeline(props) {
|
|
|
2782
3383
|
render: props.renderAuthorAvatar
|
|
2783
3384
|
});
|
|
2784
3385
|
nodes.push(
|
|
2785
|
-
/* @__PURE__ */
|
|
3386
|
+
/* @__PURE__ */ jsx34(
|
|
2786
3387
|
ParticipantMessageBubble,
|
|
2787
3388
|
{
|
|
2788
3389
|
item,
|
|
@@ -2792,25 +3393,77 @@ function ParticipantTimeline(props) {
|
|
|
2792
3393
|
tail: props.tail,
|
|
2793
3394
|
perspective: props.perspective,
|
|
2794
3395
|
avatar,
|
|
3396
|
+
renderMessageActions: props.renderMessageActions,
|
|
2795
3397
|
...resolveActions(item, pendingActions)
|
|
2796
3398
|
},
|
|
2797
3399
|
keyOf(item)
|
|
2798
3400
|
)
|
|
2799
3401
|
);
|
|
2800
3402
|
}
|
|
2801
|
-
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
|
+
] });
|
|
2802
3455
|
}
|
|
2803
3456
|
|
|
2804
3457
|
// src/participant/ParticipantConversationAvatar.tsx
|
|
2805
|
-
import { jsx as
|
|
3458
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2806
3459
|
function ParticipantConversationAvatar({ icon }) {
|
|
2807
|
-
return /* @__PURE__ */
|
|
3460
|
+
return /* @__PURE__ */ jsx36("span", { className: "cv-p-thread__avatar", "aria-hidden": "true", children: icon });
|
|
2808
3461
|
}
|
|
2809
3462
|
|
|
2810
3463
|
// src/participant/ParticipantProtocolBadge.tsx
|
|
2811
3464
|
import { Check as Check2, Copy as Copy2 } from "lucide-react";
|
|
2812
|
-
import { useEffect as
|
|
2813
|
-
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";
|
|
2814
3467
|
var COPIED_ANNOUNCEMENT_MS = 3e3;
|
|
2815
3468
|
function ParticipantProtocolBadgeView({
|
|
2816
3469
|
protocol,
|
|
@@ -2819,16 +3472,16 @@ function ParticipantProtocolBadgeView({
|
|
|
2819
3472
|
onCopy
|
|
2820
3473
|
}) {
|
|
2821
3474
|
const { isCopied, copyCount } = copyState;
|
|
2822
|
-
return /* @__PURE__ */
|
|
2823
|
-
/* @__PURE__ */
|
|
2824
|
-
/* @__PURE__ */
|
|
2825
|
-
/* @__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: [
|
|
2826
3479
|
labels.protocolPrefix,
|
|
2827
3480
|
" "
|
|
2828
3481
|
] }),
|
|
2829
3482
|
protocol
|
|
2830
3483
|
] }),
|
|
2831
|
-
/* @__PURE__ */
|
|
3484
|
+
/* @__PURE__ */ jsxs28(
|
|
2832
3485
|
"button",
|
|
2833
3486
|
{
|
|
2834
3487
|
type: "button",
|
|
@@ -2837,19 +3490,19 @@ function ParticipantProtocolBadgeView({
|
|
|
2837
3490
|
"data-cv-tooltip": labels.copyProtocol,
|
|
2838
3491
|
onClick: onCopy,
|
|
2839
3492
|
children: [
|
|
2840
|
-
isCopied ? /* @__PURE__ */
|
|
2841
|
-
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
|
|
2842
3495
|
]
|
|
2843
3496
|
}
|
|
2844
3497
|
)
|
|
2845
3498
|
] }),
|
|
2846
|
-
/* @__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 })
|
|
2847
3500
|
] });
|
|
2848
3501
|
}
|
|
2849
3502
|
function ParticipantProtocolBadge({ protocol, labels }) {
|
|
2850
3503
|
const [copyState, dispatch] = useReducer4(protocolCopyReducer, INITIAL_PROTOCOL_COPY_STATE);
|
|
2851
3504
|
const { isCopied, copyCount } = copyState;
|
|
2852
|
-
|
|
3505
|
+
useEffect16(() => {
|
|
2853
3506
|
if (!isCopied) return void 0;
|
|
2854
3507
|
const timer = setTimeout(() => dispatch({ type: "expired" }), COPIED_ANNOUNCEMENT_MS);
|
|
2855
3508
|
return () => clearTimeout(timer);
|
|
@@ -2858,7 +3511,7 @@ function ParticipantProtocolBadge({ protocol, labels }) {
|
|
|
2858
3511
|
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
2859
3512
|
if (await copyProtocolToClipboard(protocol, clipboard)) dispatch({ type: "copied" });
|
|
2860
3513
|
}
|
|
2861
|
-
return /* @__PURE__ */
|
|
3514
|
+
return /* @__PURE__ */ jsx37(
|
|
2862
3515
|
ParticipantProtocolBadgeView,
|
|
2863
3516
|
{
|
|
2864
3517
|
protocol,
|
|
@@ -2870,19 +3523,19 @@ function ParticipantProtocolBadge({ protocol, labels }) {
|
|
|
2870
3523
|
}
|
|
2871
3524
|
|
|
2872
3525
|
// src/participant/ParticipantThreadHeader.tsx
|
|
2873
|
-
import { jsx as
|
|
3526
|
+
import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2874
3527
|
function Title({ conversation, labels, onOpenSubject }) {
|
|
2875
3528
|
const { subjectType, subjectId, subjectLabel } = conversation;
|
|
2876
|
-
return /* @__PURE__ */
|
|
3529
|
+
return /* @__PURE__ */ jsx38("h2", { className: "cv-p-thread__title", children: onOpenSubject ? /* @__PURE__ */ jsx38(
|
|
2877
3530
|
"button",
|
|
2878
3531
|
{
|
|
2879
3532
|
type: "button",
|
|
2880
3533
|
className: "cv-p-thread__title-button",
|
|
2881
3534
|
"aria-label": `${labels.openSubject}: ${subjectLabel}`,
|
|
2882
3535
|
onClick: () => onOpenSubject({ subjectType, subjectId }),
|
|
2883
|
-
children: /* @__PURE__ */
|
|
3536
|
+
children: /* @__PURE__ */ jsx38("span", { className: "cv-p-thread__title-text", children: subjectLabel })
|
|
2884
3537
|
}
|
|
2885
|
-
) : /* @__PURE__ */
|
|
3538
|
+
) : /* @__PURE__ */ jsx38("span", { className: "cv-p-thread__title-text", children: subjectLabel }) });
|
|
2886
3539
|
}
|
|
2887
3540
|
function ParticipantThreadHeader({
|
|
2888
3541
|
conversation,
|
|
@@ -2897,34 +3550,36 @@ function ParticipantThreadHeader({
|
|
|
2897
3550
|
const group = subjectGroups?.find((candidate) => candidate.subjectType === conversation.subjectType);
|
|
2898
3551
|
const icon = resolveConversationIcon({ conversation, group, renderSubjectIcon });
|
|
2899
3552
|
const hasChannels = resolveParticipantChannels(conversation.channels).length > 0;
|
|
2900
|
-
return /* @__PURE__ */
|
|
2901
|
-
onBack ? /* @__PURE__ */
|
|
2902
|
-
icon !== void 0 ? /* @__PURE__ */
|
|
2903
|
-
/* @__PURE__ */
|
|
2904
|
-
icon === void 0 && group ? /* @__PURE__ */
|
|
2905
|
-
/* @__PURE__ */
|
|
2906
|
-
counterpartLabel ? /* @__PURE__ */
|
|
2907
|
-
conversation.protocol || hasChannels ? /* @__PURE__ */
|
|
2908
|
-
conversation.protocol ? /* @__PURE__ */
|
|
2909
|
-
/* @__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 })
|
|
2910
3563
|
] }) : null
|
|
2911
3564
|
] }),
|
|
2912
|
-
actions ? /* @__PURE__ */
|
|
3565
|
+
actions ? /* @__PURE__ */ jsx38("div", { className: "cv-p-thread__actions", children: actions }) : null
|
|
2913
3566
|
] });
|
|
2914
3567
|
}
|
|
2915
3568
|
|
|
2916
3569
|
// src/participant/ParticipantThread.tsx
|
|
2917
|
-
import { jsx as
|
|
3570
|
+
import { Fragment as Fragment8, jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2918
3571
|
function Footer({ props }) {
|
|
2919
3572
|
const { conversation, labels, draft } = props;
|
|
3573
|
+
const menu = useParticipantMessageMenu();
|
|
3574
|
+
if (menu?.isSelecting) return /* @__PURE__ */ jsx39(ParticipantSelectionBar, { state: menu });
|
|
2920
3575
|
if (conversation.status === "closed") {
|
|
2921
|
-
return /* @__PURE__ */
|
|
3576
|
+
return /* @__PURE__ */ jsx39("p", { className: "cv-p-thread__closed", children: labels.closedNotice });
|
|
2922
3577
|
}
|
|
2923
3578
|
const lock = props.renderComposerLock?.(conversation);
|
|
2924
3579
|
if (lock !== void 0 && lock !== null) {
|
|
2925
|
-
return /* @__PURE__ */
|
|
3580
|
+
return /* @__PURE__ */ jsx39("div", { className: "cv-p-thread__lock", role: "region", "aria-live": "polite", children: lock });
|
|
2926
3581
|
}
|
|
2927
|
-
return /* @__PURE__ */
|
|
3582
|
+
return /* @__PURE__ */ jsx39(
|
|
2928
3583
|
ParticipantComposer,
|
|
2929
3584
|
{
|
|
2930
3585
|
value: draft.value,
|
|
@@ -2944,7 +3599,12 @@ function Footer({ props }) {
|
|
|
2944
3599
|
);
|
|
2945
3600
|
}
|
|
2946
3601
|
function Header({ props }) {
|
|
2947
|
-
|
|
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(
|
|
2948
3608
|
ParticipantThreadHeader,
|
|
2949
3609
|
{
|
|
2950
3610
|
conversation: props.conversation,
|
|
@@ -2954,7 +3614,7 @@ function Header({ props }) {
|
|
|
2954
3614
|
onBack: props.onBack,
|
|
2955
3615
|
onOpenSubject: props.onOpenSubject,
|
|
2956
3616
|
counterpartLabel: props.counterpartLabel,
|
|
2957
|
-
actions
|
|
3617
|
+
actions
|
|
2958
3618
|
}
|
|
2959
3619
|
);
|
|
2960
3620
|
}
|
|
@@ -2962,17 +3622,17 @@ function ThreadBody(props) {
|
|
|
2962
3622
|
const { conversation, labels, hasMore, onLoadOlder, renderSubjectCard } = props;
|
|
2963
3623
|
const subjectCard = renderSubjectCard?.(conversation);
|
|
2964
3624
|
const loadView = resolveLoadView({ status: props.status, hasItems: props.items.length > 0 });
|
|
2965
|
-
const rootRef =
|
|
3625
|
+
const rootRef = useRef15(null);
|
|
2966
3626
|
useSuppressTouchTooltips(rootRef);
|
|
2967
|
-
return /* @__PURE__ */
|
|
2968
|
-
/* @__PURE__ */
|
|
2969
|
-
subjectCard ? /* @__PURE__ */
|
|
2970
|
-
/* @__PURE__ */
|
|
2971
|
-
loadView.isLoading ? /* @__PURE__ */
|
|
2972
|
-
loadView.hasError ? /* @__PURE__ */
|
|
2973
|
-
hasMore && onLoadOlder ? /* @__PURE__ */
|
|
2974
|
-
/* @__PURE__ */
|
|
2975
|
-
/* @__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(
|
|
2976
3636
|
ParticipantScrollToLatest,
|
|
2977
3637
|
{
|
|
2978
3638
|
label: labels.scrollToLatest,
|
|
@@ -2983,19 +3643,29 @@ function ThreadBody(props) {
|
|
|
2983
3643
|
}
|
|
2984
3644
|
)
|
|
2985
3645
|
] }),
|
|
2986
|
-
/* @__PURE__ */
|
|
2987
|
-
/* @__PURE__ */
|
|
3646
|
+
/* @__PURE__ */ jsx39(ParticipantThreadLive, { notice: props.newMessagesCount > 0 ? labels.newMessages : "" }),
|
|
3647
|
+
/* @__PURE__ */ jsx39(Footer, { props })
|
|
2988
3648
|
] });
|
|
2989
3649
|
}
|
|
2990
3650
|
function ParticipantThread(props) {
|
|
2991
|
-
return /* @__PURE__ */
|
|
2992
|
-
/* @__PURE__ */
|
|
2993
|
-
|
|
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, {})
|
|
2994
3664
|
] });
|
|
2995
3665
|
}
|
|
2996
3666
|
|
|
2997
3667
|
// src/participant/useParticipantThreadController.ts
|
|
2998
|
-
import { useMemo as
|
|
3668
|
+
import { useMemo as useMemo6 } from "react";
|
|
2999
3669
|
|
|
3000
3670
|
// src/participant/bindAttachmentUrlResolver.ts
|
|
3001
3671
|
function bindAttachmentUrlResolver(api) {
|
|
@@ -3079,7 +3749,7 @@ function appendEntryToDraft(entry, current) {
|
|
|
3079
3749
|
}
|
|
3080
3750
|
|
|
3081
3751
|
// src/participant/useParticipantConversationView.ts
|
|
3082
|
-
import { useCallback as
|
|
3752
|
+
import { useCallback as useCallback11, useMemo as useMemo5 } from "react";
|
|
3083
3753
|
|
|
3084
3754
|
// src/participant/participantDrafts.ts
|
|
3085
3755
|
var EMPTY_PARTICIPANT_DRAFT = { text: "", files: [] };
|
|
@@ -3123,7 +3793,7 @@ function participantSendStatesReducer(states, { subject, action }) {
|
|
|
3123
3793
|
}
|
|
3124
3794
|
|
|
3125
3795
|
// src/participant/useParticipantThread.ts
|
|
3126
|
-
import { useCallback as
|
|
3796
|
+
import { useCallback as useCallback10, useEffect as useEffect20, useMemo as useMemo4, useReducer as useReducer5, useRef as useRef19 } from "react";
|
|
3127
3797
|
|
|
3128
3798
|
// src/participant/participantThreadState.ts
|
|
3129
3799
|
var INITIAL_PARTICIPANT_THREAD_STATE = {
|
|
@@ -3159,7 +3829,7 @@ function participantThreadReducer(state, action) {
|
|
|
3159
3829
|
}
|
|
3160
3830
|
|
|
3161
3831
|
// src/participant/useParticipantMarkRead.ts
|
|
3162
|
-
import { useEffect as
|
|
3832
|
+
import { useEffect as useEffect18 } from "react";
|
|
3163
3833
|
|
|
3164
3834
|
// src/participant/shouldMarkRead.ts
|
|
3165
3835
|
function shouldMarkParticipantRead(params) {
|
|
@@ -3171,12 +3841,12 @@ function shouldMarkParticipantRead(params) {
|
|
|
3171
3841
|
}
|
|
3172
3842
|
|
|
3173
3843
|
// src/participant/useDocumentVisibility.ts
|
|
3174
|
-
import { useEffect as
|
|
3844
|
+
import { useEffect as useEffect17, useState as useState13 } from "react";
|
|
3175
3845
|
function useDocumentVisibility() {
|
|
3176
|
-
const [visibilityState, setVisibilityState] =
|
|
3846
|
+
const [visibilityState, setVisibilityState] = useState13(
|
|
3177
3847
|
() => typeof document === "undefined" ? "visible" : document.visibilityState
|
|
3178
3848
|
);
|
|
3179
|
-
|
|
3849
|
+
useEffect17(() => {
|
|
3180
3850
|
function handleVisibilityChange() {
|
|
3181
3851
|
setVisibilityState(document.visibilityState);
|
|
3182
3852
|
}
|
|
@@ -3191,18 +3861,18 @@ function useDocumentVisibility() {
|
|
|
3191
3861
|
function useParticipantMarkRead(params) {
|
|
3192
3862
|
const { apiRef, subject, unreadCount, messageCount, onMarkedRead } = params;
|
|
3193
3863
|
const visibilityState = useDocumentVisibility();
|
|
3194
|
-
|
|
3864
|
+
useEffect18(() => {
|
|
3195
3865
|
if (!shouldMarkParticipantRead({ selected: subject, subject, visibilityState, unreadCount })) return;
|
|
3196
3866
|
void apiRef.current.markRead(subject).then(() => onMarkedRead?.(subject), () => void 0);
|
|
3197
3867
|
}, [apiRef, subject, visibilityState, unreadCount, messageCount, onMarkedRead]);
|
|
3198
3868
|
}
|
|
3199
3869
|
|
|
3200
3870
|
// src/participant/useParticipantRevalidation.ts
|
|
3201
|
-
import { useEffect as
|
|
3871
|
+
import { useEffect as useEffect19, useRef as useRef16 } from "react";
|
|
3202
3872
|
function useParticipantRevalidation(api, onRevalidate) {
|
|
3203
|
-
const handlerRef =
|
|
3873
|
+
const handlerRef = useRef16(onRevalidate);
|
|
3204
3874
|
handlerRef.current = onRevalidate;
|
|
3205
|
-
|
|
3875
|
+
useEffect19(() => {
|
|
3206
3876
|
function handleFocus() {
|
|
3207
3877
|
handlerRef.current();
|
|
3208
3878
|
}
|
|
@@ -3229,12 +3899,12 @@ function useParticipantRevalidation(api, onRevalidate) {
|
|
|
3229
3899
|
}
|
|
3230
3900
|
|
|
3231
3901
|
// src/participant/useParticipantSend.ts
|
|
3232
|
-
import { useCallback as
|
|
3902
|
+
import { useCallback as useCallback8, useRef as useRef17 } from "react";
|
|
3233
3903
|
function useParticipantSend(params) {
|
|
3234
3904
|
const { apiRef, subject, dispatchSend, onSentMessage } = params;
|
|
3235
|
-
const entriesRef =
|
|
3905
|
+
const entriesRef = useRef17(params.entries);
|
|
3236
3906
|
entriesRef.current = params.entries;
|
|
3237
|
-
const deliver =
|
|
3907
|
+
const deliver = useCallback8(
|
|
3238
3908
|
async (entry) => {
|
|
3239
3909
|
const { clientMessageId } = entry;
|
|
3240
3910
|
try {
|
|
@@ -3258,7 +3928,7 @@ function useParticipantSend(params) {
|
|
|
3258
3928
|
},
|
|
3259
3929
|
[apiRef, dispatchSend, onSentMessage]
|
|
3260
3930
|
);
|
|
3261
|
-
const send =
|
|
3931
|
+
const send = useCallback8(
|
|
3262
3932
|
async (content) => {
|
|
3263
3933
|
const entry = createParticipantSendEntry({
|
|
3264
3934
|
clientMessageId: crypto.randomUUID(),
|
|
@@ -3271,7 +3941,7 @@ function useParticipantSend(params) {
|
|
|
3271
3941
|
},
|
|
3272
3942
|
[deliver, dispatchSend, subject]
|
|
3273
3943
|
);
|
|
3274
|
-
const retry =
|
|
3944
|
+
const retry = useCallback8(
|
|
3275
3945
|
async (clientMessageId) => {
|
|
3276
3946
|
const entry = findFailedEntry(entriesRef.current, clientMessageId);
|
|
3277
3947
|
if (!entry) return;
|
|
@@ -3284,13 +3954,13 @@ function useParticipantSend(params) {
|
|
|
3284
3954
|
}
|
|
3285
3955
|
|
|
3286
3956
|
// src/participant/useParticipantUnread.ts
|
|
3287
|
-
import { useCallback as
|
|
3957
|
+
import { useCallback as useCallback9, useRef as useRef18, useState as useState14 } from "react";
|
|
3288
3958
|
function useParticipantUnread(params) {
|
|
3289
3959
|
const { messages, perspective, reportedUnreadCount, onMarkedRead } = params;
|
|
3290
|
-
const [acknowledgedCount, setAcknowledgedCount] =
|
|
3291
|
-
const messagesRef =
|
|
3960
|
+
const [acknowledgedCount, setAcknowledgedCount] = useState14(0);
|
|
3961
|
+
const messagesRef = useRef18(messages);
|
|
3292
3962
|
messagesRef.current = messages;
|
|
3293
|
-
const handleMarkedRead =
|
|
3963
|
+
const handleMarkedRead = useCallback9(
|
|
3294
3964
|
(subject) => {
|
|
3295
3965
|
if (perspective) setAcknowledgedCount(countOtherSideMessages(messagesRef.current, perspective));
|
|
3296
3966
|
onMarkedRead?.(subject);
|
|
@@ -3313,13 +3983,13 @@ function isForSubject(event, subject) {
|
|
|
3313
3983
|
function useParticipantThread(params) {
|
|
3314
3984
|
const { api, subject, channel, unreadCount = 0, onMarkedRead, sendEntries, dispatchSend, hostPending = [], perspective } = params;
|
|
3315
3985
|
const { subjectType, subjectId } = subject;
|
|
3316
|
-
const stableSubject =
|
|
3986
|
+
const stableSubject = useMemo4(() => ({ subjectType, subjectId }), [subjectType, subjectId]);
|
|
3317
3987
|
const [state, dispatch] = useReducer5(participantThreadReducer, INITIAL_PARTICIPANT_THREAD_STATE);
|
|
3318
|
-
const apiRef =
|
|
3988
|
+
const apiRef = useRef19(api);
|
|
3319
3989
|
apiRef.current = api;
|
|
3320
|
-
const activeSubjectKey =
|
|
3990
|
+
const activeSubjectKey = useRef19("");
|
|
3321
3991
|
const confirmsRead = channelCapabilityFor(channel).confirmsRead;
|
|
3322
|
-
const refresh =
|
|
3992
|
+
const refresh = useCallback10(async () => {
|
|
3323
3993
|
const requestKey = `${subjectType}:${subjectId}`;
|
|
3324
3994
|
dispatch({ type: "started" });
|
|
3325
3995
|
try {
|
|
@@ -3332,7 +4002,7 @@ function useParticipantThread(params) {
|
|
|
3332
4002
|
}
|
|
3333
4003
|
}, [stableSubject, subjectType, subjectId]);
|
|
3334
4004
|
const oldestMessageId = state.messages[0]?.id;
|
|
3335
|
-
const loadOlder =
|
|
4005
|
+
const loadOlder = useCallback10(async () => {
|
|
3336
4006
|
if (!oldestMessageId) return;
|
|
3337
4007
|
const requestKey = `${subjectType}:${subjectId}`;
|
|
3338
4008
|
try {
|
|
@@ -3344,9 +4014,9 @@ function useParticipantThread(params) {
|
|
|
3344
4014
|
if (activeSubjectKey.current === requestKey) dispatch({ type: "failed", error: describeError(error) });
|
|
3345
4015
|
}
|
|
3346
4016
|
}, [stableSubject, subjectType, subjectId, oldestMessageId]);
|
|
3347
|
-
const handleSentMessage =
|
|
4017
|
+
const handleSentMessage = useCallback10((message) => dispatch({ type: "sendConfirmed", message }), []);
|
|
3348
4018
|
const sending = useParticipantSend({ apiRef, subject: stableSubject, entries: sendEntries, dispatchSend, onSentMessage: handleSentMessage });
|
|
3349
|
-
|
|
4019
|
+
useEffect20(() => {
|
|
3350
4020
|
activeSubjectKey.current = `${subjectType}:${subjectId}`;
|
|
3351
4021
|
dispatch({ type: "reset" });
|
|
3352
4022
|
void refresh();
|
|
@@ -3354,7 +4024,7 @@ function useParticipantThread(params) {
|
|
|
3354
4024
|
useParticipantRevalidation(api, (event) => {
|
|
3355
4025
|
if (isForSubject(event, stableSubject)) void refresh();
|
|
3356
4026
|
});
|
|
3357
|
-
|
|
4027
|
+
useEffect20(() => {
|
|
3358
4028
|
if (sendEntries.length === 0) return;
|
|
3359
4029
|
dispatchSend({ type: "reflected", knownClientMessageIds: collectKnownClientMessageIds({ messages: state.messages, hostPending }) });
|
|
3360
4030
|
}, [sendEntries, state.messages, hostPending, dispatchSend]);
|
|
@@ -3366,7 +4036,7 @@ function useParticipantThread(params) {
|
|
|
3366
4036
|
messageCount: state.messages.length,
|
|
3367
4037
|
onMarkedRead: unread.handleMarkedRead
|
|
3368
4038
|
});
|
|
3369
|
-
const localPending =
|
|
4039
|
+
const localPending = useMemo4(() => toLocalPending(sendEntries), [sendEntries]);
|
|
3370
4040
|
return { ...state, localPending, confirmsRead, refresh, loadOlder, ...sending };
|
|
3371
4041
|
}
|
|
3372
4042
|
|
|
@@ -3375,7 +4045,7 @@ function useParticipantConversationView(params) {
|
|
|
3375
4045
|
const { api, subject, conversation, pendingMessages, sendStates, dispatchSendStates } = params;
|
|
3376
4046
|
const { subjectType, subjectId } = subject;
|
|
3377
4047
|
const sendEntries = selectSendEntries(sendStates, subject);
|
|
3378
|
-
const dispatchSend =
|
|
4048
|
+
const dispatchSend = useCallback11(
|
|
3379
4049
|
(action) => dispatchSendStates({ subject: { subjectType, subjectId }, action }),
|
|
3380
4050
|
[dispatchSendStates, subjectType, subjectId]
|
|
3381
4051
|
);
|
|
@@ -3390,7 +4060,7 @@ function useParticipantConversationView(params) {
|
|
|
3390
4060
|
hostPending: pendingMessages,
|
|
3391
4061
|
perspective: params.perspective
|
|
3392
4062
|
});
|
|
3393
|
-
const items =
|
|
4063
|
+
const items = useMemo5(
|
|
3394
4064
|
() => mergeParticipantMessages({
|
|
3395
4065
|
serverMessages: thread.messages,
|
|
3396
4066
|
hostPending: pendingMessages ?? [],
|
|
@@ -3403,9 +4073,9 @@ function useParticipantConversationView(params) {
|
|
|
3403
4073
|
}
|
|
3404
4074
|
|
|
3405
4075
|
// src/participant/useParticipantDraft.ts
|
|
3406
|
-
import { useState as
|
|
4076
|
+
import { useState as useState15 } from "react";
|
|
3407
4077
|
function useParticipantDraft(draftsRef, subject) {
|
|
3408
|
-
const [draft, setDraftState] =
|
|
4078
|
+
const [draft, setDraftState] = useState15(() => getDraft(draftsRef.current, subject));
|
|
3409
4079
|
function replaceDrafts(next) {
|
|
3410
4080
|
draftsRef.current = next;
|
|
3411
4081
|
setDraftState(getDraft(next, subject));
|
|
@@ -3425,12 +4095,12 @@ function useParticipantDraft(draftsRef, subject) {
|
|
|
3425
4095
|
}
|
|
3426
4096
|
|
|
3427
4097
|
// src/participant/useParticipantSendAudio.ts
|
|
3428
|
-
import { useCallback as
|
|
4098
|
+
import { useCallback as useCallback12, useRef as useRef20 } from "react";
|
|
3429
4099
|
function useParticipantSendAudio(send) {
|
|
3430
|
-
const sendRef =
|
|
4100
|
+
const sendRef = useRef20(send);
|
|
3431
4101
|
sendRef.current = send;
|
|
3432
|
-
const inFlightRef =
|
|
3433
|
-
return
|
|
4102
|
+
const inFlightRef = useRef20(/* @__PURE__ */ new Set());
|
|
4103
|
+
return useCallback12((file) => {
|
|
3434
4104
|
const inFlight = inFlightRef.current;
|
|
3435
4105
|
if (inFlight.has(file)) return;
|
|
3436
4106
|
inFlight.add(file);
|
|
@@ -3446,7 +4116,7 @@ function useParticipantSendAudio(send) {
|
|
|
3446
4116
|
}
|
|
3447
4117
|
|
|
3448
4118
|
// src/participant/useParticipantThreadScroll.ts
|
|
3449
|
-
import { useEffect as
|
|
4119
|
+
import { useEffect as useEffect21, useRef as useRef22 } from "react";
|
|
3450
4120
|
|
|
3451
4121
|
// src/participant/participantNewMessages.ts
|
|
3452
4122
|
function collectServerMessageIds(items) {
|
|
@@ -3471,9 +4141,9 @@ function countNewIncomingMessages(previousIds, currentItems, perspective) {
|
|
|
3471
4141
|
}
|
|
3472
4142
|
|
|
3473
4143
|
// src/participant/useScrollAwayState.ts
|
|
3474
|
-
import { useCallback as
|
|
4144
|
+
import { useCallback as useCallback13 } from "react";
|
|
3475
4145
|
function useScrollAwayState(ref, isNearBottom) {
|
|
3476
|
-
const scrollToLatest =
|
|
4146
|
+
const scrollToLatest = useCallback13(() => {
|
|
3477
4147
|
const element = ref.current;
|
|
3478
4148
|
if (!element) return;
|
|
3479
4149
|
element.scrollTo(
|
|
@@ -3484,7 +4154,7 @@ function useScrollAwayState(ref, isNearBottom) {
|
|
|
3484
4154
|
}
|
|
3485
4155
|
|
|
3486
4156
|
// src/participant/useStickToBottom.ts
|
|
3487
|
-
import { useCallback as
|
|
4157
|
+
import { useCallback as useCallback14, useLayoutEffect as useLayoutEffect4, useRef as useRef21, useState as useState16 } from "react";
|
|
3488
4158
|
|
|
3489
4159
|
// src/participant/participantScroll.ts
|
|
3490
4160
|
var NEAR_BOTTOM_THRESHOLD_PX = 80;
|
|
@@ -3506,17 +4176,17 @@ function resolveScrollAction(params) {
|
|
|
3506
4176
|
|
|
3507
4177
|
// src/participant/useStickToBottom.ts
|
|
3508
4178
|
function useStickToBottom({ firstKey, lastKey }) {
|
|
3509
|
-
const ref =
|
|
3510
|
-
const snapshot =
|
|
4179
|
+
const ref = useRef21(null);
|
|
4180
|
+
const snapshot = useRef21({
|
|
3511
4181
|
hasPositioned: false,
|
|
3512
4182
|
firstKey: void 0,
|
|
3513
4183
|
lastKey: void 0,
|
|
3514
4184
|
scrollTop: 0,
|
|
3515
4185
|
scrollHeight: 0
|
|
3516
4186
|
});
|
|
3517
|
-
const [isNearBottom, setIsNearBottom] =
|
|
3518
|
-
const isNearBottomRef =
|
|
3519
|
-
const onScroll =
|
|
4187
|
+
const [isNearBottom, setIsNearBottom] = useState16(true);
|
|
4188
|
+
const isNearBottomRef = useRef21(true);
|
|
4189
|
+
const onScroll = useCallback14((event) => {
|
|
3520
4190
|
const { scrollTop, clientHeight, scrollHeight } = event.currentTarget;
|
|
3521
4191
|
const isNear = shouldStickToBottom({ scrollTop, clientHeight, scrollHeight, threshold: NEAR_BOTTOM_THRESHOLD_PX });
|
|
3522
4192
|
snapshot.current.scrollTop = scrollTop;
|
|
@@ -3524,7 +4194,7 @@ function useStickToBottom({ firstKey, lastKey }) {
|
|
|
3524
4194
|
isNearBottomRef.current = isNear;
|
|
3525
4195
|
setIsNearBottom(isNear);
|
|
3526
4196
|
}, []);
|
|
3527
|
-
|
|
4197
|
+
useLayoutEffect4(() => {
|
|
3528
4198
|
const element = ref.current;
|
|
3529
4199
|
const previous = snapshot.current;
|
|
3530
4200
|
if (!element) return;
|
|
@@ -3571,8 +4241,8 @@ function useParticipantThreadScroll(items, perspective) {
|
|
|
3571
4241
|
});
|
|
3572
4242
|
const { isAwayFromBottom, scrollToLatest } = useScrollAwayState(base.ref, isNearBottom);
|
|
3573
4243
|
const scroll = { ...base, isAwayFromBottom, scrollToLatest };
|
|
3574
|
-
const seenIds =
|
|
3575
|
-
|
|
4244
|
+
const seenIds = useRef22(/* @__PURE__ */ new Set());
|
|
4245
|
+
useEffect21(() => {
|
|
3576
4246
|
if (isNearBottom) seenIds.current = collectServerMessageIds(items);
|
|
3577
4247
|
}, [isNearBottom, items]);
|
|
3578
4248
|
return { scroll, newMessagesCount: isNearBottom ? 0 : countNewIncomingMessages(seenIds.current, items, perspective) };
|
|
@@ -3599,7 +4269,7 @@ function useParticipantThreadController(params) {
|
|
|
3599
4269
|
const { api, subject, conversation, draftsRef } = params;
|
|
3600
4270
|
const { draft, setText, setFiles, takeForSend, restoreFromEntry } = useParticipantDraft(draftsRef, subject);
|
|
3601
4271
|
const { thread, items, sendEntries, dispatchSend } = useParticipantConversationView(params);
|
|
3602
|
-
const resolveAttachmentUrl =
|
|
4272
|
+
const resolveAttachmentUrl = useMemo6(() => bindAttachmentUrlResolver(api), [api]);
|
|
3603
4273
|
const { scroll, newMessagesCount } = useParticipantThreadScroll(items, params.perspective);
|
|
3604
4274
|
const handleSendAudio = useParticipantSendAudio(thread.send);
|
|
3605
4275
|
const handleEdit = buildEditHandler({ sendEntries, dispatchSend, restoreFromEntry });
|
|
@@ -3630,12 +4300,12 @@ function useParticipantThreadController(params) {
|
|
|
3630
4300
|
}
|
|
3631
4301
|
|
|
3632
4302
|
// src/participant/ParticipantThreadScreen.tsx
|
|
3633
|
-
import { jsx as
|
|
4303
|
+
import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3634
4304
|
function useOpenedConversation(api, subject, isNeeded) {
|
|
3635
|
-
const [opened, setOpened] =
|
|
4305
|
+
const [opened, setOpened] = useState17({ status: "idle" });
|
|
3636
4306
|
const { subjectType, subjectId } = subject;
|
|
3637
4307
|
const canOpen = api.openConversation !== void 0;
|
|
3638
|
-
|
|
4308
|
+
useEffect22(() => {
|
|
3639
4309
|
if (!isNeeded || !canOpen) return;
|
|
3640
4310
|
let isCurrent = true;
|
|
3641
4311
|
setOpened({ status: "loading" });
|
|
@@ -3653,9 +4323,9 @@ function NotFound({
|
|
|
3653
4323
|
labels,
|
|
3654
4324
|
onBack
|
|
3655
4325
|
}) {
|
|
3656
|
-
return /* @__PURE__ */
|
|
3657
|
-
/* @__PURE__ */
|
|
3658
|
-
/* @__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 })
|
|
3659
4329
|
] });
|
|
3660
4330
|
}
|
|
3661
4331
|
function ParticipantThreadScreen(props) {
|
|
@@ -3665,12 +4335,12 @@ function ParticipantThreadScreen(props) {
|
|
|
3665
4335
|
);
|
|
3666
4336
|
const opened = useOpenedConversation(api, selected, listed === void 0);
|
|
3667
4337
|
const conversation = listed ?? (opened.status === "found" ? opened.conversation : void 0);
|
|
3668
|
-
if (conversation) return /* @__PURE__ */
|
|
4338
|
+
if (conversation) return /* @__PURE__ */ jsx40(LoadedThread, { ...props, conversation });
|
|
3669
4339
|
const isResolving = opened.status === "loading" || api.openConversation !== void 0 && opened.status === "idle";
|
|
3670
4340
|
const isListLoading = inbox.status === "idle" || inbox.status === "loading";
|
|
3671
4341
|
if (isResolving || api.openConversation === void 0 && isListLoading)
|
|
3672
|
-
return /* @__PURE__ */
|
|
3673
|
-
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 });
|
|
3674
4344
|
}
|
|
3675
4345
|
function LoadedThread(props) {
|
|
3676
4346
|
const core = useParticipantThreadController({
|
|
@@ -3678,7 +4348,7 @@ function LoadedThread(props) {
|
|
|
3678
4348
|
subject: props.selected,
|
|
3679
4349
|
onMarkedRead: props.inbox.markSubjectRead
|
|
3680
4350
|
});
|
|
3681
|
-
return /* @__PURE__ */
|
|
4351
|
+
return /* @__PURE__ */ jsx40(
|
|
3682
4352
|
ParticipantThread,
|
|
3683
4353
|
{
|
|
3684
4354
|
...core,
|
|
@@ -3690,6 +4360,8 @@ function LoadedThread(props) {
|
|
|
3690
4360
|
renderSubjectIcon: props.renderSubjectIcon,
|
|
3691
4361
|
avatars: props.avatars,
|
|
3692
4362
|
renderAuthorAvatar: props.renderAuthorAvatar,
|
|
4363
|
+
renderMessageActions: props.renderMessageActions,
|
|
4364
|
+
messageMenu: props.messageMenu,
|
|
3693
4365
|
tail: props.tail,
|
|
3694
4366
|
audioRecording: props.audioRecording,
|
|
3695
4367
|
renderComposerLock: props.renderComposerLock
|
|
@@ -3818,16 +4490,16 @@ function buildParticipantThemeStyle(theme) {
|
|
|
3818
4490
|
}
|
|
3819
4491
|
|
|
3820
4492
|
// src/participant/ParticipantConversationsScreen.tsx
|
|
3821
|
-
import { jsx as
|
|
4493
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
3822
4494
|
var ALL_FILTER3 = "all";
|
|
3823
4495
|
function ParticipantConversationsScreen(props) {
|
|
3824
4496
|
const { selected, inbox, subjectGroups, classNames } = props;
|
|
3825
|
-
const [filter, setFilter] =
|
|
3826
|
-
const [searchQuery, setSearchQuery] =
|
|
3827
|
-
const draftsRef =
|
|
4497
|
+
const [filter, setFilter] = useState18(ALL_FILTER3);
|
|
4498
|
+
const [searchQuery, setSearchQuery] = useState18("");
|
|
4499
|
+
const draftsRef = useRef23(/* @__PURE__ */ new Map());
|
|
3828
4500
|
const [sendStates, dispatchSendStates] = useReducer6(participantSendStatesReducer, /* @__PURE__ */ new Map());
|
|
3829
|
-
const labels =
|
|
3830
|
-
const view =
|
|
4501
|
+
const labels = useMemo7(() => ({ ...DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS, ...props.labels }), [props.labels]);
|
|
4502
|
+
const view = useMemo7(
|
|
3831
4503
|
() => groupParticipantConversations({
|
|
3832
4504
|
conversations: inbox.conversations,
|
|
3833
4505
|
subjectGroups,
|
|
@@ -3838,7 +4510,7 @@ function ParticipantConversationsScreen(props) {
|
|
|
3838
4510
|
);
|
|
3839
4511
|
const isSearchVisible = isInboxSearchVisible({ conversations: inbox.conversations, searchQuery });
|
|
3840
4512
|
const rootClassName = ["cv-p-root", props.className, classNames?.root].filter(Boolean).join(" ");
|
|
3841
|
-
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(
|
|
3842
4514
|
ParticipantInbox,
|
|
3843
4515
|
{
|
|
3844
4516
|
view,
|
|
@@ -3855,7 +4527,7 @@ function ParticipantConversationsScreen(props) {
|
|
|
3855
4527
|
renderSubjectIcon: props.renderSubjectIcon,
|
|
3856
4528
|
search: { value: searchQuery, onChange: setSearchQuery, isVisible: isSearchVisible }
|
|
3857
4529
|
}
|
|
3858
|
-
) }) : /* @__PURE__ */
|
|
4530
|
+
) }) : /* @__PURE__ */ jsx41("div", { className: ["cv-p-root__screen", classNames?.thread].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx41(
|
|
3859
4531
|
ParticipantThreadScreen,
|
|
3860
4532
|
{
|
|
3861
4533
|
...props,
|
|
@@ -3870,7 +4542,7 @@ function ParticipantConversationsScreen(props) {
|
|
|
3870
4542
|
}
|
|
3871
4543
|
|
|
3872
4544
|
// src/participant/useParticipantInbox.ts
|
|
3873
|
-
import { useCallback as
|
|
4545
|
+
import { useCallback as useCallback15, useEffect as useEffect23, useReducer as useReducer7 } from "react";
|
|
3874
4546
|
|
|
3875
4547
|
// src/participant/participantInboxState.ts
|
|
3876
4548
|
var INITIAL_PARTICIPANT_INBOX_STATE = { status: "idle", conversations: [] };
|
|
@@ -3921,7 +4593,7 @@ function describeError2(error) {
|
|
|
3921
4593
|
}
|
|
3922
4594
|
function useParticipantInbox(api) {
|
|
3923
4595
|
const [state, dispatch] = useReducer7(participantInboxReducer, INITIAL_PARTICIPANT_INBOX_STATE);
|
|
3924
|
-
const refresh =
|
|
4596
|
+
const refresh = useCallback15(async () => {
|
|
3925
4597
|
dispatch({ type: "started" });
|
|
3926
4598
|
try {
|
|
3927
4599
|
const page = await api.listConversations();
|
|
@@ -3931,7 +4603,7 @@ function useParticipantInbox(api) {
|
|
|
3931
4603
|
}
|
|
3932
4604
|
}, [api]);
|
|
3933
4605
|
const { nextCursor } = state;
|
|
3934
|
-
const loadMore =
|
|
4606
|
+
const loadMore = useCallback15(async () => {
|
|
3935
4607
|
if (!nextCursor) return;
|
|
3936
4608
|
try {
|
|
3937
4609
|
const page = await api.listConversations({ cursor: nextCursor });
|
|
@@ -3940,10 +4612,10 @@ function useParticipantInbox(api) {
|
|
|
3940
4612
|
dispatch({ type: "failed", error: describeError2(error) });
|
|
3941
4613
|
}
|
|
3942
4614
|
}, [api, nextCursor]);
|
|
3943
|
-
const markSubjectRead =
|
|
4615
|
+
const markSubjectRead = useCallback15((subject) => {
|
|
3944
4616
|
dispatch({ type: "markedRead", subject });
|
|
3945
4617
|
}, []);
|
|
3946
|
-
|
|
4618
|
+
useEffect23(() => {
|
|
3947
4619
|
void refresh();
|
|
3948
4620
|
}, [refresh]);
|
|
3949
4621
|
useParticipantRevalidation(api, () => {
|
|
@@ -3953,11 +4625,11 @@ function useParticipantInbox(api) {
|
|
|
3953
4625
|
}
|
|
3954
4626
|
|
|
3955
4627
|
// src/participant/ParticipantConversations.tsx
|
|
3956
|
-
import { jsx as
|
|
4628
|
+
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
3957
4629
|
function ParticipantConversations(props) {
|
|
3958
4630
|
const inbox = useParticipantInbox(props.api);
|
|
3959
4631
|
const { status, conversations, hasMore, refresh, loadMore, markSubjectRead } = inbox;
|
|
3960
|
-
return /* @__PURE__ */
|
|
4632
|
+
return /* @__PURE__ */ jsx42(
|
|
3961
4633
|
ParticipantConversationsScreen,
|
|
3962
4634
|
{
|
|
3963
4635
|
...props,
|
|
@@ -3967,7 +4639,7 @@ function ParticipantConversations(props) {
|
|
|
3967
4639
|
}
|
|
3968
4640
|
|
|
3969
4641
|
// src/participant/ConversationThread.tsx
|
|
3970
|
-
import { useCallback as
|
|
4642
|
+
import { useCallback as useCallback16, useMemo as useMemo8, useReducer as useReducer8, useRef as useRef24 } from "react";
|
|
3971
4643
|
|
|
3972
4644
|
// src/participant/conversationThreadApi.ts
|
|
3973
4645
|
function toParticipantApi(api) {
|
|
@@ -4007,20 +4679,20 @@ function buildThreadSummary({
|
|
|
4007
4679
|
}
|
|
4008
4680
|
|
|
4009
4681
|
// src/participant/ConversationThread.tsx
|
|
4010
|
-
import { jsx as
|
|
4682
|
+
import { jsx as jsx43 } from "react/jsx-runtime";
|
|
4011
4683
|
function useConversationThreadCore(props) {
|
|
4012
4684
|
const { perspective = "participant" } = props;
|
|
4013
4685
|
const { subjectType, subjectId } = props.subject;
|
|
4014
|
-
const subject =
|
|
4015
|
-
const onMarkedReadRef =
|
|
4686
|
+
const subject = useMemo8(() => ({ subjectType, subjectId }), [subjectType, subjectId]);
|
|
4687
|
+
const onMarkedReadRef = useRef24(props.onMarkedRead);
|
|
4016
4688
|
onMarkedReadRef.current = props.onMarkedRead;
|
|
4017
|
-
const handleMarkedRead =
|
|
4018
|
-
const api =
|
|
4019
|
-
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());
|
|
4020
4692
|
const [sendStates, dispatchSendStates] = useReducer8(participantSendStatesReducer, /* @__PURE__ */ new Map());
|
|
4021
|
-
const labels =
|
|
4693
|
+
const labels = useMemo8(() => ({ ...DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS, ...props.labels }), [props.labels]);
|
|
4022
4694
|
const { title, protocol, channels, status } = props;
|
|
4023
|
-
const conversation =
|
|
4695
|
+
const conversation = useMemo8(
|
|
4024
4696
|
() => buildThreadSummary({ subject, title, protocol, channels, status }),
|
|
4025
4697
|
[subject, title, protocol, channels, status]
|
|
4026
4698
|
);
|
|
@@ -4042,12 +4714,12 @@ function useConversationThreadCore(props) {
|
|
|
4042
4714
|
}
|
|
4043
4715
|
function ConversationThreadBody(props) {
|
|
4044
4716
|
const core = useConversationThreadCore(props);
|
|
4045
|
-
return /* @__PURE__ */
|
|
4717
|
+
return /* @__PURE__ */ jsx43(
|
|
4046
4718
|
"div",
|
|
4047
4719
|
{
|
|
4048
4720
|
className: ["cv-p-root", props.className].filter(Boolean).join(" "),
|
|
4049
4721
|
style: buildParticipantThemeStyle(props.theme),
|
|
4050
|
-
children: /* @__PURE__ */
|
|
4722
|
+
children: /* @__PURE__ */ jsx43("div", { className: "cv-p-root__screen", children: /* @__PURE__ */ jsx43(
|
|
4051
4723
|
ParticipantThread,
|
|
4052
4724
|
{
|
|
4053
4725
|
...core,
|
|
@@ -4057,6 +4729,8 @@ function ConversationThreadBody(props) {
|
|
|
4057
4729
|
renderSubjectIcon: props.renderSubjectIcon,
|
|
4058
4730
|
avatars: props.avatars,
|
|
4059
4731
|
renderAuthorAvatar: props.renderAuthorAvatar,
|
|
4732
|
+
renderMessageActions: props.renderMessageActions,
|
|
4733
|
+
messageMenu: props.messageMenu,
|
|
4060
4734
|
tail: props.tail,
|
|
4061
4735
|
counterpartLabel: props.counterpartLabel,
|
|
4062
4736
|
headerActions: props.headerActions,
|
|
@@ -4068,7 +4742,7 @@ function ConversationThreadBody(props) {
|
|
|
4068
4742
|
);
|
|
4069
4743
|
}
|
|
4070
4744
|
function ConversationThread(props) {
|
|
4071
|
-
return /* @__PURE__ */
|
|
4745
|
+
return /* @__PURE__ */ jsx43(ConversationThreadBody, { ...props }, draftKey(props.subject));
|
|
4072
4746
|
}
|
|
4073
4747
|
export {
|
|
4074
4748
|
ConversationThread,
|
|
@@ -4076,5 +4750,6 @@ export {
|
|
|
4076
4750
|
DEFAULT_PARTICIPANT_AUDIO_LABELS,
|
|
4077
4751
|
DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS,
|
|
4078
4752
|
ParticipantConversations,
|
|
4753
|
+
formatMessagesForCopy,
|
|
4079
4754
|
groupParticipantConversations
|
|
4080
4755
|
};
|