@poolse/react-native 2.0.13 → 2.1.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/index.cjs +736 -352
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -16
- package/dist/index.d.ts +76 -16
- package/dist/index.js +739 -355
- package/dist/index.js.map +1 -1
- package/package.json +12 -4
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,12 @@ function _interopNamespace(e) {
|
|
|
30
30
|
var ImagePicker__namespace = /*#__PURE__*/_interopNamespace(ImagePicker);
|
|
31
31
|
var DocumentPicker__namespace = /*#__PURE__*/_interopNamespace(DocumentPicker);
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
34
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
35
|
+
}) : x)(function(x) {
|
|
36
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
37
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
38
|
+
});
|
|
34
39
|
|
|
35
40
|
// src/theme/default.ts
|
|
36
41
|
var lightColors = {
|
|
@@ -1330,6 +1335,51 @@ var styles6 = reactNative.StyleSheet.create({
|
|
|
1330
1335
|
fontWeight: "500"
|
|
1331
1336
|
}
|
|
1332
1337
|
});
|
|
1338
|
+
function DaySeparator({ label }) {
|
|
1339
|
+
const theme = usePoolseTheme();
|
|
1340
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.wrap, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1341
|
+
reactNative.View,
|
|
1342
|
+
{
|
|
1343
|
+
style: [
|
|
1344
|
+
styles7.chip,
|
|
1345
|
+
{
|
|
1346
|
+
backgroundColor: theme.colors.surface2,
|
|
1347
|
+
borderColor: theme.colors.border
|
|
1348
|
+
}
|
|
1349
|
+
],
|
|
1350
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1351
|
+
reactNative.Text,
|
|
1352
|
+
{
|
|
1353
|
+
style: [
|
|
1354
|
+
styles7.label,
|
|
1355
|
+
{
|
|
1356
|
+
color: theme.colors.ink2,
|
|
1357
|
+
fontFamily: theme.type.fontBody
|
|
1358
|
+
}
|
|
1359
|
+
],
|
|
1360
|
+
children: label
|
|
1361
|
+
}
|
|
1362
|
+
)
|
|
1363
|
+
}
|
|
1364
|
+
) });
|
|
1365
|
+
}
|
|
1366
|
+
var styles7 = reactNative.StyleSheet.create({
|
|
1367
|
+
wrap: {
|
|
1368
|
+
alignItems: "center",
|
|
1369
|
+
marginVertical: 8
|
|
1370
|
+
},
|
|
1371
|
+
chip: {
|
|
1372
|
+
paddingHorizontal: 12,
|
|
1373
|
+
paddingVertical: 4,
|
|
1374
|
+
borderRadius: 999,
|
|
1375
|
+
borderWidth: reactNative.StyleSheet.hairlineWidth
|
|
1376
|
+
},
|
|
1377
|
+
label: {
|
|
1378
|
+
fontSize: 11,
|
|
1379
|
+
fontWeight: "600",
|
|
1380
|
+
letterSpacing: 0.3
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1333
1383
|
function UploadQueueStrip() {
|
|
1334
1384
|
const theme = usePoolseTheme();
|
|
1335
1385
|
const upload = useSharedUpload();
|
|
@@ -1340,8 +1390,8 @@ function UploadQueueStrip() {
|
|
|
1340
1390
|
{
|
|
1341
1391
|
horizontal: true,
|
|
1342
1392
|
showsHorizontalScrollIndicator: false,
|
|
1343
|
-
contentContainerStyle:
|
|
1344
|
-
style: [
|
|
1393
|
+
contentContainerStyle: styles8.scroll,
|
|
1394
|
+
style: [styles8.scrollStyle, { marginBottom: 8 }],
|
|
1345
1395
|
children: upload.queue.map((item) => {
|
|
1346
1396
|
const isError = item.status === "error";
|
|
1347
1397
|
const isUploading = item.status === "uploading" || item.status === "pending";
|
|
@@ -1352,7 +1402,7 @@ function UploadQueueStrip() {
|
|
|
1352
1402
|
if (item.status === "ready" || isError) upload.remove(item.localId);
|
|
1353
1403
|
else upload.cancel(item.localId);
|
|
1354
1404
|
};
|
|
1355
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1405
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.tileWrap, children: [
|
|
1356
1406
|
isImage && previewUri ? /* @__PURE__ */ jsxRuntime.jsx(ImageTile, { uri: previewUri, uploading: isUploading, error: isError }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1357
1407
|
FileTile,
|
|
1358
1408
|
{
|
|
@@ -1367,13 +1417,13 @@ function UploadQueueStrip() {
|
|
|
1367
1417
|
{
|
|
1368
1418
|
onPress: handleClose,
|
|
1369
1419
|
hitSlop: 8,
|
|
1370
|
-
style: [
|
|
1420
|
+
style: [styles8.closeBtn, { backgroundColor: theme.colors.ink }],
|
|
1371
1421
|
accessibilityLabel: isError ? "Remove failed upload" : "Cancel upload",
|
|
1372
1422
|
children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 11, color: theme.colors.onBrand })
|
|
1373
1423
|
}
|
|
1374
1424
|
) : null,
|
|
1375
|
-
isUploading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
1376
|
-
isError ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
1425
|
+
isUploading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.progressOverlay, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles8.spinner, { borderColor: theme.colors.onBrand }] }) }) : null,
|
|
1426
|
+
isError ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles8.errorOverlay, { backgroundColor: "rgba(229,72,77,0.85)" }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.errorText, { color: "#fff" }], children: "Failed" }) }) : null
|
|
1377
1427
|
] }, item.localId);
|
|
1378
1428
|
})
|
|
1379
1429
|
}
|
|
@@ -1386,7 +1436,7 @@ function ImageTile({ uri, uploading, error }) {
|
|
|
1386
1436
|
{
|
|
1387
1437
|
source: { uri },
|
|
1388
1438
|
style: [
|
|
1389
|
-
|
|
1439
|
+
styles8.imageTile,
|
|
1390
1440
|
{
|
|
1391
1441
|
backgroundColor: theme.colors.surface2,
|
|
1392
1442
|
borderColor: error ? theme.colors.error : "transparent",
|
|
@@ -1408,24 +1458,24 @@ function FileTile({
|
|
|
1408
1458
|
reactNative.View,
|
|
1409
1459
|
{
|
|
1410
1460
|
style: [
|
|
1411
|
-
|
|
1461
|
+
styles8.fileTile,
|
|
1412
1462
|
{
|
|
1413
1463
|
backgroundColor: theme.colors.surface2,
|
|
1414
1464
|
borderColor: error ? theme.colors.error : uploading ? theme.colors.brand : theme.colors.border
|
|
1415
1465
|
}
|
|
1416
1466
|
],
|
|
1417
1467
|
children: [
|
|
1418
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
1419
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1468
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles8.fileIconBox, { backgroundColor: theme.colors.brandSoft }], children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "attachment", size: 18, color: theme.colors.brand }) }),
|
|
1469
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.fileInfo, children: [
|
|
1420
1470
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1421
1471
|
reactNative.Text,
|
|
1422
1472
|
{
|
|
1423
|
-
style: [
|
|
1473
|
+
style: [styles8.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
|
|
1424
1474
|
numberOfLines: 1,
|
|
1425
1475
|
children: filename
|
|
1426
1476
|
}
|
|
1427
1477
|
),
|
|
1428
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1478
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.fileSize, { color: theme.colors.ink3 }], children: formatBytes(size) })
|
|
1429
1479
|
] })
|
|
1430
1480
|
]
|
|
1431
1481
|
}
|
|
@@ -1439,7 +1489,7 @@ function formatBytes(n) {
|
|
|
1439
1489
|
return `${(n / 1024 / 1024 / 1024).toFixed(1)} GB`;
|
|
1440
1490
|
}
|
|
1441
1491
|
var TILE = 64;
|
|
1442
|
-
var
|
|
1492
|
+
var styles8 = reactNative.StyleSheet.create({
|
|
1443
1493
|
scrollStyle: {
|
|
1444
1494
|
overflow: "visible"
|
|
1445
1495
|
},
|
|
@@ -1539,6 +1589,22 @@ var styles7 = reactNative.StyleSheet.create({
|
|
|
1539
1589
|
fontWeight: "700"
|
|
1540
1590
|
}
|
|
1541
1591
|
});
|
|
1592
|
+
var FALLBACK = {
|
|
1593
|
+
top: reactNative.Platform.OS === "ios" ? 50 : 24,
|
|
1594
|
+
bottom: reactNative.Platform.OS === "ios" ? 34 : 0,
|
|
1595
|
+
left: 0,
|
|
1596
|
+
right: 0
|
|
1597
|
+
};
|
|
1598
|
+
var realHook = null;
|
|
1599
|
+
try {
|
|
1600
|
+
const mod = __require("react-native-safe-area-context");
|
|
1601
|
+
if (mod && typeof mod.useSafeAreaInsets === "function") {
|
|
1602
|
+
realHook = mod.useSafeAreaInsets;
|
|
1603
|
+
}
|
|
1604
|
+
} catch {
|
|
1605
|
+
}
|
|
1606
|
+
var useFallback = () => FALLBACK;
|
|
1607
|
+
var useSafeInsets = realHook ?? useFallback;
|
|
1542
1608
|
var MessageComposer = react.forwardRef(
|
|
1543
1609
|
function MessageComposer2({
|
|
1544
1610
|
onSend,
|
|
@@ -1550,13 +1616,32 @@ var MessageComposer = react.forwardRef(
|
|
|
1550
1616
|
labelFor,
|
|
1551
1617
|
attachments = true,
|
|
1552
1618
|
onAttachPress,
|
|
1553
|
-
keyboardOffset: _keyboardOffset = 0
|
|
1619
|
+
keyboardOffset: _keyboardOffset = 0,
|
|
1620
|
+
editingMessage = null,
|
|
1621
|
+
onCancelEdit,
|
|
1622
|
+
onSaveEdit
|
|
1554
1623
|
}, ref) {
|
|
1555
1624
|
const theme = usePoolseTheme();
|
|
1556
1625
|
const inputRef = react.useRef(null);
|
|
1557
1626
|
const [value, setValue] = react.useState("");
|
|
1558
1627
|
const [sending, setSending] = react.useState(false);
|
|
1628
|
+
const [keyboardOpen, setKeyboardOpen] = react.useState(false);
|
|
1559
1629
|
const upload = useSharedUpload();
|
|
1630
|
+
const insets = useSafeInsets();
|
|
1631
|
+
react.useEffect(() => {
|
|
1632
|
+
const show = reactNative.Keyboard.addListener(
|
|
1633
|
+
reactNative.Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow",
|
|
1634
|
+
() => setKeyboardOpen(true)
|
|
1635
|
+
);
|
|
1636
|
+
const hide = reactNative.Keyboard.addListener(
|
|
1637
|
+
reactNative.Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide",
|
|
1638
|
+
() => setKeyboardOpen(false)
|
|
1639
|
+
);
|
|
1640
|
+
return () => {
|
|
1641
|
+
show.remove();
|
|
1642
|
+
hide.remove();
|
|
1643
|
+
};
|
|
1644
|
+
}, []);
|
|
1560
1645
|
react.useImperativeHandle(
|
|
1561
1646
|
ref,
|
|
1562
1647
|
() => ({
|
|
@@ -1575,36 +1660,65 @@ var MessageComposer = react.forwardRef(
|
|
|
1575
1660
|
const isUploading = upload.queue.some(
|
|
1576
1661
|
(item) => item.status === "uploading" || item.status === "pending"
|
|
1577
1662
|
);
|
|
1578
|
-
const
|
|
1663
|
+
const previousDraftRef = react.useRef(null);
|
|
1664
|
+
const editingIdRef = react.useRef(null);
|
|
1665
|
+
react.useEffect(() => {
|
|
1666
|
+
const id = editingMessage?.id ?? null;
|
|
1667
|
+
if (id === editingIdRef.current) return;
|
|
1668
|
+
if (id) {
|
|
1669
|
+
previousDraftRef.current = value;
|
|
1670
|
+
setValue(editingMessage?.body ?? "");
|
|
1671
|
+
inputRef.current?.focus();
|
|
1672
|
+
} else {
|
|
1673
|
+
setValue(previousDraftRef.current ?? "");
|
|
1674
|
+
previousDraftRef.current = null;
|
|
1675
|
+
}
|
|
1676
|
+
editingIdRef.current = id;
|
|
1677
|
+
}, [editingMessage?.id, editingMessage?.body]);
|
|
1678
|
+
const isEditing = !!editingMessage;
|
|
1679
|
+
const editChanged = isEditing && value.trim() !== (editingMessage?.body?.trim() ?? "");
|
|
1680
|
+
const submitDisabled = disabled || sending || isUploading || value.trim().length === 0 || (isEditing ? !editChanged : value.trim().length === 0 && readyAttachmentIds.length === 0);
|
|
1579
1681
|
const handleSend = async () => {
|
|
1580
1682
|
if (submitDisabled) return;
|
|
1581
1683
|
const body = value.trim();
|
|
1582
|
-
const replyId = replyingTo?.id;
|
|
1583
|
-
const attachmentIds = readyAttachmentIds;
|
|
1584
|
-
setValue("");
|
|
1585
|
-
upload.reset();
|
|
1586
1684
|
setSending(true);
|
|
1587
1685
|
try {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1686
|
+
if (isEditing && onSaveEdit) {
|
|
1687
|
+
await onSaveEdit(body);
|
|
1688
|
+
} else {
|
|
1689
|
+
const replyId = replyingTo?.id;
|
|
1690
|
+
const attachmentIds = readyAttachmentIds;
|
|
1691
|
+
setValue("");
|
|
1692
|
+
upload.reset();
|
|
1693
|
+
await onSend(body, {
|
|
1694
|
+
...replyId ? { quoted_message_id: replyId } : {},
|
|
1695
|
+
...attachmentIds.length > 0 ? { attachment_ids: attachmentIds } : {}
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1592
1698
|
} finally {
|
|
1593
1699
|
setSending(false);
|
|
1594
1700
|
}
|
|
1595
1701
|
};
|
|
1702
|
+
const paddingBottom = keyboardOpen ? 10 : Math.max(10, insets.bottom);
|
|
1596
1703
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1597
1704
|
reactNative.View,
|
|
1598
1705
|
{
|
|
1599
1706
|
style: [
|
|
1600
|
-
|
|
1707
|
+
styles9.wrap,
|
|
1601
1708
|
{
|
|
1602
1709
|
backgroundColor: theme.colors.surface,
|
|
1603
|
-
borderTopColor: theme.colors.border
|
|
1710
|
+
borderTopColor: theme.colors.border,
|
|
1711
|
+
paddingBottom
|
|
1604
1712
|
}
|
|
1605
1713
|
],
|
|
1606
1714
|
children: [
|
|
1607
|
-
|
|
1715
|
+
isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1716
|
+
EditChip,
|
|
1717
|
+
{
|
|
1718
|
+
message: editingMessage,
|
|
1719
|
+
...onCancelEdit ? { onCancel: onCancelEdit } : {}
|
|
1720
|
+
}
|
|
1721
|
+
) : replyingTo ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1608
1722
|
ReplyChip,
|
|
1609
1723
|
{
|
|
1610
1724
|
message: replyingTo,
|
|
@@ -1612,14 +1726,14 @@ var MessageComposer = react.forwardRef(
|
|
|
1612
1726
|
...onCancelReply ? { onCancel: onCancelReply } : {}
|
|
1613
1727
|
}
|
|
1614
1728
|
) : null,
|
|
1615
|
-
attachments ? /* @__PURE__ */ jsxRuntime.jsx(UploadQueueStrip, {}) : null,
|
|
1616
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1729
|
+
attachments && !isEditing ? /* @__PURE__ */ jsxRuntime.jsx(UploadQueueStrip, {}) : null,
|
|
1730
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles9.row, children: [
|
|
1617
1731
|
attachments && onAttachPress ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1618
1732
|
reactNative.Pressable,
|
|
1619
1733
|
{
|
|
1620
1734
|
onPress: onAttachPress,
|
|
1621
1735
|
hitSlop: 8,
|
|
1622
|
-
style:
|
|
1736
|
+
style: styles9.iconBtn,
|
|
1623
1737
|
accessibilityLabel: "Attach file",
|
|
1624
1738
|
children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "attachment", size: 22, color: theme.colors.ink2 })
|
|
1625
1739
|
}
|
|
@@ -1638,7 +1752,7 @@ var MessageComposer = react.forwardRef(
|
|
|
1638
1752
|
multiline: true,
|
|
1639
1753
|
editable: !disabled,
|
|
1640
1754
|
style: [
|
|
1641
|
-
|
|
1755
|
+
styles9.input,
|
|
1642
1756
|
{
|
|
1643
1757
|
color: theme.colors.ink,
|
|
1644
1758
|
backgroundColor: theme.colors.surface2,
|
|
@@ -1655,7 +1769,7 @@ var MessageComposer = react.forwardRef(
|
|
|
1655
1769
|
onPress: handleSend,
|
|
1656
1770
|
disabled: submitDisabled,
|
|
1657
1771
|
style: [
|
|
1658
|
-
|
|
1772
|
+
styles9.sendBtn,
|
|
1659
1773
|
{
|
|
1660
1774
|
backgroundColor: submitDisabled ? theme.colors.ink3 : theme.colors.brand,
|
|
1661
1775
|
opacity: submitDisabled ? 0.6 : 1
|
|
@@ -1671,6 +1785,28 @@ var MessageComposer = react.forwardRef(
|
|
|
1671
1785
|
);
|
|
1672
1786
|
}
|
|
1673
1787
|
);
|
|
1788
|
+
function EditChip({ message, onCancel }) {
|
|
1789
|
+
const theme = usePoolseTheme();
|
|
1790
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1791
|
+
reactNative.View,
|
|
1792
|
+
{
|
|
1793
|
+
style: [
|
|
1794
|
+
styles9.replyChip,
|
|
1795
|
+
{
|
|
1796
|
+
backgroundColor: theme.colors.surface2,
|
|
1797
|
+
borderLeftColor: theme.colors.brand
|
|
1798
|
+
}
|
|
1799
|
+
],
|
|
1800
|
+
children: [
|
|
1801
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
|
|
1802
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: "Editing message" }),
|
|
1803
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
|
|
1804
|
+
] }),
|
|
1805
|
+
onCancel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onCancel, hitSlop: 8, style: styles9.replyCancel, children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
|
|
1806
|
+
]
|
|
1807
|
+
}
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1674
1810
|
function ReplyChip({
|
|
1675
1811
|
message,
|
|
1676
1812
|
labelFor,
|
|
@@ -1683,7 +1819,7 @@ function ReplyChip({
|
|
|
1683
1819
|
reactNative.View,
|
|
1684
1820
|
{
|
|
1685
1821
|
style: [
|
|
1686
|
-
|
|
1822
|
+
styles9.replyChip,
|
|
1687
1823
|
{
|
|
1688
1824
|
backgroundColor: theme.colors.surface2,
|
|
1689
1825
|
borderLeftColor: theme.colors.brand
|
|
@@ -1691,27 +1827,26 @@ function ReplyChip({
|
|
|
1691
1827
|
],
|
|
1692
1828
|
children: [
|
|
1693
1829
|
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
|
|
1694
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [
|
|
1830
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: [
|
|
1695
1831
|
"Replying to ",
|
|
1696
1832
|
senderName
|
|
1697
1833
|
] }),
|
|
1698
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1834
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
|
|
1699
1835
|
] }),
|
|
1700
|
-
onCancel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onCancel, hitSlop: 8, style:
|
|
1836
|
+
onCancel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onCancel, hitSlop: 8, style: styles9.replyCancel, children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
|
|
1701
1837
|
]
|
|
1702
1838
|
}
|
|
1703
1839
|
);
|
|
1704
1840
|
}
|
|
1705
|
-
var
|
|
1841
|
+
var styles9 = reactNative.StyleSheet.create({
|
|
1706
1842
|
wrap: {
|
|
1707
1843
|
borderTopWidth: reactNative.StyleSheet.hairlineWidth,
|
|
1708
1844
|
paddingTop: 8,
|
|
1709
|
-
paddingBottom: 10,
|
|
1710
1845
|
paddingHorizontal: 12
|
|
1711
|
-
//
|
|
1712
|
-
//
|
|
1713
|
-
//
|
|
1714
|
-
//
|
|
1846
|
+
// paddingBottom is applied inline — it follows the keyboard state
|
|
1847
|
+
// (10 px while open, max(10, safeBottomInset) while closed) so the
|
|
1848
|
+
// send/attach buttons stay clear of the home indicator without
|
|
1849
|
+
// double-padding above the keyboard.
|
|
1715
1850
|
},
|
|
1716
1851
|
row: {
|
|
1717
1852
|
flexDirection: "row",
|
|
@@ -1800,11 +1935,11 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
|
|
|
1800
1935
|
transparent: true,
|
|
1801
1936
|
animationType: "slide",
|
|
1802
1937
|
onRequestClose: () => setPickerOpen(false),
|
|
1803
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style:
|
|
1938
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles10.backdrop, onPress: () => setPickerOpen(false), children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1804
1939
|
reactNative.View,
|
|
1805
1940
|
{
|
|
1806
1941
|
style: [
|
|
1807
|
-
|
|
1942
|
+
styles10.sheet,
|
|
1808
1943
|
{
|
|
1809
1944
|
backgroundColor: theme.colors.surface,
|
|
1810
1945
|
borderTopLeftRadius: theme.radii.lg,
|
|
@@ -1815,7 +1950,7 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
|
|
|
1815
1950
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1816
1951
|
reactNative.Text,
|
|
1817
1952
|
{
|
|
1818
|
-
style: [
|
|
1953
|
+
style: [styles10.title, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
|
|
1819
1954
|
children: "Mention a member"
|
|
1820
1955
|
}
|
|
1821
1956
|
),
|
|
@@ -1846,12 +1981,12 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
|
|
|
1846
1981
|
}
|
|
1847
1982
|
function MentionRow({ member, onPress }) {
|
|
1848
1983
|
const theme = usePoolseTheme();
|
|
1849
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress, style:
|
|
1984
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress, style: styles10.row, children: [
|
|
1850
1985
|
/* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: member.external_id, seed: member.external_id, size: "sm" }),
|
|
1851
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1986
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles10.rowText, { color: theme.colors.ink, fontFamily: theme.type.fontBody }], children: member.external_id })
|
|
1852
1987
|
] });
|
|
1853
1988
|
}
|
|
1854
|
-
var
|
|
1989
|
+
var styles10 = reactNative.StyleSheet.create({
|
|
1855
1990
|
backdrop: {
|
|
1856
1991
|
flex: 1,
|
|
1857
1992
|
backgroundColor: "rgba(0,0,0,0.35)",
|
|
@@ -1878,138 +2013,134 @@ var styles9 = reactNative.StyleSheet.create({
|
|
|
1878
2013
|
fontSize: 14
|
|
1879
2014
|
}
|
|
1880
2015
|
});
|
|
2016
|
+
var AT_BOTTOM_THRESHOLD = 32;
|
|
1881
2017
|
function MessageList({
|
|
1882
2018
|
messages,
|
|
2019
|
+
items,
|
|
1883
2020
|
loading = false,
|
|
1884
2021
|
hasMore = false,
|
|
1885
2022
|
onLoadMore,
|
|
1886
2023
|
renderItem,
|
|
1887
2024
|
ListHeader = null,
|
|
1888
2025
|
ListFooter = null,
|
|
1889
|
-
emptyState
|
|
2026
|
+
emptyState,
|
|
2027
|
+
meId = null
|
|
1890
2028
|
}) {
|
|
1891
2029
|
const theme = usePoolseTheme();
|
|
1892
|
-
const
|
|
2030
|
+
const flatListRef = react.useRef(null);
|
|
2031
|
+
const [atBottom, setAtBottom] = react.useState(true);
|
|
2032
|
+
const [newCount, setNewCount] = react.useState(0);
|
|
2033
|
+
const lastTailIdRef = react.useRef(null);
|
|
2034
|
+
const sourceItems = items ?? messages.map((msg) => ({ kind: "msg", msg }));
|
|
2035
|
+
const data = [...sourceItems].reverse();
|
|
2036
|
+
react.useEffect(() => {
|
|
2037
|
+
const tail = messages[messages.length - 1];
|
|
2038
|
+
if (!tail) return;
|
|
2039
|
+
const tailId = tail.id;
|
|
2040
|
+
if (tailId === lastTailIdRef.current) return;
|
|
2041
|
+
const isFirstSet = lastTailIdRef.current === null;
|
|
2042
|
+
lastTailIdRef.current = tailId;
|
|
2043
|
+
if (atBottom || isFirstSet) {
|
|
2044
|
+
setNewCount(0);
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
if (meId !== null && tail.sender_id === meId) return;
|
|
2048
|
+
setNewCount((n) => n + 1);
|
|
2049
|
+
}, [messages, atBottom, meId]);
|
|
2050
|
+
const handleScroll = react.useCallback((e) => {
|
|
2051
|
+
const y = e.nativeEvent.contentOffset.y;
|
|
2052
|
+
const next = y <= AT_BOTTOM_THRESHOLD;
|
|
2053
|
+
setAtBottom((prev) => prev === next ? prev : next);
|
|
2054
|
+
if (next) setNewCount(0);
|
|
2055
|
+
}, []);
|
|
2056
|
+
const scrollToBottom = react.useCallback(() => {
|
|
2057
|
+
flatListRef.current?.scrollToOffset({ offset: 0, animated: true });
|
|
2058
|
+
setNewCount(0);
|
|
2059
|
+
}, []);
|
|
1893
2060
|
const handleRender = react.useCallback(
|
|
1894
|
-
({ item, index }) =>
|
|
1895
|
-
|
|
2061
|
+
({ item, index }) => {
|
|
2062
|
+
if (item.kind === "separator") return item.node;
|
|
2063
|
+
const sourceIndex = data.length - 1 - index;
|
|
2064
|
+
return renderItem(item.msg, sourceIndex);
|
|
2065
|
+
},
|
|
2066
|
+
[renderItem, data.length]
|
|
1896
2067
|
);
|
|
1897
2068
|
if (loading && messages.length === 0) {
|
|
1898
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
2069
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles11.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.brand }) });
|
|
1899
2070
|
}
|
|
1900
2071
|
if (messages.length === 0) {
|
|
1901
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
2072
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles11.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState }) : emptyState ?? null });
|
|
1902
2073
|
}
|
|
1903
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
2074
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles11.root, children: [
|
|
2075
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2076
|
+
reactNative.FlatList,
|
|
2077
|
+
{
|
|
2078
|
+
ref: flatListRef,
|
|
2079
|
+
inverted: true,
|
|
2080
|
+
data,
|
|
2081
|
+
keyExtractor: (item) => item.kind === "msg" ? item.msg.id : item.id,
|
|
2082
|
+
renderItem: handleRender,
|
|
2083
|
+
onEndReached: hasMore ? onLoadMore : void 0,
|
|
2084
|
+
onEndReachedThreshold: 0.3,
|
|
2085
|
+
onScroll: handleScroll,
|
|
2086
|
+
scrollEventThrottle: 32,
|
|
2087
|
+
ListHeaderComponent: ListHeader,
|
|
2088
|
+
ListFooterComponent: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { children: [
|
|
2089
|
+
hasMore && loading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.ink3, style: { marginVertical: 8 } }) : null,
|
|
2090
|
+
ListFooter
|
|
2091
|
+
] }),
|
|
2092
|
+
style: { backgroundColor: theme.colors.paper },
|
|
2093
|
+
contentContainerStyle: { paddingVertical: 8 }
|
|
2094
|
+
}
|
|
2095
|
+
),
|
|
2096
|
+
!atBottom && newCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2097
|
+
reactNative.Pressable,
|
|
2098
|
+
{
|
|
2099
|
+
onPress: scrollToBottom,
|
|
2100
|
+
style: [
|
|
2101
|
+
styles11.jumpButton,
|
|
2102
|
+
{
|
|
2103
|
+
backgroundColor: theme.colors.brand
|
|
2104
|
+
},
|
|
2105
|
+
theme.shadows.sm
|
|
2106
|
+
],
|
|
2107
|
+
accessibilityLabel: `${newCount} new ${newCount === 1 ? "message" : "messages"}, jump to latest`,
|
|
2108
|
+
children: [
|
|
2109
|
+
/* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "chevron-down", size: 14, color: theme.colors.onBrand }),
|
|
2110
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles11.jumpText, { color: theme.colors.onBrand }], children: [
|
|
2111
|
+
newCount,
|
|
2112
|
+
" new ",
|
|
2113
|
+
newCount === 1 ? "message" : "messages"
|
|
2114
|
+
] })
|
|
2115
|
+
]
|
|
2116
|
+
}
|
|
2117
|
+
) : null
|
|
2118
|
+
] });
|
|
1921
2119
|
}
|
|
1922
|
-
var
|
|
2120
|
+
var styles11 = reactNative.StyleSheet.create({
|
|
2121
|
+
root: {
|
|
2122
|
+
flex: 1
|
|
2123
|
+
},
|
|
1923
2124
|
placeholder: {
|
|
1924
2125
|
flex: 1,
|
|
1925
2126
|
alignItems: "center",
|
|
1926
2127
|
justifyContent: "center",
|
|
1927
2128
|
padding: 24
|
|
1928
|
-
}
|
|
1929
|
-
});
|
|
1930
|
-
function EditableMessageBubble({
|
|
1931
|
-
message,
|
|
1932
|
-
currentUserId,
|
|
1933
|
-
onCancel,
|
|
1934
|
-
onSave
|
|
1935
|
-
}) {
|
|
1936
|
-
const theme = usePoolseTheme();
|
|
1937
|
-
const isSelf = currentUserId !== null && message.sender_id === currentUserId;
|
|
1938
|
-
const [value, setValue] = react.useState(message.body ?? "");
|
|
1939
|
-
const [saving, setSaving] = react.useState(false);
|
|
1940
|
-
const submit = async () => {
|
|
1941
|
-
if (saving || !value.trim()) return;
|
|
1942
|
-
setSaving(true);
|
|
1943
|
-
try {
|
|
1944
|
-
await onSave(value.trim());
|
|
1945
|
-
} finally {
|
|
1946
|
-
setSaving(false);
|
|
1947
|
-
}
|
|
1948
|
-
};
|
|
1949
|
-
const bg = isSelf ? theme.colors.selfBubble : theme.colors.otherBubble;
|
|
1950
|
-
const ink = isSelf ? theme.colors.selfBubbleText : theme.colors.otherBubbleText;
|
|
1951
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1952
|
-
reactNative.View,
|
|
1953
|
-
{
|
|
1954
|
-
style: [
|
|
1955
|
-
styles11.wrap,
|
|
1956
|
-
{
|
|
1957
|
-
alignSelf: isSelf ? "flex-end" : "flex-start",
|
|
1958
|
-
maxWidth: "80%"
|
|
1959
|
-
}
|
|
1960
|
-
],
|
|
1961
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1962
|
-
reactNative.View,
|
|
1963
|
-
{
|
|
1964
|
-
style: [
|
|
1965
|
-
styles11.bubble,
|
|
1966
|
-
{
|
|
1967
|
-
backgroundColor: bg,
|
|
1968
|
-
borderRadius: theme.radii.bubble,
|
|
1969
|
-
paddingHorizontal: theme.spacing.md,
|
|
1970
|
-
paddingVertical: theme.spacing.sm
|
|
1971
|
-
},
|
|
1972
|
-
theme.shadows.sm
|
|
1973
|
-
],
|
|
1974
|
-
children: [
|
|
1975
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1976
|
-
reactNative.TextInput,
|
|
1977
|
-
{
|
|
1978
|
-
value,
|
|
1979
|
-
onChangeText: setValue,
|
|
1980
|
-
multiline: true,
|
|
1981
|
-
autoFocus: true,
|
|
1982
|
-
style: {
|
|
1983
|
-
color: ink,
|
|
1984
|
-
fontFamily: theme.type.fontBody,
|
|
1985
|
-
fontSize: theme.type.bodySize,
|
|
1986
|
-
minHeight: 24
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
),
|
|
1990
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles11.actions, children: [
|
|
1991
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onCancel, hitSlop: 6, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles11.btn, { color: ink, opacity: 0.7 }], children: "Cancel" }) }),
|
|
1992
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: submit, disabled: saving, hitSlop: 6, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles11.btn, { color: ink, fontWeight: "700" }], children: saving ? "Saving\u2026" : "Save" }) })
|
|
1993
|
-
] })
|
|
1994
|
-
]
|
|
1995
|
-
}
|
|
1996
|
-
)
|
|
1997
|
-
}
|
|
1998
|
-
);
|
|
1999
|
-
}
|
|
2000
|
-
var styles11 = reactNative.StyleSheet.create({
|
|
2001
|
-
wrap: {
|
|
2002
|
-
marginBottom: 4
|
|
2003
2129
|
},
|
|
2004
|
-
|
|
2005
|
-
|
|
2130
|
+
jumpButton: {
|
|
2131
|
+
position: "absolute",
|
|
2132
|
+
alignSelf: "center",
|
|
2133
|
+
bottom: 12,
|
|
2006
2134
|
flexDirection: "row",
|
|
2007
|
-
|
|
2008
|
-
gap:
|
|
2009
|
-
|
|
2135
|
+
alignItems: "center",
|
|
2136
|
+
gap: 6,
|
|
2137
|
+
paddingHorizontal: 14,
|
|
2138
|
+
paddingVertical: 8,
|
|
2139
|
+
borderRadius: 999
|
|
2010
2140
|
},
|
|
2011
|
-
|
|
2012
|
-
fontSize: 13
|
|
2141
|
+
jumpText: {
|
|
2142
|
+
fontSize: 13,
|
|
2143
|
+
fontWeight: "600"
|
|
2013
2144
|
}
|
|
2014
2145
|
});
|
|
2015
2146
|
var DEFAULT_REACTION_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F389}", "\u{1F64F}", "\u{1F525}"];
|
|
@@ -2472,6 +2603,77 @@ var styles14 = reactNative.StyleSheet.create({
|
|
|
2472
2603
|
borderRadius: 20
|
|
2473
2604
|
}
|
|
2474
2605
|
});
|
|
2606
|
+
var MarkdownModule;
|
|
2607
|
+
var warned = false;
|
|
2608
|
+
function loadMarkdown() {
|
|
2609
|
+
if (MarkdownModule === void 0) {
|
|
2610
|
+
try {
|
|
2611
|
+
MarkdownModule = __require("react-native-markdown-display");
|
|
2612
|
+
} catch {
|
|
2613
|
+
MarkdownModule = null;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
const fn = MarkdownModule?.default;
|
|
2617
|
+
if (!fn) {
|
|
2618
|
+
if (!warned) {
|
|
2619
|
+
warned = true;
|
|
2620
|
+
console.warn(
|
|
2621
|
+
"[@poolse/react-native] Markdown rendering requires `react-native-markdown-display`. Install it to enable bold/italic/code/links in message bubbles. Falling back to plain text."
|
|
2622
|
+
);
|
|
2623
|
+
}
|
|
2624
|
+
return null;
|
|
2625
|
+
}
|
|
2626
|
+
return fn;
|
|
2627
|
+
}
|
|
2628
|
+
function renderMarkdown(body, args) {
|
|
2629
|
+
const Markdown = loadMarkdown();
|
|
2630
|
+
if (!Markdown) return null;
|
|
2631
|
+
const baseText = {
|
|
2632
|
+
color: args.textColor,
|
|
2633
|
+
fontFamily: args.fontFamily,
|
|
2634
|
+
fontSize: args.fontSize,
|
|
2635
|
+
lineHeight: args.lineHeight
|
|
2636
|
+
};
|
|
2637
|
+
const style = {
|
|
2638
|
+
body: baseText,
|
|
2639
|
+
paragraph: { ...baseText, marginTop: 0, marginBottom: 0 },
|
|
2640
|
+
text: baseText,
|
|
2641
|
+
strong: { ...baseText, fontWeight: "700" },
|
|
2642
|
+
em: { ...baseText, fontStyle: "italic" },
|
|
2643
|
+
s: { ...baseText, textDecorationLine: "line-through" },
|
|
2644
|
+
link: { color: args.linkColor, textDecorationLine: "underline" },
|
|
2645
|
+
code_inline: {
|
|
2646
|
+
...baseText,
|
|
2647
|
+
backgroundColor: "rgba(127,127,127,0.18)",
|
|
2648
|
+
borderRadius: 4,
|
|
2649
|
+
paddingHorizontal: 4
|
|
2650
|
+
},
|
|
2651
|
+
code_block: {
|
|
2652
|
+
...baseText,
|
|
2653
|
+
backgroundColor: "rgba(127,127,127,0.18)",
|
|
2654
|
+
padding: 8,
|
|
2655
|
+
borderRadius: 6
|
|
2656
|
+
},
|
|
2657
|
+
fence: {
|
|
2658
|
+
...baseText,
|
|
2659
|
+
backgroundColor: "rgba(127,127,127,0.18)",
|
|
2660
|
+
padding: 8,
|
|
2661
|
+
borderRadius: 6
|
|
2662
|
+
},
|
|
2663
|
+
bullet_list: { marginTop: 2, marginBottom: 2 },
|
|
2664
|
+
ordered_list: { marginTop: 2, marginBottom: 2 },
|
|
2665
|
+
list_item: baseText,
|
|
2666
|
+
blockquote: {
|
|
2667
|
+
...baseText,
|
|
2668
|
+
borderLeftWidth: 3,
|
|
2669
|
+
borderLeftColor: args.linkColor,
|
|
2670
|
+
paddingLeft: 8,
|
|
2671
|
+
marginVertical: 4,
|
|
2672
|
+
opacity: 0.85
|
|
2673
|
+
}
|
|
2674
|
+
};
|
|
2675
|
+
return react.createElement(Markdown, { style, onLinkPress: args.onLinkPress }, body);
|
|
2676
|
+
}
|
|
2475
2677
|
function MessageBubble({
|
|
2476
2678
|
message,
|
|
2477
2679
|
currentUserId,
|
|
@@ -2482,7 +2684,8 @@ function MessageBubble({
|
|
|
2482
2684
|
maxBodyLength = 0,
|
|
2483
2685
|
showSenderName = false,
|
|
2484
2686
|
actionsTrigger,
|
|
2485
|
-
showAttachments = true
|
|
2687
|
+
showAttachments = true,
|
|
2688
|
+
markdown = true
|
|
2486
2689
|
}) {
|
|
2487
2690
|
const theme = usePoolseTheme();
|
|
2488
2691
|
const { width: screenWidth } = reactNative.useWindowDimensions();
|
|
@@ -2567,22 +2770,53 @@ function MessageBubble({
|
|
|
2567
2770
|
) : null,
|
|
2568
2771
|
renderAttachments && images.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ImageMosaic, { images, maxWidth: contentMaxWidth }) : null,
|
|
2569
2772
|
renderAttachments && files.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { gap: theme.spacing.xs, marginBottom: theme.spacing.xs }, children: files.map((att) => /* @__PURE__ */ jsxRuntime.jsx(AttachmentPreview, { attachment: att, maxWidth: contentMaxWidth }, att.id)) }) : null,
|
|
2570
|
-
body.length > 0 ?
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
styles15.body,
|
|
2773
|
+
body.length > 0 ? (() => {
|
|
2774
|
+
if (isDeleted) {
|
|
2775
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2776
|
+
reactNative.Text,
|
|
2575
2777
|
{
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2778
|
+
style: [
|
|
2779
|
+
styles15.body,
|
|
2780
|
+
{
|
|
2781
|
+
color: theme.colors.ink3,
|
|
2782
|
+
fontFamily: theme.type.fontBody,
|
|
2783
|
+
fontSize: theme.type.bodySize,
|
|
2784
|
+
lineHeight: theme.type.lineHeight,
|
|
2785
|
+
fontStyle: "italic"
|
|
2786
|
+
}
|
|
2787
|
+
],
|
|
2788
|
+
children: "Message deleted"
|
|
2581
2789
|
}
|
|
2582
|
-
|
|
2583
|
-
children: isDeleted ? "Message deleted" : truncated
|
|
2790
|
+
);
|
|
2584
2791
|
}
|
|
2585
|
-
|
|
2792
|
+
const md = markdown ? renderMarkdown(truncated, {
|
|
2793
|
+
textColor,
|
|
2794
|
+
linkColor: isSelf ? theme.colors.onBrand : theme.colors.brand,
|
|
2795
|
+
fontFamily: theme.type.fontBody,
|
|
2796
|
+
fontSize: theme.type.bodySize,
|
|
2797
|
+
lineHeight: theme.type.lineHeight,
|
|
2798
|
+
onLinkPress: (url) => {
|
|
2799
|
+
void reactNative.Linking.openURL(url).catch(() => void 0);
|
|
2800
|
+
return false;
|
|
2801
|
+
}
|
|
2802
|
+
}) : null;
|
|
2803
|
+
if (md) return md;
|
|
2804
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2805
|
+
reactNative.Text,
|
|
2806
|
+
{
|
|
2807
|
+
style: [
|
|
2808
|
+
styles15.body,
|
|
2809
|
+
{
|
|
2810
|
+
color: textColor,
|
|
2811
|
+
fontFamily: theme.type.fontBody,
|
|
2812
|
+
fontSize: theme.type.bodySize,
|
|
2813
|
+
lineHeight: theme.type.lineHeight
|
|
2814
|
+
}
|
|
2815
|
+
],
|
|
2816
|
+
children: truncated
|
|
2817
|
+
}
|
|
2818
|
+
);
|
|
2819
|
+
})() : null,
|
|
2586
2820
|
maxBodyLength > 0 && body.length > maxBodyLength ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => setExpanded((e) => !e), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2587
2821
|
reactNative.Text,
|
|
2588
2822
|
{
|
|
@@ -2675,9 +2909,9 @@ function formatTime(iso) {
|
|
|
2675
2909
|
try {
|
|
2676
2910
|
const d = new Date(iso);
|
|
2677
2911
|
const now = /* @__PURE__ */ new Date();
|
|
2678
|
-
const
|
|
2912
|
+
const sameDay2 = isSameDay(d, now);
|
|
2679
2913
|
const time = d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
|
|
2680
|
-
if (
|
|
2914
|
+
if (sameDay2) return time;
|
|
2681
2915
|
const yesterday = new Date(now);
|
|
2682
2916
|
yesterday.setDate(now.getDate() - 1);
|
|
2683
2917
|
if (isSameDay(d, yesterday)) return `Yesterday ${time}`;
|
|
@@ -2785,12 +3019,26 @@ function ReactionStrip({
|
|
|
2785
3019
|
styles16.chip,
|
|
2786
3020
|
{
|
|
2787
3021
|
backgroundColor: meReacted ? theme.colors.brandSoft : theme.colors.surface2,
|
|
2788
|
-
borderColor: meReacted ? theme.colors.brand : theme.colors.border
|
|
3022
|
+
borderColor: meReacted ? theme.colors.brand : theme.colors.border,
|
|
3023
|
+
borderWidth: meReacted ? 1.5 : 1,
|
|
3024
|
+
transform: meReacted ? [{ scale: 1.04 }] : void 0
|
|
2789
3025
|
}
|
|
2790
3026
|
],
|
|
2791
3027
|
children: [
|
|
2792
3028
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles16.emoji, children: emoji }),
|
|
2793
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3029
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3030
|
+
reactNative.Text,
|
|
3031
|
+
{
|
|
3032
|
+
style: [
|
|
3033
|
+
styles16.count,
|
|
3034
|
+
{
|
|
3035
|
+
color: meReacted ? theme.colors.brand : theme.colors.ink2,
|
|
3036
|
+
fontWeight: meReacted ? "700" : "600"
|
|
3037
|
+
}
|
|
3038
|
+
],
|
|
3039
|
+
children: userArr.length
|
|
3040
|
+
}
|
|
3041
|
+
)
|
|
2794
3042
|
]
|
|
2795
3043
|
},
|
|
2796
3044
|
emoji
|
|
@@ -2843,7 +3091,6 @@ var styles16 = reactNative.StyleSheet.create({
|
|
|
2843
3091
|
paddingHorizontal: 9,
|
|
2844
3092
|
paddingVertical: 4,
|
|
2845
3093
|
borderRadius: 14,
|
|
2846
|
-
borderWidth: 1,
|
|
2847
3094
|
gap: 5,
|
|
2848
3095
|
minHeight: 26,
|
|
2849
3096
|
// Subtle elevation so chips lift off the surface
|
|
@@ -2859,7 +3106,6 @@ var styles16 = reactNative.StyleSheet.create({
|
|
|
2859
3106
|
},
|
|
2860
3107
|
count: {
|
|
2861
3108
|
fontSize: 12,
|
|
2862
|
-
fontWeight: "600",
|
|
2863
3109
|
fontVariant: ["tabular-nums"]
|
|
2864
3110
|
},
|
|
2865
3111
|
backdrop: {
|
|
@@ -2897,11 +3143,10 @@ function MessageRow({
|
|
|
2897
3143
|
actions = true,
|
|
2898
3144
|
threads = true,
|
|
2899
3145
|
quotations = true,
|
|
3146
|
+
markdown = true,
|
|
2900
3147
|
readState,
|
|
2901
|
-
|
|
3148
|
+
isEditing = false,
|
|
2902
3149
|
onStartEdit,
|
|
2903
|
-
onCancelEdit,
|
|
2904
|
-
onSaveEdit,
|
|
2905
3150
|
onDelete,
|
|
2906
3151
|
onOpenThread,
|
|
2907
3152
|
onQuote,
|
|
@@ -2922,7 +3167,7 @@ function MessageRow({
|
|
|
2922
3167
|
...msg.reactions ? { initialReactions: msg.reactions } : {}
|
|
2923
3168
|
});
|
|
2924
3169
|
const translateX = react.useRef(new reactNative.Animated.Value(0)).current;
|
|
2925
|
-
const swipeEnabled = quotations && !!onQuote && !
|
|
3170
|
+
const swipeEnabled = quotations && !!onQuote && !isEditing;
|
|
2926
3171
|
const panResponder = react.useMemo(
|
|
2927
3172
|
() => reactNative.PanResponder.create({
|
|
2928
3173
|
// Tap / long-press still go to the inner Pressable.
|
|
@@ -2997,137 +3242,149 @@ function MessageRow({
|
|
|
2997
3242
|
const rowSender = react$1.useUser(showAvatarSlot ? msg.sender_external_id : null);
|
|
2998
3243
|
const avatarUrl = msg.sender_external_id ? avatarFor?.(msg.sender_external_id) ?? rowSender.profile?.avatarUrl ?? null : null;
|
|
2999
3244
|
const senderLabel = msg.sender_external_id ? labelFor?.(msg.sender_external_id) ?? rowSender.profile?.displayName ?? msg.sender_external_id : null;
|
|
3000
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
reactNative.
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "reply", size: 16, color: theme.colors.brand })
|
|
3025
|
-
}
|
|
3026
|
-
) : null,
|
|
3027
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3028
|
-
reactNative.Animated.View,
|
|
3029
|
-
{
|
|
3030
|
-
...swipeEnabled ? panResponder.panHandlers : {},
|
|
3031
|
-
style: { transform: [{ translateX }] },
|
|
3032
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3033
|
-
reactNative.Pressable,
|
|
3245
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3246
|
+
reactNative.View,
|
|
3247
|
+
{
|
|
3248
|
+
style: [
|
|
3249
|
+
styles17.row,
|
|
3250
|
+
{ alignSelf: isSelf ? "flex-end" : "flex-start" },
|
|
3251
|
+
// Lift this row above the dim backdrop the parent renders
|
|
3252
|
+
// during edit mode. zIndex covers iOS / web; elevation does
|
|
3253
|
+
// the equivalent on Android.
|
|
3254
|
+
isEditing ? styles17.rowEditing : null
|
|
3255
|
+
],
|
|
3256
|
+
children: [
|
|
3257
|
+
showAvatarSlot ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles17.avatarSlot, children: showAvatarRender ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3258
|
+
Avatar,
|
|
3259
|
+
{
|
|
3260
|
+
src: avatarUrl ?? null,
|
|
3261
|
+
name: senderLabel,
|
|
3262
|
+
seed: msg.sender_external_id ?? null,
|
|
3263
|
+
size: "sm"
|
|
3264
|
+
}
|
|
3265
|
+
) : null }) : null,
|
|
3266
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles17.bubbleCol, children: [
|
|
3267
|
+
swipeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3268
|
+
reactNative.Animated.View,
|
|
3034
3269
|
{
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
accessibilityRole: "button",
|
|
3040
|
-
accessibilityLabel: "Message actions",
|
|
3041
|
-
children: editing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3042
|
-
EditableMessageBubble,
|
|
3270
|
+
pointerEvents: "none",
|
|
3271
|
+
style: [
|
|
3272
|
+
styles17.replyHint,
|
|
3273
|
+
isSelf ? styles17.replyHintRight : styles17.replyHintLeft,
|
|
3043
3274
|
{
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
onSave: onSaveEdit ?? (() => void 0)
|
|
3275
|
+
opacity: replyIconOpacity,
|
|
3276
|
+
transform: [{ scale: replyIconScale }],
|
|
3277
|
+
backgroundColor: theme.colors.surface2
|
|
3048
3278
|
}
|
|
3049
|
-
|
|
3050
|
-
|
|
3279
|
+
],
|
|
3280
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "reply", size: 16, color: theme.colors.brand })
|
|
3281
|
+
}
|
|
3282
|
+
) : null,
|
|
3283
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3284
|
+
reactNative.Animated.View,
|
|
3285
|
+
{
|
|
3286
|
+
...swipeEnabled ? panResponder.panHandlers : {},
|
|
3287
|
+
style: { transform: [{ translateX }] },
|
|
3288
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3289
|
+
reactNative.Pressable,
|
|
3051
3290
|
{
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
showAttachments: attachments,
|
|
3061
|
-
actionsTrigger: actions ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => setActionsOpen(true), hitSlop: 8, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3062
|
-
PoolseIcon,
|
|
3291
|
+
onLongPress: () => {
|
|
3292
|
+
if (!actions) return;
|
|
3293
|
+
setActionsOpen(true);
|
|
3294
|
+
},
|
|
3295
|
+
accessibilityRole: "button",
|
|
3296
|
+
accessibilityLabel: "Message actions",
|
|
3297
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3298
|
+
MessageBubble,
|
|
3063
3299
|
{
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3300
|
+
message: msg,
|
|
3301
|
+
currentUserId: meId,
|
|
3302
|
+
...readState ? { readState } : {},
|
|
3303
|
+
...labelFor ? { labelFor } : {},
|
|
3304
|
+
...onQuotedClick ? { onQuotedClick } : {},
|
|
3305
|
+
groupPosition,
|
|
3306
|
+
maxBodyLength,
|
|
3307
|
+
showSenderName,
|
|
3308
|
+
showAttachments: attachments,
|
|
3309
|
+
markdown,
|
|
3310
|
+
actionsTrigger: actions ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => setActionsOpen(true), hitSlop: 8, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3311
|
+
PoolseIcon,
|
|
3312
|
+
{
|
|
3313
|
+
name: "chevron-down",
|
|
3314
|
+
size: 14,
|
|
3315
|
+
color: isSelf ? theme.colors.onBrand : theme.colors.ink3
|
|
3316
|
+
}
|
|
3317
|
+
) }) : null
|
|
3067
3318
|
}
|
|
3068
|
-
)
|
|
3319
|
+
)
|
|
3069
3320
|
}
|
|
3070
3321
|
)
|
|
3071
3322
|
}
|
|
3072
|
-
)
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3323
|
+
),
|
|
3324
|
+
threads && (msg.reply_count ?? 0) > 0 && onOpenThread ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onOpenThread, style: styles17.threadPill, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles17.threadPillText, { color: theme.colors.brand }], children: [
|
|
3325
|
+
"\u{1F4AC} ",
|
|
3326
|
+
msg.reply_count,
|
|
3327
|
+
" ",
|
|
3328
|
+
msg.reply_count === 1 ? "reply" : "replies"
|
|
3329
|
+
] }) }) : null,
|
|
3330
|
+
reactions ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles17.reactionsRow, { alignSelf: isSelf ? "flex-end" : "flex-start" }], children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3331
|
+
ReactionStrip,
|
|
3332
|
+
{
|
|
3333
|
+
messageId: msg.id,
|
|
3334
|
+
conversationId: msg.conversation_id,
|
|
3335
|
+
meId,
|
|
3336
|
+
...msg.reactions ? { initialReactions: msg.reactions } : {}
|
|
3337
|
+
}
|
|
3338
|
+
) }) : null
|
|
3339
|
+
] }),
|
|
3340
|
+
actions && actionsOpen ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3341
|
+
MessageActions,
|
|
3342
|
+
{
|
|
3343
|
+
visible: actionsOpen,
|
|
3344
|
+
onClose: () => setActionsOpen(false),
|
|
3345
|
+
isSelf,
|
|
3346
|
+
...reactions ? {
|
|
3347
|
+
// Inline quick-react row at the top of the sheet —
|
|
3348
|
+
// one tap = react + close, no second modal needed.
|
|
3349
|
+
onPickReaction: (emoji) => {
|
|
3350
|
+
void reactionsApi.addReaction(emoji);
|
|
3351
|
+
}
|
|
3352
|
+
} : {},
|
|
3353
|
+
...msg.body && !msg.deleted_at ? {
|
|
3354
|
+
onCopy: () => {
|
|
3355
|
+
setActionsOpen(false);
|
|
3356
|
+
void copyToClipboard(msg.body ?? "");
|
|
3357
|
+
}
|
|
3358
|
+
} : {},
|
|
3359
|
+
...onStartEdit ? {
|
|
3360
|
+
onEdit: () => {
|
|
3361
|
+
setActionsOpen(false);
|
|
3362
|
+
onStartEdit();
|
|
3363
|
+
}
|
|
3364
|
+
} : {},
|
|
3365
|
+
...onDelete ? {
|
|
3366
|
+
onDelete: () => {
|
|
3367
|
+
setActionsOpen(false);
|
|
3368
|
+
onDelete();
|
|
3369
|
+
}
|
|
3370
|
+
} : {},
|
|
3371
|
+
...onOpenThread ? {
|
|
3372
|
+
onReplyInThread: () => {
|
|
3373
|
+
setActionsOpen(false);
|
|
3374
|
+
onOpenThread();
|
|
3375
|
+
}
|
|
3376
|
+
} : {},
|
|
3377
|
+
...quotations && onQuote ? {
|
|
3378
|
+
onQuote: () => {
|
|
3379
|
+
setActionsOpen(false);
|
|
3380
|
+
onQuote();
|
|
3381
|
+
}
|
|
3382
|
+
} : {}
|
|
3126
3383
|
}
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3384
|
+
) : null
|
|
3385
|
+
]
|
|
3386
|
+
}
|
|
3387
|
+
);
|
|
3131
3388
|
}
|
|
3132
3389
|
var styles17 = reactNative.StyleSheet.create({
|
|
3133
3390
|
row: {
|
|
@@ -3143,6 +3400,12 @@ var styles17 = reactNative.StyleSheet.create({
|
|
|
3143
3400
|
paddingHorizontal: 10,
|
|
3144
3401
|
maxWidth: "95%"
|
|
3145
3402
|
},
|
|
3403
|
+
rowEditing: {
|
|
3404
|
+
// Above ConversationView's editBackdrop (zIndex 5). Elevation
|
|
3405
|
+
// does the equivalent stacking on Android.
|
|
3406
|
+
zIndex: 10,
|
|
3407
|
+
elevation: 10
|
|
3408
|
+
},
|
|
3146
3409
|
avatarSlot: {
|
|
3147
3410
|
width: 36,
|
|
3148
3411
|
marginRight: 6
|
|
@@ -3182,6 +3445,27 @@ var styles17 = reactNative.StyleSheet.create({
|
|
|
3182
3445
|
marginTop: 2
|
|
3183
3446
|
}
|
|
3184
3447
|
});
|
|
3448
|
+
var clipboardModule;
|
|
3449
|
+
var clipboardWarned = false;
|
|
3450
|
+
async function copyToClipboard(text) {
|
|
3451
|
+
if (clipboardModule === void 0) {
|
|
3452
|
+
try {
|
|
3453
|
+
clipboardModule = __require("expo-clipboard");
|
|
3454
|
+
} catch {
|
|
3455
|
+
clipboardModule = null;
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
if (!clipboardModule?.setStringAsync) {
|
|
3459
|
+
if (!clipboardWarned) {
|
|
3460
|
+
clipboardWarned = true;
|
|
3461
|
+
console.warn(
|
|
3462
|
+
"[@poolse/react-native] Copy action requires `expo-clipboard`. Install it to enable copy-to-clipboard."
|
|
3463
|
+
);
|
|
3464
|
+
}
|
|
3465
|
+
return;
|
|
3466
|
+
}
|
|
3467
|
+
await clipboardModule.setStringAsync(text);
|
|
3468
|
+
}
|
|
3185
3469
|
var SHEET_HEIGHT_FRACTION = 0.88;
|
|
3186
3470
|
function ThreadView({
|
|
3187
3471
|
visible,
|
|
@@ -3237,31 +3521,34 @@ function ThreadSheet({
|
|
|
3237
3521
|
const [pickerOpen, setPickerOpen] = react.useState(false);
|
|
3238
3522
|
const [editingId, setEditingId] = react.useState(null);
|
|
3239
3523
|
const renderReply = react.useCallback(
|
|
3240
|
-
(msg) =>
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3524
|
+
(msg) => {
|
|
3525
|
+
const isSelf = meId !== null && msg.sender_id === meId;
|
|
3526
|
+
const canEdit = isSelf && !msg.deleted_at;
|
|
3527
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3528
|
+
MessageRow,
|
|
3529
|
+
{
|
|
3530
|
+
msg,
|
|
3531
|
+
meId,
|
|
3532
|
+
reactions: true,
|
|
3533
|
+
attachments: true,
|
|
3534
|
+
actions: true,
|
|
3535
|
+
threads: false,
|
|
3536
|
+
quotations: false,
|
|
3537
|
+
isEditing: editingId === msg.id,
|
|
3538
|
+
...canEdit ? { onStartEdit: () => setEditingId(msg.id) } : {},
|
|
3539
|
+
onDelete: () => {
|
|
3540
|
+
void deleteReply(msg.id);
|
|
3541
|
+
},
|
|
3542
|
+
...labelFor ? { labelFor } : {},
|
|
3543
|
+
...avatarFor ? { avatarFor } : {}
|
|
3544
|
+
}
|
|
3545
|
+
);
|
|
3546
|
+
},
|
|
3547
|
+
[meId, editingId, deleteReply, labelFor, avatarFor]
|
|
3548
|
+
);
|
|
3549
|
+
const editingMessage = react.useMemo(
|
|
3550
|
+
() => editingId ? replies.find((m) => m.id === editingId) ?? null : null,
|
|
3551
|
+
[editingId, replies]
|
|
3265
3552
|
);
|
|
3266
3553
|
const rootRow = react.useMemo(
|
|
3267
3554
|
() => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3347,7 +3634,14 @@ function ThreadSheet({
|
|
|
3347
3634
|
},
|
|
3348
3635
|
placeholder: "Reply to thread\u2026",
|
|
3349
3636
|
attachments: true,
|
|
3350
|
-
onAttachPress: () => setPickerOpen(true)
|
|
3637
|
+
onAttachPress: () => setPickerOpen(true),
|
|
3638
|
+
editingMessage,
|
|
3639
|
+
onCancelEdit: () => setEditingId(null),
|
|
3640
|
+
onSaveEdit: async (body) => {
|
|
3641
|
+
if (!editingId) return;
|
|
3642
|
+
await edit(editingId, body);
|
|
3643
|
+
setEditingId(null);
|
|
3644
|
+
}
|
|
3351
3645
|
}
|
|
3352
3646
|
)
|
|
3353
3647
|
]
|
|
@@ -3511,6 +3805,7 @@ var styles19 = reactNative.StyleSheet.create({
|
|
|
3511
3805
|
fontSize: 12
|
|
3512
3806
|
}
|
|
3513
3807
|
});
|
|
3808
|
+
var GROUPING_WINDOW_MS = 5 * 60 * 1e3;
|
|
3514
3809
|
function ConversationView({
|
|
3515
3810
|
conversationId,
|
|
3516
3811
|
emptyState,
|
|
@@ -3527,6 +3822,7 @@ function ConversationView({
|
|
|
3527
3822
|
threads = true,
|
|
3528
3823
|
quotations = true,
|
|
3529
3824
|
readReceipts = true,
|
|
3825
|
+
markdown = true,
|
|
3530
3826
|
maxBodyLength = 200,
|
|
3531
3827
|
senderLabels = "auto",
|
|
3532
3828
|
avatars = "auto",
|
|
@@ -3545,9 +3841,25 @@ function ConversationView({
|
|
|
3545
3841
|
send,
|
|
3546
3842
|
edit,
|
|
3547
3843
|
delete: deleteMsg,
|
|
3548
|
-
markReadUpTo
|
|
3844
|
+
markReadUpTo,
|
|
3845
|
+
refetch
|
|
3549
3846
|
} = react$1.useMessages(conversationId);
|
|
3550
3847
|
const { typing, signalTyping } = react$1.useTyping(conversationId);
|
|
3848
|
+
const realtimeStatus = react$1.useRealtimeStatus();
|
|
3849
|
+
react.useEffect(() => {
|
|
3850
|
+
const sub = reactNative.AppState.addEventListener("change", (next) => {
|
|
3851
|
+
if (next === "active") void refetch();
|
|
3852
|
+
});
|
|
3853
|
+
return () => sub.remove();
|
|
3854
|
+
}, [refetch]);
|
|
3855
|
+
const prevStatusRef = react.useRef(realtimeStatus);
|
|
3856
|
+
react.useEffect(() => {
|
|
3857
|
+
const prev = prevStatusRef.current;
|
|
3858
|
+
prevStatusRef.current = realtimeStatus;
|
|
3859
|
+
if (realtimeStatus === "connected" && (prev === "reconnecting" || prev === "closed" || prev === "connecting")) {
|
|
3860
|
+
void refetch();
|
|
3861
|
+
}
|
|
3862
|
+
}, [realtimeStatus, refetch]);
|
|
3551
3863
|
const wantsMembers = mentions || senderLabels === "auto" || avatars === "auto";
|
|
3552
3864
|
const { members } = react$1.useMembers(wantsMembers ? conversationId : "");
|
|
3553
3865
|
const isGroupChat = members.length > 2;
|
|
@@ -3578,10 +3890,24 @@ function ConversationView({
|
|
|
3578
3890
|
});
|
|
3579
3891
|
onMarkedRead?.(conversationId, latest.id);
|
|
3580
3892
|
}, [messages, meId, readReceipts, markReadUpTo, onMarkedRead, conversationId]);
|
|
3893
|
+
const groupPositionById = react.useMemo(() => {
|
|
3894
|
+
const map = /* @__PURE__ */ new Map();
|
|
3895
|
+
for (let i = 0; i < messages.length; i++) {
|
|
3896
|
+
const msg = messages[i];
|
|
3897
|
+
if (!msg) continue;
|
|
3898
|
+
const prev = i > 0 ? messages[i - 1] ?? null : null;
|
|
3899
|
+
const next = i < messages.length - 1 ? messages[i + 1] ?? null : null;
|
|
3900
|
+
map.set(msg.id, react$1.computeGroupPosition(msg, prev, next, GROUPING_WINDOW_MS));
|
|
3901
|
+
}
|
|
3902
|
+
return map;
|
|
3903
|
+
}, [messages]);
|
|
3581
3904
|
const renderItem = react.useCallback(
|
|
3582
3905
|
(msg) => {
|
|
3583
3906
|
if (renderMessage) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderMessage(msg, meId) });
|
|
3584
3907
|
const readState = readReceipts && meId !== null && msg.sender_id === meId ? readStateForMessage(msg, members, meId, sequenceByMessageId) : void 0;
|
|
3908
|
+
const isSelf = meId !== null && msg.sender_id === meId;
|
|
3909
|
+
const canEdit = isSelf && !msg.deleted_at && (readState ?? "sent") === "sent";
|
|
3910
|
+
const groupPosition = groupPositionById.get(msg.id) ?? "standalone";
|
|
3585
3911
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3586
3912
|
MessageRow,
|
|
3587
3913
|
{
|
|
@@ -3593,13 +3919,8 @@ function ConversationView({
|
|
|
3593
3919
|
threads,
|
|
3594
3920
|
quotations,
|
|
3595
3921
|
...readState ? { readState } : {},
|
|
3596
|
-
|
|
3597
|
-
onStartEdit: () => setEditingId(msg.id),
|
|
3598
|
-
onCancelEdit: () => setEditingId(null),
|
|
3599
|
-
onSaveEdit: async (body) => {
|
|
3600
|
-
await edit(msg.id, body);
|
|
3601
|
-
setEditingId(null);
|
|
3602
|
-
},
|
|
3922
|
+
isEditing: editingId === msg.id,
|
|
3923
|
+
...canEdit ? { onStartEdit: () => setEditingId(msg.id) } : {},
|
|
3603
3924
|
onDelete: () => deleteMsg(msg.id),
|
|
3604
3925
|
...threads ? { onOpenThread: () => setThreadRoot(msg) } : {},
|
|
3605
3926
|
...quotations ? { onQuote: () => setReplyingTo(msg) } : {},
|
|
@@ -3607,7 +3928,9 @@ function ConversationView({
|
|
|
3607
3928
|
...avatarFor ? { avatarFor } : {},
|
|
3608
3929
|
showSenderName: showSenderLabels,
|
|
3609
3930
|
showAvatar: showAvatars,
|
|
3610
|
-
maxBodyLength
|
|
3931
|
+
maxBodyLength,
|
|
3932
|
+
groupPosition,
|
|
3933
|
+
markdown
|
|
3611
3934
|
}
|
|
3612
3935
|
);
|
|
3613
3936
|
},
|
|
@@ -3622,16 +3945,38 @@ function ConversationView({
|
|
|
3622
3945
|
threads,
|
|
3623
3946
|
quotations,
|
|
3624
3947
|
editingId,
|
|
3625
|
-
edit,
|
|
3626
3948
|
deleteMsg,
|
|
3627
3949
|
labelFor,
|
|
3628
3950
|
avatarFor,
|
|
3629
3951
|
showSenderLabels,
|
|
3630
3952
|
showAvatars,
|
|
3631
3953
|
maxBodyLength,
|
|
3632
|
-
sequenceByMessageId
|
|
3954
|
+
sequenceByMessageId,
|
|
3955
|
+
groupPositionById,
|
|
3956
|
+
markdown
|
|
3633
3957
|
]
|
|
3634
3958
|
);
|
|
3959
|
+
const editingMessage = react.useMemo(
|
|
3960
|
+
() => editingId ? messages.find((m) => m.id === editingId) ?? null : null,
|
|
3961
|
+
[editingId, messages]
|
|
3962
|
+
);
|
|
3963
|
+
const listItems = react.useMemo(() => {
|
|
3964
|
+
const out = [];
|
|
3965
|
+
let prev = null;
|
|
3966
|
+
for (const msg of messages) {
|
|
3967
|
+
if (prev === null || !react$1.sameDay(msg.inserted_at, prev.inserted_at)) {
|
|
3968
|
+
const label = react$1.formatDayLabel(msg.inserted_at);
|
|
3969
|
+
out.push({
|
|
3970
|
+
kind: "separator",
|
|
3971
|
+
id: `sep-${msg.id}`,
|
|
3972
|
+
node: /* @__PURE__ */ jsxRuntime.jsx(DaySeparator, { label })
|
|
3973
|
+
});
|
|
3974
|
+
}
|
|
3975
|
+
out.push({ kind: "msg", msg });
|
|
3976
|
+
prev = msg;
|
|
3977
|
+
}
|
|
3978
|
+
return out;
|
|
3979
|
+
}, [messages]);
|
|
3635
3980
|
const composerProps = react.useMemo(
|
|
3636
3981
|
() => ({
|
|
3637
3982
|
onSend: async (body, opts) => {
|
|
@@ -3645,9 +3990,28 @@ function ConversationView({
|
|
|
3645
3990
|
onCancelReply: () => setReplyingTo(null),
|
|
3646
3991
|
...labelFor ? { labelFor } : {},
|
|
3647
3992
|
attachments,
|
|
3648
|
-
keyboardOffset
|
|
3993
|
+
keyboardOffset,
|
|
3994
|
+
editingMessage,
|
|
3995
|
+
onCancelEdit: () => setEditingId(null),
|
|
3996
|
+
onSaveEdit: async (body) => {
|
|
3997
|
+
if (!editingId) return;
|
|
3998
|
+
await edit(editingId, body);
|
|
3999
|
+
setEditingId(null);
|
|
4000
|
+
}
|
|
3649
4001
|
}),
|
|
3650
|
-
[
|
|
4002
|
+
[
|
|
4003
|
+
send,
|
|
4004
|
+
signalTyping,
|
|
4005
|
+
attachments,
|
|
4006
|
+
replyingTo,
|
|
4007
|
+
labelFor,
|
|
4008
|
+
keyboardOffset,
|
|
4009
|
+
onSent,
|
|
4010
|
+
conversationId,
|
|
4011
|
+
editingMessage,
|
|
4012
|
+
editingId,
|
|
4013
|
+
edit
|
|
4014
|
+
]
|
|
3651
4015
|
);
|
|
3652
4016
|
const headerNode = header === false ? null : header === true ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3653
4017
|
ChatHeader,
|
|
@@ -3667,18 +4031,30 @@ function ConversationView({
|
|
|
3667
4031
|
keyboardVerticalOffset: keyboardOffset,
|
|
3668
4032
|
children: [
|
|
3669
4033
|
headerNode,
|
|
3670
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
4034
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles20.listWrap, children: [
|
|
4035
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4036
|
+
MessageList,
|
|
4037
|
+
{
|
|
4038
|
+
messages,
|
|
4039
|
+
items: listItems,
|
|
4040
|
+
meId,
|
|
4041
|
+
loading,
|
|
4042
|
+
hasMore,
|
|
4043
|
+
onLoadMore: loadMore,
|
|
4044
|
+
renderItem,
|
|
4045
|
+
ListHeader: /* @__PURE__ */ jsxRuntime.jsx(TypingIndicator, { typing, ...labelFor ? { labelFor } : {} }),
|
|
4046
|
+
...emptyState !== void 0 ? { emptyState } : {}
|
|
4047
|
+
}
|
|
4048
|
+
),
|
|
4049
|
+
editingId ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4050
|
+
reactNative.Pressable,
|
|
4051
|
+
{
|
|
4052
|
+
style: styles20.editBackdrop,
|
|
4053
|
+
onPress: () => setEditingId(null),
|
|
4054
|
+
accessibilityLabel: "Cancel editing"
|
|
4055
|
+
}
|
|
4056
|
+
) : null
|
|
4057
|
+
] }),
|
|
3682
4058
|
mentions ? /* @__PURE__ */ jsxRuntime.jsx(MentionInput, { conversationId, ...composerProps }) : /* @__PURE__ */ jsxRuntime.jsx(MessageComposer, { ...composerProps }),
|
|
3683
4059
|
attachments ? /* @__PURE__ */ jsxRuntime.jsx(AttachmentPicker, { visible: pickerOpen, onClose: () => setPickerOpen(false) }) : null,
|
|
3684
4060
|
threads && threadRoot ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3714,6 +4090,14 @@ var styles20 = reactNative.StyleSheet.create({
|
|
|
3714
4090
|
},
|
|
3715
4091
|
listWrap: {
|
|
3716
4092
|
flex: 1
|
|
4093
|
+
},
|
|
4094
|
+
editBackdrop: {
|
|
4095
|
+
...reactNative.StyleSheet.absoluteFillObject,
|
|
4096
|
+
backgroundColor: "rgba(0,0,0,0.45)",
|
|
4097
|
+
// Sit above message rows by default but below any row that
|
|
4098
|
+
// explicitly elevates itself (MessageRow bumps zIndex/elevation
|
|
4099
|
+
// when isEditing is true).
|
|
4100
|
+
zIndex: 5
|
|
3717
4101
|
}
|
|
3718
4102
|
});
|
|
3719
4103
|
function UserPickerSheet({
|
|
@@ -4578,7 +4962,7 @@ exports.Avatar = Avatar;
|
|
|
4578
4962
|
exports.ChatHeader = ChatHeader;
|
|
4579
4963
|
exports.ConversationList = ConversationList;
|
|
4580
4964
|
exports.ConversationView = ConversationView;
|
|
4581
|
-
exports.
|
|
4965
|
+
exports.DaySeparator = DaySeparator;
|
|
4582
4966
|
exports.GroupDetailsSheet = GroupDetailsSheet;
|
|
4583
4967
|
exports.ImageMosaic = ImageMosaic;
|
|
4584
4968
|
exports.MemberList = MemberList;
|