@poolse/react-native 2.0.13 → 2.1.1
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 +742 -375
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -18
- package/dist/index.d.ts +90 -18
- package/dist/index.js +741 -378
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import { useUser, useMe, useConversations, useAttachmentUpload, useConversation, useMembers, usePresence, useMessages, useTyping, useReactions, useThread, useAttachmentUrl, usePoolse } from '@poolse/react';
|
|
2
|
-
import { createContext, forwardRef, useRef, useState,
|
|
3
|
-
import { StyleSheet, View, Pressable, TextInput, Text, useWindowDimensions, Animated, Easing, PanResponder, Image, ScrollView, ActivityIndicator, FlatList,
|
|
1
|
+
import { useUser, useMe, useConversations, useAttachmentUpload, useConversation, useMembers, usePresence, useMessages, useTyping, useRealtimeStatus, computeGroupPosition, formatDayLabel, useReactions, sameDay, useThread, useAttachmentUrl, usePoolse } from '@poolse/react';
|
|
2
|
+
import { createContext, forwardRef, useRef, useState, useEffect, useImperativeHandle, useMemo, useCallback, useContext, createElement } from 'react';
|
|
3
|
+
import { StyleSheet, Platform, Keyboard, View, Pressable, TextInput, Text, useWindowDimensions, Animated, Easing, PanResponder, Image, ScrollView, ActivityIndicator, FlatList, AppState, KeyboardAvoidingView, Modal, Dimensions, Linking } from 'react-native';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { SvgXml, Svg, Path, Circle, G } from 'react-native-svg';
|
|
6
6
|
import * as ImagePicker from 'expo-image-picker';
|
|
7
7
|
import * as DocumentPicker from 'expo-document-picker';
|
|
8
8
|
import { manipulateAsync, SaveFormat } from 'expo-image-manipulator';
|
|
9
|
+
import * as Clipboard from 'expo-clipboard';
|
|
10
|
+
import Markdown from 'react-native-markdown-display';
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
13
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
14
|
+
}) : x)(function(x) {
|
|
15
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
16
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
17
|
+
});
|
|
11
18
|
|
|
12
19
|
// src/theme/default.ts
|
|
13
20
|
var lightColors = {
|
|
@@ -288,6 +295,10 @@ var ICONS = {
|
|
|
288
295
|
vb: "0 0 24 24",
|
|
289
296
|
d: '<path d="M6 9.2a6 6 0 0 1 12 0c0 4.8 1.4 6.3 1.9 6.8H4.1C4.6 15.5 6 14 6 9.2Z"/><path d="M9.6 19.8a2.5 2.5 0 0 0 4.8 0"/>'
|
|
290
297
|
},
|
|
298
|
+
camera: {
|
|
299
|
+
vb: "0 0 24 24",
|
|
300
|
+
d: '<path d="M3 8.5a2 2 0 0 1 2-2h2.4l1.4-2h6.4l1.4 2H19a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-9Z"/><circle cx="12" cy="13" r="3.5"/>'
|
|
301
|
+
},
|
|
291
302
|
"check-double": {
|
|
292
303
|
vb: "0 0 24 24",
|
|
293
304
|
d: '<path d="M2.5 12.8 6.9 17 15.4 7.6"/><path d="M11.4 16.4 12.3 17.2 21 7.6"/>'
|
|
@@ -1107,7 +1118,12 @@ function useUploadPreview() {
|
|
|
1107
1118
|
getPreview: ctx?.getPreview ?? (() => null)
|
|
1108
1119
|
};
|
|
1109
1120
|
}
|
|
1110
|
-
function AttachmentPicker({
|
|
1121
|
+
function AttachmentPicker({
|
|
1122
|
+
visible,
|
|
1123
|
+
onClose,
|
|
1124
|
+
onPickInputs,
|
|
1125
|
+
camera = true
|
|
1126
|
+
}) {
|
|
1111
1127
|
const theme = usePoolseTheme();
|
|
1112
1128
|
const upload = useSharedUpload();
|
|
1113
1129
|
const { setPreview } = useUploadPreview();
|
|
@@ -1141,8 +1157,12 @@ function AttachmentPicker({ visible, onClose, onPickInputs }) {
|
|
|
1141
1157
|
theme.shadows.lg
|
|
1142
1158
|
],
|
|
1143
1159
|
children: [
|
|
1160
|
+
camera ? /* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("camera"), children: [
|
|
1161
|
+
/* @__PURE__ */ jsx(PoolseIcon, { name: "camera", size: 20, color: theme.colors.ink2 }),
|
|
1162
|
+
/* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Camera" })
|
|
1163
|
+
] }) : null,
|
|
1144
1164
|
/* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("image"), children: [
|
|
1145
|
-
/* @__PURE__ */ jsx(PoolseIcon, { name: "
|
|
1165
|
+
/* @__PURE__ */ jsx(PoolseIcon, { name: "image", size: 20, color: theme.colors.ink2 }),
|
|
1146
1166
|
/* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Photo or video" })
|
|
1147
1167
|
] }),
|
|
1148
1168
|
/* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("document"), children: [
|
|
@@ -1201,7 +1221,52 @@ async function compressImage({
|
|
|
1201
1221
|
filename: `${base}.webp`
|
|
1202
1222
|
};
|
|
1203
1223
|
}
|
|
1224
|
+
async function assetsToInputs(assets) {
|
|
1225
|
+
return Promise.all(
|
|
1226
|
+
assets.map(async (asset) => {
|
|
1227
|
+
try {
|
|
1228
|
+
const compressed = await compressImage({
|
|
1229
|
+
uri: asset.uri,
|
|
1230
|
+
width: asset.width,
|
|
1231
|
+
height: asset.height,
|
|
1232
|
+
filename: asset.fileName ?? `image-${Date.now()}`
|
|
1233
|
+
});
|
|
1234
|
+
return {
|
|
1235
|
+
body: compressed.blob,
|
|
1236
|
+
contentType: compressed.contentType,
|
|
1237
|
+
byteSize: compressed.blob.size,
|
|
1238
|
+
filename: compressed.filename,
|
|
1239
|
+
_previewUri: asset.uri
|
|
1240
|
+
};
|
|
1241
|
+
} catch (err) {
|
|
1242
|
+
console.warn("[poolse/picker] image compression failed, sending original:", err);
|
|
1243
|
+
const blob = await uriToBlob(asset.uri);
|
|
1244
|
+
const contentType = asset.mimeType ?? blob.type ?? "image/jpeg";
|
|
1245
|
+
return {
|
|
1246
|
+
body: blob,
|
|
1247
|
+
contentType,
|
|
1248
|
+
byteSize: asset.fileSize ?? blob.size,
|
|
1249
|
+
filename: asset.fileName ?? `image-${Date.now()}.jpg`,
|
|
1250
|
+
_previewUri: asset.uri
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
})
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1204
1256
|
async function defaultAdapter(kind) {
|
|
1257
|
+
if (kind === "camera") {
|
|
1258
|
+
const perm = await ImagePicker.requestCameraPermissionsAsync();
|
|
1259
|
+
if (!perm.granted) {
|
|
1260
|
+
console.warn("[poolse/picker] camera permission denied");
|
|
1261
|
+
return [];
|
|
1262
|
+
}
|
|
1263
|
+
const res2 = await ImagePicker.launchCameraAsync({
|
|
1264
|
+
mediaTypes: "images",
|
|
1265
|
+
quality: 1
|
|
1266
|
+
});
|
|
1267
|
+
if (res2.canceled) return [];
|
|
1268
|
+
return assetsToInputs(res2.assets);
|
|
1269
|
+
}
|
|
1205
1270
|
if (kind === "image") {
|
|
1206
1271
|
const res2 = await ImagePicker.launchImageLibraryAsync({
|
|
1207
1272
|
mediaTypes: "images",
|
|
@@ -1211,36 +1276,7 @@ async function defaultAdapter(kind) {
|
|
|
1211
1276
|
quality: 1
|
|
1212
1277
|
});
|
|
1213
1278
|
if (res2.canceled) return [];
|
|
1214
|
-
return
|
|
1215
|
-
res2.assets.map(async (asset) => {
|
|
1216
|
-
try {
|
|
1217
|
-
const compressed = await compressImage({
|
|
1218
|
-
uri: asset.uri,
|
|
1219
|
-
width: asset.width,
|
|
1220
|
-
height: asset.height,
|
|
1221
|
-
filename: asset.fileName ?? `image-${Date.now()}`
|
|
1222
|
-
});
|
|
1223
|
-
return {
|
|
1224
|
-
body: compressed.blob,
|
|
1225
|
-
contentType: compressed.contentType,
|
|
1226
|
-
byteSize: compressed.blob.size,
|
|
1227
|
-
filename: compressed.filename,
|
|
1228
|
-
_previewUri: asset.uri
|
|
1229
|
-
};
|
|
1230
|
-
} catch (err) {
|
|
1231
|
-
console.warn("[poolse/picker] image compression failed, sending original:", err);
|
|
1232
|
-
const blob = await uriToBlob(asset.uri);
|
|
1233
|
-
const contentType = asset.mimeType ?? blob.type ?? "image/jpeg";
|
|
1234
|
-
return {
|
|
1235
|
-
body: blob,
|
|
1236
|
-
contentType,
|
|
1237
|
-
byteSize: asset.fileSize ?? blob.size,
|
|
1238
|
-
filename: asset.fileName ?? `image-${Date.now()}.jpg`,
|
|
1239
|
-
_previewUri: asset.uri
|
|
1240
|
-
};
|
|
1241
|
-
}
|
|
1242
|
-
})
|
|
1243
|
-
);
|
|
1279
|
+
return assetsToInputs(res2.assets);
|
|
1244
1280
|
}
|
|
1245
1281
|
const res = await DocumentPicker.getDocumentAsync({
|
|
1246
1282
|
copyToCacheDirectory: true,
|
|
@@ -1307,6 +1343,51 @@ var styles6 = StyleSheet.create({
|
|
|
1307
1343
|
fontWeight: "500"
|
|
1308
1344
|
}
|
|
1309
1345
|
});
|
|
1346
|
+
function DaySeparator({ label }) {
|
|
1347
|
+
const theme = usePoolseTheme();
|
|
1348
|
+
return /* @__PURE__ */ jsx(View, { style: styles7.wrap, children: /* @__PURE__ */ jsx(
|
|
1349
|
+
View,
|
|
1350
|
+
{
|
|
1351
|
+
style: [
|
|
1352
|
+
styles7.chip,
|
|
1353
|
+
{
|
|
1354
|
+
backgroundColor: theme.colors.surface2,
|
|
1355
|
+
borderColor: theme.colors.border
|
|
1356
|
+
}
|
|
1357
|
+
],
|
|
1358
|
+
children: /* @__PURE__ */ jsx(
|
|
1359
|
+
Text,
|
|
1360
|
+
{
|
|
1361
|
+
style: [
|
|
1362
|
+
styles7.label,
|
|
1363
|
+
{
|
|
1364
|
+
color: theme.colors.ink2,
|
|
1365
|
+
fontFamily: theme.type.fontBody
|
|
1366
|
+
}
|
|
1367
|
+
],
|
|
1368
|
+
children: label
|
|
1369
|
+
}
|
|
1370
|
+
)
|
|
1371
|
+
}
|
|
1372
|
+
) });
|
|
1373
|
+
}
|
|
1374
|
+
var styles7 = StyleSheet.create({
|
|
1375
|
+
wrap: {
|
|
1376
|
+
alignItems: "center",
|
|
1377
|
+
marginVertical: 8
|
|
1378
|
+
},
|
|
1379
|
+
chip: {
|
|
1380
|
+
paddingHorizontal: 12,
|
|
1381
|
+
paddingVertical: 4,
|
|
1382
|
+
borderRadius: 999,
|
|
1383
|
+
borderWidth: StyleSheet.hairlineWidth
|
|
1384
|
+
},
|
|
1385
|
+
label: {
|
|
1386
|
+
fontSize: 11,
|
|
1387
|
+
fontWeight: "600",
|
|
1388
|
+
letterSpacing: 0.3
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1310
1391
|
function UploadQueueStrip() {
|
|
1311
1392
|
const theme = usePoolseTheme();
|
|
1312
1393
|
const upload = useSharedUpload();
|
|
@@ -1317,8 +1398,8 @@ function UploadQueueStrip() {
|
|
|
1317
1398
|
{
|
|
1318
1399
|
horizontal: true,
|
|
1319
1400
|
showsHorizontalScrollIndicator: false,
|
|
1320
|
-
contentContainerStyle:
|
|
1321
|
-
style: [
|
|
1401
|
+
contentContainerStyle: styles8.scroll,
|
|
1402
|
+
style: [styles8.scrollStyle, { marginBottom: 8 }],
|
|
1322
1403
|
children: upload.queue.map((item) => {
|
|
1323
1404
|
const isError = item.status === "error";
|
|
1324
1405
|
const isUploading = item.status === "uploading" || item.status === "pending";
|
|
@@ -1329,7 +1410,7 @@ function UploadQueueStrip() {
|
|
|
1329
1410
|
if (item.status === "ready" || isError) upload.remove(item.localId);
|
|
1330
1411
|
else upload.cancel(item.localId);
|
|
1331
1412
|
};
|
|
1332
|
-
return /* @__PURE__ */ jsxs(View, { style:
|
|
1413
|
+
return /* @__PURE__ */ jsxs(View, { style: styles8.tileWrap, children: [
|
|
1333
1414
|
isImage && previewUri ? /* @__PURE__ */ jsx(ImageTile, { uri: previewUri, uploading: isUploading, error: isError }) : /* @__PURE__ */ jsx(
|
|
1334
1415
|
FileTile,
|
|
1335
1416
|
{
|
|
@@ -1344,13 +1425,13 @@ function UploadQueueStrip() {
|
|
|
1344
1425
|
{
|
|
1345
1426
|
onPress: handleClose,
|
|
1346
1427
|
hitSlop: 8,
|
|
1347
|
-
style: [
|
|
1428
|
+
style: [styles8.closeBtn, { backgroundColor: theme.colors.ink }],
|
|
1348
1429
|
accessibilityLabel: isError ? "Remove failed upload" : "Cancel upload",
|
|
1349
1430
|
children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 11, color: theme.colors.onBrand })
|
|
1350
1431
|
}
|
|
1351
1432
|
) : null,
|
|
1352
|
-
isUploading ? /* @__PURE__ */ jsx(View, { style:
|
|
1353
|
-
isError ? /* @__PURE__ */ jsx(View, { style: [
|
|
1433
|
+
isUploading ? /* @__PURE__ */ jsx(View, { style: styles8.progressOverlay, children: /* @__PURE__ */ jsx(View, { style: [styles8.spinner, { borderColor: theme.colors.onBrand }] }) }) : null,
|
|
1434
|
+
isError ? /* @__PURE__ */ jsx(View, { style: [styles8.errorOverlay, { backgroundColor: "rgba(229,72,77,0.85)" }], children: /* @__PURE__ */ jsx(Text, { style: [styles8.errorText, { color: "#fff" }], children: "Failed" }) }) : null
|
|
1354
1435
|
] }, item.localId);
|
|
1355
1436
|
})
|
|
1356
1437
|
}
|
|
@@ -1363,7 +1444,7 @@ function ImageTile({ uri, uploading, error }) {
|
|
|
1363
1444
|
{
|
|
1364
1445
|
source: { uri },
|
|
1365
1446
|
style: [
|
|
1366
|
-
|
|
1447
|
+
styles8.imageTile,
|
|
1367
1448
|
{
|
|
1368
1449
|
backgroundColor: theme.colors.surface2,
|
|
1369
1450
|
borderColor: error ? theme.colors.error : "transparent",
|
|
@@ -1385,24 +1466,24 @@ function FileTile({
|
|
|
1385
1466
|
View,
|
|
1386
1467
|
{
|
|
1387
1468
|
style: [
|
|
1388
|
-
|
|
1469
|
+
styles8.fileTile,
|
|
1389
1470
|
{
|
|
1390
1471
|
backgroundColor: theme.colors.surface2,
|
|
1391
1472
|
borderColor: error ? theme.colors.error : uploading ? theme.colors.brand : theme.colors.border
|
|
1392
1473
|
}
|
|
1393
1474
|
],
|
|
1394
1475
|
children: [
|
|
1395
|
-
/* @__PURE__ */ jsx(View, { style: [
|
|
1396
|
-
/* @__PURE__ */ jsxs(View, { style:
|
|
1476
|
+
/* @__PURE__ */ jsx(View, { style: [styles8.fileIconBox, { backgroundColor: theme.colors.brandSoft }], children: /* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 18, color: theme.colors.brand }) }),
|
|
1477
|
+
/* @__PURE__ */ jsxs(View, { style: styles8.fileInfo, children: [
|
|
1397
1478
|
/* @__PURE__ */ jsx(
|
|
1398
1479
|
Text,
|
|
1399
1480
|
{
|
|
1400
|
-
style: [
|
|
1481
|
+
style: [styles8.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
|
|
1401
1482
|
numberOfLines: 1,
|
|
1402
1483
|
children: filename
|
|
1403
1484
|
}
|
|
1404
1485
|
),
|
|
1405
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
1486
|
+
/* @__PURE__ */ jsx(Text, { style: [styles8.fileSize, { color: theme.colors.ink3 }], children: formatBytes(size) })
|
|
1406
1487
|
] })
|
|
1407
1488
|
]
|
|
1408
1489
|
}
|
|
@@ -1416,7 +1497,7 @@ function formatBytes(n) {
|
|
|
1416
1497
|
return `${(n / 1024 / 1024 / 1024).toFixed(1)} GB`;
|
|
1417
1498
|
}
|
|
1418
1499
|
var TILE = 64;
|
|
1419
|
-
var
|
|
1500
|
+
var styles8 = StyleSheet.create({
|
|
1420
1501
|
scrollStyle: {
|
|
1421
1502
|
overflow: "visible"
|
|
1422
1503
|
},
|
|
@@ -1516,6 +1597,22 @@ var styles7 = StyleSheet.create({
|
|
|
1516
1597
|
fontWeight: "700"
|
|
1517
1598
|
}
|
|
1518
1599
|
});
|
|
1600
|
+
var FALLBACK = {
|
|
1601
|
+
top: Platform.OS === "ios" ? 50 : 24,
|
|
1602
|
+
bottom: Platform.OS === "ios" ? 34 : 0,
|
|
1603
|
+
left: 0,
|
|
1604
|
+
right: 0
|
|
1605
|
+
};
|
|
1606
|
+
var realHook = null;
|
|
1607
|
+
try {
|
|
1608
|
+
const mod = __require("react-native-safe-area-context");
|
|
1609
|
+
if (mod && typeof mod.useSafeAreaInsets === "function") {
|
|
1610
|
+
realHook = mod.useSafeAreaInsets;
|
|
1611
|
+
}
|
|
1612
|
+
} catch {
|
|
1613
|
+
}
|
|
1614
|
+
var useFallback = () => FALLBACK;
|
|
1615
|
+
var useSafeInsets = realHook ?? useFallback;
|
|
1519
1616
|
var MessageComposer = forwardRef(
|
|
1520
1617
|
function MessageComposer2({
|
|
1521
1618
|
onSend,
|
|
@@ -1527,13 +1624,32 @@ var MessageComposer = forwardRef(
|
|
|
1527
1624
|
labelFor,
|
|
1528
1625
|
attachments = true,
|
|
1529
1626
|
onAttachPress,
|
|
1530
|
-
keyboardOffset: _keyboardOffset = 0
|
|
1627
|
+
keyboardOffset: _keyboardOffset = 0,
|
|
1628
|
+
editingMessage = null,
|
|
1629
|
+
onCancelEdit,
|
|
1630
|
+
onSaveEdit
|
|
1531
1631
|
}, ref) {
|
|
1532
1632
|
const theme = usePoolseTheme();
|
|
1533
1633
|
const inputRef = useRef(null);
|
|
1534
1634
|
const [value, setValue] = useState("");
|
|
1535
1635
|
const [sending, setSending] = useState(false);
|
|
1636
|
+
const [keyboardOpen, setKeyboardOpen] = useState(false);
|
|
1536
1637
|
const upload = useSharedUpload();
|
|
1638
|
+
const insets = useSafeInsets();
|
|
1639
|
+
useEffect(() => {
|
|
1640
|
+
const show = Keyboard.addListener(
|
|
1641
|
+
Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow",
|
|
1642
|
+
() => setKeyboardOpen(true)
|
|
1643
|
+
);
|
|
1644
|
+
const hide = Keyboard.addListener(
|
|
1645
|
+
Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide",
|
|
1646
|
+
() => setKeyboardOpen(false)
|
|
1647
|
+
);
|
|
1648
|
+
return () => {
|
|
1649
|
+
show.remove();
|
|
1650
|
+
hide.remove();
|
|
1651
|
+
};
|
|
1652
|
+
}, []);
|
|
1537
1653
|
useImperativeHandle(
|
|
1538
1654
|
ref,
|
|
1539
1655
|
() => ({
|
|
@@ -1552,36 +1668,65 @@ var MessageComposer = forwardRef(
|
|
|
1552
1668
|
const isUploading = upload.queue.some(
|
|
1553
1669
|
(item) => item.status === "uploading" || item.status === "pending"
|
|
1554
1670
|
);
|
|
1555
|
-
const
|
|
1671
|
+
const previousDraftRef = useRef(null);
|
|
1672
|
+
const editingIdRef = useRef(null);
|
|
1673
|
+
useEffect(() => {
|
|
1674
|
+
const id = editingMessage?.id ?? null;
|
|
1675
|
+
if (id === editingIdRef.current) return;
|
|
1676
|
+
if (id) {
|
|
1677
|
+
previousDraftRef.current = value;
|
|
1678
|
+
setValue(editingMessage?.body ?? "");
|
|
1679
|
+
inputRef.current?.focus();
|
|
1680
|
+
} else {
|
|
1681
|
+
setValue(previousDraftRef.current ?? "");
|
|
1682
|
+
previousDraftRef.current = null;
|
|
1683
|
+
}
|
|
1684
|
+
editingIdRef.current = id;
|
|
1685
|
+
}, [editingMessage?.id, editingMessage?.body]);
|
|
1686
|
+
const isEditing = !!editingMessage;
|
|
1687
|
+
const editChanged = isEditing && value.trim() !== (editingMessage?.body?.trim() ?? "");
|
|
1688
|
+
const submitDisabled = disabled || sending || isUploading || value.trim().length === 0 || (isEditing ? !editChanged : value.trim().length === 0 && readyAttachmentIds.length === 0);
|
|
1556
1689
|
const handleSend = async () => {
|
|
1557
1690
|
if (submitDisabled) return;
|
|
1558
1691
|
const body = value.trim();
|
|
1559
|
-
const replyId = replyingTo?.id;
|
|
1560
|
-
const attachmentIds = readyAttachmentIds;
|
|
1561
|
-
setValue("");
|
|
1562
|
-
upload.reset();
|
|
1563
1692
|
setSending(true);
|
|
1564
1693
|
try {
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1694
|
+
if (isEditing && onSaveEdit) {
|
|
1695
|
+
await onSaveEdit(body);
|
|
1696
|
+
} else {
|
|
1697
|
+
const replyId = replyingTo?.id;
|
|
1698
|
+
const attachmentIds = readyAttachmentIds;
|
|
1699
|
+
setValue("");
|
|
1700
|
+
upload.reset();
|
|
1701
|
+
await onSend(body, {
|
|
1702
|
+
...replyId ? { quoted_message_id: replyId } : {},
|
|
1703
|
+
...attachmentIds.length > 0 ? { attachment_ids: attachmentIds } : {}
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1569
1706
|
} finally {
|
|
1570
1707
|
setSending(false);
|
|
1571
1708
|
}
|
|
1572
1709
|
};
|
|
1710
|
+
const paddingBottom = keyboardOpen ? 10 : Math.max(10, insets.bottom);
|
|
1573
1711
|
return /* @__PURE__ */ jsxs(
|
|
1574
1712
|
View,
|
|
1575
1713
|
{
|
|
1576
1714
|
style: [
|
|
1577
|
-
|
|
1715
|
+
styles9.wrap,
|
|
1578
1716
|
{
|
|
1579
1717
|
backgroundColor: theme.colors.surface,
|
|
1580
|
-
borderTopColor: theme.colors.border
|
|
1718
|
+
borderTopColor: theme.colors.border,
|
|
1719
|
+
paddingBottom
|
|
1581
1720
|
}
|
|
1582
1721
|
],
|
|
1583
1722
|
children: [
|
|
1584
|
-
|
|
1723
|
+
isEditing ? /* @__PURE__ */ jsx(
|
|
1724
|
+
EditChip,
|
|
1725
|
+
{
|
|
1726
|
+
message: editingMessage,
|
|
1727
|
+
...onCancelEdit ? { onCancel: onCancelEdit } : {}
|
|
1728
|
+
}
|
|
1729
|
+
) : replyingTo ? /* @__PURE__ */ jsx(
|
|
1585
1730
|
ReplyChip,
|
|
1586
1731
|
{
|
|
1587
1732
|
message: replyingTo,
|
|
@@ -1589,14 +1734,14 @@ var MessageComposer = forwardRef(
|
|
|
1589
1734
|
...onCancelReply ? { onCancel: onCancelReply } : {}
|
|
1590
1735
|
}
|
|
1591
1736
|
) : null,
|
|
1592
|
-
attachments ? /* @__PURE__ */ jsx(UploadQueueStrip, {}) : null,
|
|
1593
|
-
/* @__PURE__ */ jsxs(View, { style:
|
|
1737
|
+
attachments && !isEditing ? /* @__PURE__ */ jsx(UploadQueueStrip, {}) : null,
|
|
1738
|
+
/* @__PURE__ */ jsxs(View, { style: styles9.row, children: [
|
|
1594
1739
|
attachments && onAttachPress ? /* @__PURE__ */ jsx(
|
|
1595
1740
|
Pressable,
|
|
1596
1741
|
{
|
|
1597
1742
|
onPress: onAttachPress,
|
|
1598
1743
|
hitSlop: 8,
|
|
1599
|
-
style:
|
|
1744
|
+
style: styles9.iconBtn,
|
|
1600
1745
|
accessibilityLabel: "Attach file",
|
|
1601
1746
|
children: /* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 22, color: theme.colors.ink2 })
|
|
1602
1747
|
}
|
|
@@ -1615,7 +1760,7 @@ var MessageComposer = forwardRef(
|
|
|
1615
1760
|
multiline: true,
|
|
1616
1761
|
editable: !disabled,
|
|
1617
1762
|
style: [
|
|
1618
|
-
|
|
1763
|
+
styles9.input,
|
|
1619
1764
|
{
|
|
1620
1765
|
color: theme.colors.ink,
|
|
1621
1766
|
backgroundColor: theme.colors.surface2,
|
|
@@ -1632,7 +1777,7 @@ var MessageComposer = forwardRef(
|
|
|
1632
1777
|
onPress: handleSend,
|
|
1633
1778
|
disabled: submitDisabled,
|
|
1634
1779
|
style: [
|
|
1635
|
-
|
|
1780
|
+
styles9.sendBtn,
|
|
1636
1781
|
{
|
|
1637
1782
|
backgroundColor: submitDisabled ? theme.colors.ink3 : theme.colors.brand,
|
|
1638
1783
|
opacity: submitDisabled ? 0.6 : 1
|
|
@@ -1648,6 +1793,28 @@ var MessageComposer = forwardRef(
|
|
|
1648
1793
|
);
|
|
1649
1794
|
}
|
|
1650
1795
|
);
|
|
1796
|
+
function EditChip({ message, onCancel }) {
|
|
1797
|
+
const theme = usePoolseTheme();
|
|
1798
|
+
return /* @__PURE__ */ jsxs(
|
|
1799
|
+
View,
|
|
1800
|
+
{
|
|
1801
|
+
style: [
|
|
1802
|
+
styles9.replyChip,
|
|
1803
|
+
{
|
|
1804
|
+
backgroundColor: theme.colors.surface2,
|
|
1805
|
+
borderLeftColor: theme.colors.brand
|
|
1806
|
+
}
|
|
1807
|
+
],
|
|
1808
|
+
children: [
|
|
1809
|
+
/* @__PURE__ */ jsxs(View, { style: { flex: 1 }, children: [
|
|
1810
|
+
/* @__PURE__ */ jsx(Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: "Editing message" }),
|
|
1811
|
+
/* @__PURE__ */ jsx(Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
|
|
1812
|
+
] }),
|
|
1813
|
+
onCancel ? /* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, style: styles9.replyCancel, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
|
|
1814
|
+
]
|
|
1815
|
+
}
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1651
1818
|
function ReplyChip({
|
|
1652
1819
|
message,
|
|
1653
1820
|
labelFor,
|
|
@@ -1660,7 +1827,7 @@ function ReplyChip({
|
|
|
1660
1827
|
View,
|
|
1661
1828
|
{
|
|
1662
1829
|
style: [
|
|
1663
|
-
|
|
1830
|
+
styles9.replyChip,
|
|
1664
1831
|
{
|
|
1665
1832
|
backgroundColor: theme.colors.surface2,
|
|
1666
1833
|
borderLeftColor: theme.colors.brand
|
|
@@ -1668,27 +1835,26 @@ function ReplyChip({
|
|
|
1668
1835
|
],
|
|
1669
1836
|
children: [
|
|
1670
1837
|
/* @__PURE__ */ jsxs(View, { style: { flex: 1 }, children: [
|
|
1671
|
-
/* @__PURE__ */ jsxs(Text, { style: [
|
|
1838
|
+
/* @__PURE__ */ jsxs(Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: [
|
|
1672
1839
|
"Replying to ",
|
|
1673
1840
|
senderName
|
|
1674
1841
|
] }),
|
|
1675
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
1842
|
+
/* @__PURE__ */ jsx(Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
|
|
1676
1843
|
] }),
|
|
1677
|
-
onCancel ? /* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, style:
|
|
1844
|
+
onCancel ? /* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, style: styles9.replyCancel, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
|
|
1678
1845
|
]
|
|
1679
1846
|
}
|
|
1680
1847
|
);
|
|
1681
1848
|
}
|
|
1682
|
-
var
|
|
1849
|
+
var styles9 = StyleSheet.create({
|
|
1683
1850
|
wrap: {
|
|
1684
1851
|
borderTopWidth: StyleSheet.hairlineWidth,
|
|
1685
1852
|
paddingTop: 8,
|
|
1686
|
-
paddingBottom: 10,
|
|
1687
1853
|
paddingHorizontal: 12
|
|
1688
|
-
//
|
|
1689
|
-
//
|
|
1690
|
-
//
|
|
1691
|
-
//
|
|
1854
|
+
// paddingBottom is applied inline — it follows the keyboard state
|
|
1855
|
+
// (10 px while open, max(10, safeBottomInset) while closed) so the
|
|
1856
|
+
// send/attach buttons stay clear of the home indicator without
|
|
1857
|
+
// double-padding above the keyboard.
|
|
1692
1858
|
},
|
|
1693
1859
|
row: {
|
|
1694
1860
|
flexDirection: "row",
|
|
@@ -1777,11 +1943,11 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
|
|
|
1777
1943
|
transparent: true,
|
|
1778
1944
|
animationType: "slide",
|
|
1779
1945
|
onRequestClose: () => setPickerOpen(false),
|
|
1780
|
-
children: /* @__PURE__ */ jsx(Pressable, { style:
|
|
1946
|
+
children: /* @__PURE__ */ jsx(Pressable, { style: styles10.backdrop, onPress: () => setPickerOpen(false), children: /* @__PURE__ */ jsxs(
|
|
1781
1947
|
View,
|
|
1782
1948
|
{
|
|
1783
1949
|
style: [
|
|
1784
|
-
|
|
1950
|
+
styles10.sheet,
|
|
1785
1951
|
{
|
|
1786
1952
|
backgroundColor: theme.colors.surface,
|
|
1787
1953
|
borderTopLeftRadius: theme.radii.lg,
|
|
@@ -1792,7 +1958,7 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
|
|
|
1792
1958
|
/* @__PURE__ */ jsx(
|
|
1793
1959
|
Text,
|
|
1794
1960
|
{
|
|
1795
|
-
style: [
|
|
1961
|
+
style: [styles10.title, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
|
|
1796
1962
|
children: "Mention a member"
|
|
1797
1963
|
}
|
|
1798
1964
|
),
|
|
@@ -1823,12 +1989,12 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
|
|
|
1823
1989
|
}
|
|
1824
1990
|
function MentionRow({ member, onPress }) {
|
|
1825
1991
|
const theme = usePoolseTheme();
|
|
1826
|
-
return /* @__PURE__ */ jsxs(Pressable, { onPress, style:
|
|
1992
|
+
return /* @__PURE__ */ jsxs(Pressable, { onPress, style: styles10.row, children: [
|
|
1827
1993
|
/* @__PURE__ */ jsx(Avatar, { src: null, name: member.external_id, seed: member.external_id, size: "sm" }),
|
|
1828
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
1994
|
+
/* @__PURE__ */ jsx(Text, { style: [styles10.rowText, { color: theme.colors.ink, fontFamily: theme.type.fontBody }], children: member.external_id })
|
|
1829
1995
|
] });
|
|
1830
1996
|
}
|
|
1831
|
-
var
|
|
1997
|
+
var styles10 = StyleSheet.create({
|
|
1832
1998
|
backdrop: {
|
|
1833
1999
|
flex: 1,
|
|
1834
2000
|
backgroundColor: "rgba(0,0,0,0.35)",
|
|
@@ -1855,138 +2021,134 @@ var styles9 = StyleSheet.create({
|
|
|
1855
2021
|
fontSize: 14
|
|
1856
2022
|
}
|
|
1857
2023
|
});
|
|
2024
|
+
var AT_BOTTOM_THRESHOLD = 32;
|
|
1858
2025
|
function MessageList({
|
|
1859
2026
|
messages,
|
|
2027
|
+
items,
|
|
1860
2028
|
loading = false,
|
|
1861
2029
|
hasMore = false,
|
|
1862
2030
|
onLoadMore,
|
|
1863
2031
|
renderItem,
|
|
1864
2032
|
ListHeader = null,
|
|
1865
2033
|
ListFooter = null,
|
|
1866
|
-
emptyState
|
|
2034
|
+
emptyState,
|
|
2035
|
+
meId = null
|
|
1867
2036
|
}) {
|
|
1868
2037
|
const theme = usePoolseTheme();
|
|
1869
|
-
const
|
|
2038
|
+
const flatListRef = useRef(null);
|
|
2039
|
+
const [atBottom, setAtBottom] = useState(true);
|
|
2040
|
+
const [newCount, setNewCount] = useState(0);
|
|
2041
|
+
const lastTailIdRef = useRef(null);
|
|
2042
|
+
const sourceItems = items ?? messages.map((msg) => ({ kind: "msg", msg }));
|
|
2043
|
+
const data = [...sourceItems].reverse();
|
|
2044
|
+
useEffect(() => {
|
|
2045
|
+
const tail = messages[messages.length - 1];
|
|
2046
|
+
if (!tail) return;
|
|
2047
|
+
const tailId = tail.id;
|
|
2048
|
+
if (tailId === lastTailIdRef.current) return;
|
|
2049
|
+
const isFirstSet = lastTailIdRef.current === null;
|
|
2050
|
+
lastTailIdRef.current = tailId;
|
|
2051
|
+
if (atBottom || isFirstSet) {
|
|
2052
|
+
setNewCount(0);
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
if (meId !== null && tail.sender_id === meId) return;
|
|
2056
|
+
setNewCount((n) => n + 1);
|
|
2057
|
+
}, [messages, atBottom, meId]);
|
|
2058
|
+
const handleScroll = useCallback((e) => {
|
|
2059
|
+
const y = e.nativeEvent.contentOffset.y;
|
|
2060
|
+
const next = y <= AT_BOTTOM_THRESHOLD;
|
|
2061
|
+
setAtBottom((prev) => prev === next ? prev : next);
|
|
2062
|
+
if (next) setNewCount(0);
|
|
2063
|
+
}, []);
|
|
2064
|
+
const scrollToBottom = useCallback(() => {
|
|
2065
|
+
flatListRef.current?.scrollToOffset({ offset: 0, animated: true });
|
|
2066
|
+
setNewCount(0);
|
|
2067
|
+
}, []);
|
|
1870
2068
|
const handleRender = useCallback(
|
|
1871
|
-
({ item, index }) =>
|
|
1872
|
-
|
|
2069
|
+
({ item, index }) => {
|
|
2070
|
+
if (item.kind === "separator") return item.node;
|
|
2071
|
+
const sourceIndex = data.length - 1 - index;
|
|
2072
|
+
return renderItem(item.msg, sourceIndex);
|
|
2073
|
+
},
|
|
2074
|
+
[renderItem, data.length]
|
|
1873
2075
|
);
|
|
1874
2076
|
if (loading && messages.length === 0) {
|
|
1875
|
-
return /* @__PURE__ */ jsx(View, { style: [
|
|
2077
|
+
return /* @__PURE__ */ jsx(View, { style: [styles11.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.brand }) });
|
|
1876
2078
|
}
|
|
1877
2079
|
if (messages.length === 0) {
|
|
1878
|
-
return /* @__PURE__ */ jsx(View, { style: [
|
|
2080
|
+
return /* @__PURE__ */ jsx(View, { style: [styles11.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" ? /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState }) : emptyState ?? null });
|
|
1879
2081
|
}
|
|
1880
|
-
return /* @__PURE__ */
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
2082
|
+
return /* @__PURE__ */ jsxs(View, { style: styles11.root, children: [
|
|
2083
|
+
/* @__PURE__ */ jsx(
|
|
2084
|
+
FlatList,
|
|
2085
|
+
{
|
|
2086
|
+
ref: flatListRef,
|
|
2087
|
+
inverted: true,
|
|
2088
|
+
data,
|
|
2089
|
+
keyExtractor: (item) => item.kind === "msg" ? item.msg.id : item.id,
|
|
2090
|
+
renderItem: handleRender,
|
|
2091
|
+
onEndReached: hasMore ? onLoadMore : void 0,
|
|
2092
|
+
onEndReachedThreshold: 0.3,
|
|
2093
|
+
onScroll: handleScroll,
|
|
2094
|
+
scrollEventThrottle: 32,
|
|
2095
|
+
ListHeaderComponent: ListHeader,
|
|
2096
|
+
ListFooterComponent: /* @__PURE__ */ jsxs(View, { children: [
|
|
2097
|
+
hasMore && loading ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.ink3, style: { marginVertical: 8 } }) : null,
|
|
2098
|
+
ListFooter
|
|
2099
|
+
] }),
|
|
2100
|
+
style: { backgroundColor: theme.colors.paper },
|
|
2101
|
+
contentContainerStyle: { paddingVertical: 8 }
|
|
2102
|
+
}
|
|
2103
|
+
),
|
|
2104
|
+
!atBottom && newCount > 0 ? /* @__PURE__ */ jsxs(
|
|
2105
|
+
Pressable,
|
|
2106
|
+
{
|
|
2107
|
+
onPress: scrollToBottom,
|
|
2108
|
+
style: [
|
|
2109
|
+
styles11.jumpButton,
|
|
2110
|
+
{
|
|
2111
|
+
backgroundColor: theme.colors.brand
|
|
2112
|
+
},
|
|
2113
|
+
theme.shadows.sm
|
|
2114
|
+
],
|
|
2115
|
+
accessibilityLabel: `${newCount} new ${newCount === 1 ? "message" : "messages"}, jump to latest`,
|
|
2116
|
+
children: [
|
|
2117
|
+
/* @__PURE__ */ jsx(PoolseIcon, { name: "chevron-down", size: 14, color: theme.colors.onBrand }),
|
|
2118
|
+
/* @__PURE__ */ jsxs(Text, { style: [styles11.jumpText, { color: theme.colors.onBrand }], children: [
|
|
2119
|
+
newCount,
|
|
2120
|
+
" new ",
|
|
2121
|
+
newCount === 1 ? "message" : "messages"
|
|
2122
|
+
] })
|
|
2123
|
+
]
|
|
2124
|
+
}
|
|
2125
|
+
) : null
|
|
2126
|
+
] });
|
|
1898
2127
|
}
|
|
1899
|
-
var
|
|
2128
|
+
var styles11 = StyleSheet.create({
|
|
2129
|
+
root: {
|
|
2130
|
+
flex: 1
|
|
2131
|
+
},
|
|
1900
2132
|
placeholder: {
|
|
1901
2133
|
flex: 1,
|
|
1902
2134
|
alignItems: "center",
|
|
1903
2135
|
justifyContent: "center",
|
|
1904
2136
|
padding: 24
|
|
1905
|
-
}
|
|
1906
|
-
});
|
|
1907
|
-
function EditableMessageBubble({
|
|
1908
|
-
message,
|
|
1909
|
-
currentUserId,
|
|
1910
|
-
onCancel,
|
|
1911
|
-
onSave
|
|
1912
|
-
}) {
|
|
1913
|
-
const theme = usePoolseTheme();
|
|
1914
|
-
const isSelf = currentUserId !== null && message.sender_id === currentUserId;
|
|
1915
|
-
const [value, setValue] = useState(message.body ?? "");
|
|
1916
|
-
const [saving, setSaving] = useState(false);
|
|
1917
|
-
const submit = async () => {
|
|
1918
|
-
if (saving || !value.trim()) return;
|
|
1919
|
-
setSaving(true);
|
|
1920
|
-
try {
|
|
1921
|
-
await onSave(value.trim());
|
|
1922
|
-
} finally {
|
|
1923
|
-
setSaving(false);
|
|
1924
|
-
}
|
|
1925
|
-
};
|
|
1926
|
-
const bg = isSelf ? theme.colors.selfBubble : theme.colors.otherBubble;
|
|
1927
|
-
const ink = isSelf ? theme.colors.selfBubbleText : theme.colors.otherBubbleText;
|
|
1928
|
-
return /* @__PURE__ */ jsx(
|
|
1929
|
-
View,
|
|
1930
|
-
{
|
|
1931
|
-
style: [
|
|
1932
|
-
styles11.wrap,
|
|
1933
|
-
{
|
|
1934
|
-
alignSelf: isSelf ? "flex-end" : "flex-start",
|
|
1935
|
-
maxWidth: "80%"
|
|
1936
|
-
}
|
|
1937
|
-
],
|
|
1938
|
-
children: /* @__PURE__ */ jsxs(
|
|
1939
|
-
View,
|
|
1940
|
-
{
|
|
1941
|
-
style: [
|
|
1942
|
-
styles11.bubble,
|
|
1943
|
-
{
|
|
1944
|
-
backgroundColor: bg,
|
|
1945
|
-
borderRadius: theme.radii.bubble,
|
|
1946
|
-
paddingHorizontal: theme.spacing.md,
|
|
1947
|
-
paddingVertical: theme.spacing.sm
|
|
1948
|
-
},
|
|
1949
|
-
theme.shadows.sm
|
|
1950
|
-
],
|
|
1951
|
-
children: [
|
|
1952
|
-
/* @__PURE__ */ jsx(
|
|
1953
|
-
TextInput,
|
|
1954
|
-
{
|
|
1955
|
-
value,
|
|
1956
|
-
onChangeText: setValue,
|
|
1957
|
-
multiline: true,
|
|
1958
|
-
autoFocus: true,
|
|
1959
|
-
style: {
|
|
1960
|
-
color: ink,
|
|
1961
|
-
fontFamily: theme.type.fontBody,
|
|
1962
|
-
fontSize: theme.type.bodySize,
|
|
1963
|
-
minHeight: 24
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
),
|
|
1967
|
-
/* @__PURE__ */ jsxs(View, { style: styles11.actions, children: [
|
|
1968
|
-
/* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 6, children: /* @__PURE__ */ jsx(Text, { style: [styles11.btn, { color: ink, opacity: 0.7 }], children: "Cancel" }) }),
|
|
1969
|
-
/* @__PURE__ */ jsx(Pressable, { onPress: submit, disabled: saving, hitSlop: 6, children: /* @__PURE__ */ jsx(Text, { style: [styles11.btn, { color: ink, fontWeight: "700" }], children: saving ? "Saving\u2026" : "Save" }) })
|
|
1970
|
-
] })
|
|
1971
|
-
]
|
|
1972
|
-
}
|
|
1973
|
-
)
|
|
1974
|
-
}
|
|
1975
|
-
);
|
|
1976
|
-
}
|
|
1977
|
-
var styles11 = StyleSheet.create({
|
|
1978
|
-
wrap: {
|
|
1979
|
-
marginBottom: 4
|
|
1980
2137
|
},
|
|
1981
|
-
|
|
1982
|
-
|
|
2138
|
+
jumpButton: {
|
|
2139
|
+
position: "absolute",
|
|
2140
|
+
alignSelf: "center",
|
|
2141
|
+
bottom: 12,
|
|
1983
2142
|
flexDirection: "row",
|
|
1984
|
-
|
|
1985
|
-
gap:
|
|
1986
|
-
|
|
2143
|
+
alignItems: "center",
|
|
2144
|
+
gap: 6,
|
|
2145
|
+
paddingHorizontal: 14,
|
|
2146
|
+
paddingVertical: 8,
|
|
2147
|
+
borderRadius: 999
|
|
1987
2148
|
},
|
|
1988
|
-
|
|
1989
|
-
fontSize: 13
|
|
2149
|
+
jumpText: {
|
|
2150
|
+
fontSize: 13,
|
|
2151
|
+
fontWeight: "600"
|
|
1990
2152
|
}
|
|
1991
2153
|
});
|
|
1992
2154
|
var DEFAULT_REACTION_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F389}", "\u{1F64F}", "\u{1F525}"];
|
|
@@ -2449,6 +2611,53 @@ var styles14 = StyleSheet.create({
|
|
|
2449
2611
|
borderRadius: 20
|
|
2450
2612
|
}
|
|
2451
2613
|
});
|
|
2614
|
+
function renderMarkdown(body, args) {
|
|
2615
|
+
const baseText = {
|
|
2616
|
+
color: args.textColor,
|
|
2617
|
+
fontFamily: args.fontFamily,
|
|
2618
|
+
fontSize: args.fontSize,
|
|
2619
|
+
lineHeight: args.lineHeight
|
|
2620
|
+
};
|
|
2621
|
+
const style = {
|
|
2622
|
+
body: baseText,
|
|
2623
|
+
paragraph: { ...baseText, marginTop: 0, marginBottom: 0 },
|
|
2624
|
+
text: baseText,
|
|
2625
|
+
strong: { ...baseText, fontWeight: "700" },
|
|
2626
|
+
em: { ...baseText, fontStyle: "italic" },
|
|
2627
|
+
s: { ...baseText, textDecorationLine: "line-through" },
|
|
2628
|
+
link: { color: args.linkColor, textDecorationLine: "underline" },
|
|
2629
|
+
code_inline: {
|
|
2630
|
+
...baseText,
|
|
2631
|
+
backgroundColor: "rgba(127,127,127,0.18)",
|
|
2632
|
+
borderRadius: 4,
|
|
2633
|
+
paddingHorizontal: 4
|
|
2634
|
+
},
|
|
2635
|
+
code_block: {
|
|
2636
|
+
...baseText,
|
|
2637
|
+
backgroundColor: "rgba(127,127,127,0.18)",
|
|
2638
|
+
padding: 8,
|
|
2639
|
+
borderRadius: 6
|
|
2640
|
+
},
|
|
2641
|
+
fence: {
|
|
2642
|
+
...baseText,
|
|
2643
|
+
backgroundColor: "rgba(127,127,127,0.18)",
|
|
2644
|
+
padding: 8,
|
|
2645
|
+
borderRadius: 6
|
|
2646
|
+
},
|
|
2647
|
+
bullet_list: { marginTop: 2, marginBottom: 2 },
|
|
2648
|
+
ordered_list: { marginTop: 2, marginBottom: 2 },
|
|
2649
|
+
list_item: baseText,
|
|
2650
|
+
blockquote: {
|
|
2651
|
+
...baseText,
|
|
2652
|
+
borderLeftWidth: 3,
|
|
2653
|
+
borderLeftColor: args.linkColor,
|
|
2654
|
+
paddingLeft: 8,
|
|
2655
|
+
marginVertical: 4,
|
|
2656
|
+
opacity: 0.85
|
|
2657
|
+
}
|
|
2658
|
+
};
|
|
2659
|
+
return createElement(Markdown, { style, onLinkPress: args.onLinkPress }, body);
|
|
2660
|
+
}
|
|
2452
2661
|
function MessageBubble({
|
|
2453
2662
|
message,
|
|
2454
2663
|
currentUserId,
|
|
@@ -2459,7 +2668,8 @@ function MessageBubble({
|
|
|
2459
2668
|
maxBodyLength = 0,
|
|
2460
2669
|
showSenderName = false,
|
|
2461
2670
|
actionsTrigger,
|
|
2462
|
-
showAttachments = true
|
|
2671
|
+
showAttachments = true,
|
|
2672
|
+
markdown = true
|
|
2463
2673
|
}) {
|
|
2464
2674
|
const theme = usePoolseTheme();
|
|
2465
2675
|
const { width: screenWidth } = useWindowDimensions();
|
|
@@ -2544,20 +2754,44 @@ function MessageBubble({
|
|
|
2544
2754
|
) : null,
|
|
2545
2755
|
renderAttachments && images.length > 0 ? /* @__PURE__ */ jsx(ImageMosaic, { images, maxWidth: contentMaxWidth }) : null,
|
|
2546
2756
|
renderAttachments && files.length > 0 ? /* @__PURE__ */ jsx(View, { style: { gap: theme.spacing.xs, marginBottom: theme.spacing.xs }, children: files.map((att) => /* @__PURE__ */ jsx(AttachmentPreview, { attachment: att, maxWidth: contentMaxWidth }, att.id)) }) : null,
|
|
2547
|
-
body.length > 0 ? /* @__PURE__ */ jsx(
|
|
2757
|
+
body.length > 0 ? isDeleted ? /* @__PURE__ */ jsx(
|
|
2548
2758
|
Text,
|
|
2549
2759
|
{
|
|
2550
2760
|
style: [
|
|
2551
2761
|
styles15.body,
|
|
2552
2762
|
{
|
|
2553
|
-
color:
|
|
2763
|
+
color: theme.colors.ink3,
|
|
2554
2764
|
fontFamily: theme.type.fontBody,
|
|
2555
2765
|
fontSize: theme.type.bodySize,
|
|
2556
2766
|
lineHeight: theme.type.lineHeight,
|
|
2557
|
-
fontStyle:
|
|
2767
|
+
fontStyle: "italic"
|
|
2558
2768
|
}
|
|
2559
2769
|
],
|
|
2560
|
-
children:
|
|
2770
|
+
children: "Message deleted"
|
|
2771
|
+
}
|
|
2772
|
+
) : markdown ? renderMarkdown(truncated, {
|
|
2773
|
+
textColor,
|
|
2774
|
+
linkColor: isSelf ? theme.colors.onBrand : theme.colors.brand,
|
|
2775
|
+
fontFamily: theme.type.fontBody,
|
|
2776
|
+
fontSize: theme.type.bodySize,
|
|
2777
|
+
lineHeight: theme.type.lineHeight,
|
|
2778
|
+
onLinkPress: (url) => {
|
|
2779
|
+
void Linking.openURL(url).catch(() => void 0);
|
|
2780
|
+
return false;
|
|
2781
|
+
}
|
|
2782
|
+
}) : /* @__PURE__ */ jsx(
|
|
2783
|
+
Text,
|
|
2784
|
+
{
|
|
2785
|
+
style: [
|
|
2786
|
+
styles15.body,
|
|
2787
|
+
{
|
|
2788
|
+
color: textColor,
|
|
2789
|
+
fontFamily: theme.type.fontBody,
|
|
2790
|
+
fontSize: theme.type.bodySize,
|
|
2791
|
+
lineHeight: theme.type.lineHeight
|
|
2792
|
+
}
|
|
2793
|
+
],
|
|
2794
|
+
children: truncated
|
|
2561
2795
|
}
|
|
2562
2796
|
) : null,
|
|
2563
2797
|
maxBodyLength > 0 && body.length > maxBodyLength ? /* @__PURE__ */ jsx(Pressable, { onPress: () => setExpanded((e) => !e), children: /* @__PURE__ */ jsx(
|
|
@@ -2652,9 +2886,9 @@ function formatTime(iso) {
|
|
|
2652
2886
|
try {
|
|
2653
2887
|
const d = new Date(iso);
|
|
2654
2888
|
const now = /* @__PURE__ */ new Date();
|
|
2655
|
-
const
|
|
2889
|
+
const sameDay2 = isSameDay(d, now);
|
|
2656
2890
|
const time = d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
|
|
2657
|
-
if (
|
|
2891
|
+
if (sameDay2) return time;
|
|
2658
2892
|
const yesterday = new Date(now);
|
|
2659
2893
|
yesterday.setDate(now.getDate() - 1);
|
|
2660
2894
|
if (isSameDay(d, yesterday)) return `Yesterday ${time}`;
|
|
@@ -2762,12 +2996,26 @@ function ReactionStrip({
|
|
|
2762
2996
|
styles16.chip,
|
|
2763
2997
|
{
|
|
2764
2998
|
backgroundColor: meReacted ? theme.colors.brandSoft : theme.colors.surface2,
|
|
2765
|
-
borderColor: meReacted ? theme.colors.brand : theme.colors.border
|
|
2999
|
+
borderColor: meReacted ? theme.colors.brand : theme.colors.border,
|
|
3000
|
+
borderWidth: meReacted ? 1.5 : 1,
|
|
3001
|
+
transform: meReacted ? [{ scale: 1.04 }] : void 0
|
|
2766
3002
|
}
|
|
2767
3003
|
],
|
|
2768
3004
|
children: [
|
|
2769
3005
|
/* @__PURE__ */ jsx(Text, { style: styles16.emoji, children: emoji }),
|
|
2770
|
-
/* @__PURE__ */ jsx(
|
|
3006
|
+
/* @__PURE__ */ jsx(
|
|
3007
|
+
Text,
|
|
3008
|
+
{
|
|
3009
|
+
style: [
|
|
3010
|
+
styles16.count,
|
|
3011
|
+
{
|
|
3012
|
+
color: meReacted ? theme.colors.brand : theme.colors.ink2,
|
|
3013
|
+
fontWeight: meReacted ? "700" : "600"
|
|
3014
|
+
}
|
|
3015
|
+
],
|
|
3016
|
+
children: userArr.length
|
|
3017
|
+
}
|
|
3018
|
+
)
|
|
2771
3019
|
]
|
|
2772
3020
|
},
|
|
2773
3021
|
emoji
|
|
@@ -2820,7 +3068,6 @@ var styles16 = StyleSheet.create({
|
|
|
2820
3068
|
paddingHorizontal: 9,
|
|
2821
3069
|
paddingVertical: 4,
|
|
2822
3070
|
borderRadius: 14,
|
|
2823
|
-
borderWidth: 1,
|
|
2824
3071
|
gap: 5,
|
|
2825
3072
|
minHeight: 26,
|
|
2826
3073
|
// Subtle elevation so chips lift off the surface
|
|
@@ -2836,7 +3083,6 @@ var styles16 = StyleSheet.create({
|
|
|
2836
3083
|
},
|
|
2837
3084
|
count: {
|
|
2838
3085
|
fontSize: 12,
|
|
2839
|
-
fontWeight: "600",
|
|
2840
3086
|
fontVariant: ["tabular-nums"]
|
|
2841
3087
|
},
|
|
2842
3088
|
backdrop: {
|
|
@@ -2874,11 +3120,10 @@ function MessageRow({
|
|
|
2874
3120
|
actions = true,
|
|
2875
3121
|
threads = true,
|
|
2876
3122
|
quotations = true,
|
|
3123
|
+
markdown = true,
|
|
2877
3124
|
readState,
|
|
2878
|
-
|
|
3125
|
+
isEditing = false,
|
|
2879
3126
|
onStartEdit,
|
|
2880
|
-
onCancelEdit,
|
|
2881
|
-
onSaveEdit,
|
|
2882
3127
|
onDelete,
|
|
2883
3128
|
onOpenThread,
|
|
2884
3129
|
onQuote,
|
|
@@ -2899,7 +3144,7 @@ function MessageRow({
|
|
|
2899
3144
|
...msg.reactions ? { initialReactions: msg.reactions } : {}
|
|
2900
3145
|
});
|
|
2901
3146
|
const translateX = useRef(new Animated.Value(0)).current;
|
|
2902
|
-
const swipeEnabled = quotations && !!onQuote && !
|
|
3147
|
+
const swipeEnabled = quotations && !!onQuote && !isEditing;
|
|
2903
3148
|
const panResponder = useMemo(
|
|
2904
3149
|
() => PanResponder.create({
|
|
2905
3150
|
// Tap / long-press still go to the inner Pressable.
|
|
@@ -2974,137 +3219,149 @@ function MessageRow({
|
|
|
2974
3219
|
const rowSender = useUser(showAvatarSlot ? msg.sender_external_id : null);
|
|
2975
3220
|
const avatarUrl = msg.sender_external_id ? avatarFor?.(msg.sender_external_id) ?? rowSender.profile?.avatarUrl ?? null : null;
|
|
2976
3221
|
const senderLabel = msg.sender_external_id ? labelFor?.(msg.sender_external_id) ?? rowSender.profile?.displayName ?? msg.sender_external_id : null;
|
|
2977
|
-
return /* @__PURE__ */ jsxs(
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
children: /* @__PURE__ */ jsx(PoolseIcon, { name: "reply", size: 16, color: theme.colors.brand })
|
|
3002
|
-
}
|
|
3003
|
-
) : null,
|
|
3004
|
-
/* @__PURE__ */ jsx(
|
|
3005
|
-
Animated.View,
|
|
3006
|
-
{
|
|
3007
|
-
...swipeEnabled ? panResponder.panHandlers : {},
|
|
3008
|
-
style: { transform: [{ translateX }] },
|
|
3009
|
-
children: /* @__PURE__ */ jsx(
|
|
3010
|
-
Pressable,
|
|
3222
|
+
return /* @__PURE__ */ jsxs(
|
|
3223
|
+
View,
|
|
3224
|
+
{
|
|
3225
|
+
style: [
|
|
3226
|
+
styles17.row,
|
|
3227
|
+
{ alignSelf: isSelf ? "flex-end" : "flex-start" },
|
|
3228
|
+
// Lift this row above the dim backdrop the parent renders
|
|
3229
|
+
// during edit mode. zIndex covers iOS / web; elevation does
|
|
3230
|
+
// the equivalent on Android.
|
|
3231
|
+
isEditing ? styles17.rowEditing : null
|
|
3232
|
+
],
|
|
3233
|
+
children: [
|
|
3234
|
+
showAvatarSlot ? /* @__PURE__ */ jsx(View, { style: styles17.avatarSlot, children: showAvatarRender ? /* @__PURE__ */ jsx(
|
|
3235
|
+
Avatar,
|
|
3236
|
+
{
|
|
3237
|
+
src: avatarUrl ?? null,
|
|
3238
|
+
name: senderLabel,
|
|
3239
|
+
seed: msg.sender_external_id ?? null,
|
|
3240
|
+
size: "sm"
|
|
3241
|
+
}
|
|
3242
|
+
) : null }) : null,
|
|
3243
|
+
/* @__PURE__ */ jsxs(View, { style: styles17.bubbleCol, children: [
|
|
3244
|
+
swipeEnabled ? /* @__PURE__ */ jsx(
|
|
3245
|
+
Animated.View,
|
|
3011
3246
|
{
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
accessibilityRole: "button",
|
|
3017
|
-
accessibilityLabel: "Message actions",
|
|
3018
|
-
children: editing ? /* @__PURE__ */ jsx(
|
|
3019
|
-
EditableMessageBubble,
|
|
3247
|
+
pointerEvents: "none",
|
|
3248
|
+
style: [
|
|
3249
|
+
styles17.replyHint,
|
|
3250
|
+
isSelf ? styles17.replyHintRight : styles17.replyHintLeft,
|
|
3020
3251
|
{
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
onSave: onSaveEdit ?? (() => void 0)
|
|
3252
|
+
opacity: replyIconOpacity,
|
|
3253
|
+
transform: [{ scale: replyIconScale }],
|
|
3254
|
+
backgroundColor: theme.colors.surface2
|
|
3025
3255
|
}
|
|
3026
|
-
|
|
3027
|
-
|
|
3256
|
+
],
|
|
3257
|
+
children: /* @__PURE__ */ jsx(PoolseIcon, { name: "reply", size: 16, color: theme.colors.brand })
|
|
3258
|
+
}
|
|
3259
|
+
) : null,
|
|
3260
|
+
/* @__PURE__ */ jsx(
|
|
3261
|
+
Animated.View,
|
|
3262
|
+
{
|
|
3263
|
+
...swipeEnabled ? panResponder.panHandlers : {},
|
|
3264
|
+
style: { transform: [{ translateX }] },
|
|
3265
|
+
children: /* @__PURE__ */ jsx(
|
|
3266
|
+
Pressable,
|
|
3028
3267
|
{
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
showAttachments: attachments,
|
|
3038
|
-
actionsTrigger: actions ? /* @__PURE__ */ jsx(Pressable, { onPress: () => setActionsOpen(true), hitSlop: 8, children: /* @__PURE__ */ jsx(
|
|
3039
|
-
PoolseIcon,
|
|
3268
|
+
onLongPress: () => {
|
|
3269
|
+
if (!actions) return;
|
|
3270
|
+
setActionsOpen(true);
|
|
3271
|
+
},
|
|
3272
|
+
accessibilityRole: "button",
|
|
3273
|
+
accessibilityLabel: "Message actions",
|
|
3274
|
+
children: /* @__PURE__ */ jsx(
|
|
3275
|
+
MessageBubble,
|
|
3040
3276
|
{
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3277
|
+
message: msg,
|
|
3278
|
+
currentUserId: meId,
|
|
3279
|
+
...readState ? { readState } : {},
|
|
3280
|
+
...labelFor ? { labelFor } : {},
|
|
3281
|
+
...onQuotedClick ? { onQuotedClick } : {},
|
|
3282
|
+
groupPosition,
|
|
3283
|
+
maxBodyLength,
|
|
3284
|
+
showSenderName,
|
|
3285
|
+
showAttachments: attachments,
|
|
3286
|
+
markdown,
|
|
3287
|
+
actionsTrigger: actions ? /* @__PURE__ */ jsx(Pressable, { onPress: () => setActionsOpen(true), hitSlop: 8, children: /* @__PURE__ */ jsx(
|
|
3288
|
+
PoolseIcon,
|
|
3289
|
+
{
|
|
3290
|
+
name: "chevron-down",
|
|
3291
|
+
size: 14,
|
|
3292
|
+
color: isSelf ? theme.colors.onBrand : theme.colors.ink3
|
|
3293
|
+
}
|
|
3294
|
+
) }) : null
|
|
3044
3295
|
}
|
|
3045
|
-
)
|
|
3296
|
+
)
|
|
3046
3297
|
}
|
|
3047
3298
|
)
|
|
3048
3299
|
}
|
|
3049
|
-
)
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
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
|
-
|
|
3300
|
+
),
|
|
3301
|
+
threads && (msg.reply_count ?? 0) > 0 && onOpenThread ? /* @__PURE__ */ jsx(Pressable, { onPress: onOpenThread, style: styles17.threadPill, children: /* @__PURE__ */ jsxs(Text, { style: [styles17.threadPillText, { color: theme.colors.brand }], children: [
|
|
3302
|
+
"\u{1F4AC} ",
|
|
3303
|
+
msg.reply_count,
|
|
3304
|
+
" ",
|
|
3305
|
+
msg.reply_count === 1 ? "reply" : "replies"
|
|
3306
|
+
] }) }) : null,
|
|
3307
|
+
reactions ? /* @__PURE__ */ jsx(View, { style: [styles17.reactionsRow, { alignSelf: isSelf ? "flex-end" : "flex-start" }], children: /* @__PURE__ */ jsx(
|
|
3308
|
+
ReactionStrip,
|
|
3309
|
+
{
|
|
3310
|
+
messageId: msg.id,
|
|
3311
|
+
conversationId: msg.conversation_id,
|
|
3312
|
+
meId,
|
|
3313
|
+
...msg.reactions ? { initialReactions: msg.reactions } : {}
|
|
3314
|
+
}
|
|
3315
|
+
) }) : null
|
|
3316
|
+
] }),
|
|
3317
|
+
actions && actionsOpen ? /* @__PURE__ */ jsx(
|
|
3318
|
+
MessageActions,
|
|
3319
|
+
{
|
|
3320
|
+
visible: actionsOpen,
|
|
3321
|
+
onClose: () => setActionsOpen(false),
|
|
3322
|
+
isSelf,
|
|
3323
|
+
...reactions ? {
|
|
3324
|
+
// Inline quick-react row at the top of the sheet —
|
|
3325
|
+
// one tap = react + close, no second modal needed.
|
|
3326
|
+
onPickReaction: (emoji) => {
|
|
3327
|
+
void reactionsApi.addReaction(emoji);
|
|
3328
|
+
}
|
|
3329
|
+
} : {},
|
|
3330
|
+
...msg.body && !msg.deleted_at ? {
|
|
3331
|
+
onCopy: () => {
|
|
3332
|
+
setActionsOpen(false);
|
|
3333
|
+
void Clipboard.setStringAsync(msg.body ?? "");
|
|
3334
|
+
}
|
|
3335
|
+
} : {},
|
|
3336
|
+
...onStartEdit ? {
|
|
3337
|
+
onEdit: () => {
|
|
3338
|
+
setActionsOpen(false);
|
|
3339
|
+
onStartEdit();
|
|
3340
|
+
}
|
|
3341
|
+
} : {},
|
|
3342
|
+
...onDelete ? {
|
|
3343
|
+
onDelete: () => {
|
|
3344
|
+
setActionsOpen(false);
|
|
3345
|
+
onDelete();
|
|
3346
|
+
}
|
|
3347
|
+
} : {},
|
|
3348
|
+
...onOpenThread ? {
|
|
3349
|
+
onReplyInThread: () => {
|
|
3350
|
+
setActionsOpen(false);
|
|
3351
|
+
onOpenThread();
|
|
3352
|
+
}
|
|
3353
|
+
} : {},
|
|
3354
|
+
...quotations && onQuote ? {
|
|
3355
|
+
onQuote: () => {
|
|
3356
|
+
setActionsOpen(false);
|
|
3357
|
+
onQuote();
|
|
3358
|
+
}
|
|
3359
|
+
} : {}
|
|
3103
3360
|
}
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3361
|
+
) : null
|
|
3362
|
+
]
|
|
3363
|
+
}
|
|
3364
|
+
);
|
|
3108
3365
|
}
|
|
3109
3366
|
var styles17 = StyleSheet.create({
|
|
3110
3367
|
row: {
|
|
@@ -3120,6 +3377,12 @@ var styles17 = StyleSheet.create({
|
|
|
3120
3377
|
paddingHorizontal: 10,
|
|
3121
3378
|
maxWidth: "95%"
|
|
3122
3379
|
},
|
|
3380
|
+
rowEditing: {
|
|
3381
|
+
// Above ConversationView's editBackdrop (zIndex 5). Elevation
|
|
3382
|
+
// does the equivalent stacking on Android.
|
|
3383
|
+
zIndex: 10,
|
|
3384
|
+
elevation: 10
|
|
3385
|
+
},
|
|
3123
3386
|
avatarSlot: {
|
|
3124
3387
|
width: 36,
|
|
3125
3388
|
marginRight: 6
|
|
@@ -3214,31 +3477,34 @@ function ThreadSheet({
|
|
|
3214
3477
|
const [pickerOpen, setPickerOpen] = useState(false);
|
|
3215
3478
|
const [editingId, setEditingId] = useState(null);
|
|
3216
3479
|
const renderReply = useCallback(
|
|
3217
|
-
(msg) =>
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3480
|
+
(msg) => {
|
|
3481
|
+
const isSelf = meId !== null && msg.sender_id === meId;
|
|
3482
|
+
const canEdit = isSelf && !msg.deleted_at;
|
|
3483
|
+
return /* @__PURE__ */ jsx(
|
|
3484
|
+
MessageRow,
|
|
3485
|
+
{
|
|
3486
|
+
msg,
|
|
3487
|
+
meId,
|
|
3488
|
+
reactions: true,
|
|
3489
|
+
attachments: true,
|
|
3490
|
+
actions: true,
|
|
3491
|
+
threads: false,
|
|
3492
|
+
quotations: false,
|
|
3493
|
+
isEditing: editingId === msg.id,
|
|
3494
|
+
...canEdit ? { onStartEdit: () => setEditingId(msg.id) } : {},
|
|
3495
|
+
onDelete: () => {
|
|
3496
|
+
void deleteReply(msg.id);
|
|
3497
|
+
},
|
|
3498
|
+
...labelFor ? { labelFor } : {},
|
|
3499
|
+
...avatarFor ? { avatarFor } : {}
|
|
3500
|
+
}
|
|
3501
|
+
);
|
|
3502
|
+
},
|
|
3503
|
+
[meId, editingId, deleteReply, labelFor, avatarFor]
|
|
3504
|
+
);
|
|
3505
|
+
const editingMessage = useMemo(
|
|
3506
|
+
() => editingId ? replies.find((m) => m.id === editingId) ?? null : null,
|
|
3507
|
+
[editingId, replies]
|
|
3242
3508
|
);
|
|
3243
3509
|
const rootRow = useMemo(
|
|
3244
3510
|
() => /* @__PURE__ */ jsx(
|
|
@@ -3324,7 +3590,14 @@ function ThreadSheet({
|
|
|
3324
3590
|
},
|
|
3325
3591
|
placeholder: "Reply to thread\u2026",
|
|
3326
3592
|
attachments: true,
|
|
3327
|
-
onAttachPress: () => setPickerOpen(true)
|
|
3593
|
+
onAttachPress: () => setPickerOpen(true),
|
|
3594
|
+
editingMessage,
|
|
3595
|
+
onCancelEdit: () => setEditingId(null),
|
|
3596
|
+
onSaveEdit: async (body) => {
|
|
3597
|
+
if (!editingId) return;
|
|
3598
|
+
await edit(editingId, body);
|
|
3599
|
+
setEditingId(null);
|
|
3600
|
+
}
|
|
3328
3601
|
}
|
|
3329
3602
|
)
|
|
3330
3603
|
]
|
|
@@ -3488,6 +3761,7 @@ var styles19 = StyleSheet.create({
|
|
|
3488
3761
|
fontSize: 12
|
|
3489
3762
|
}
|
|
3490
3763
|
});
|
|
3764
|
+
var GROUPING_WINDOW_MS = 5 * 60 * 1e3;
|
|
3491
3765
|
function ConversationView({
|
|
3492
3766
|
conversationId,
|
|
3493
3767
|
emptyState,
|
|
@@ -3504,6 +3778,7 @@ function ConversationView({
|
|
|
3504
3778
|
threads = true,
|
|
3505
3779
|
quotations = true,
|
|
3506
3780
|
readReceipts = true,
|
|
3781
|
+
markdown = true,
|
|
3507
3782
|
maxBodyLength = 200,
|
|
3508
3783
|
senderLabels = "auto",
|
|
3509
3784
|
avatars = "auto",
|
|
@@ -3522,9 +3797,25 @@ function ConversationView({
|
|
|
3522
3797
|
send,
|
|
3523
3798
|
edit,
|
|
3524
3799
|
delete: deleteMsg,
|
|
3525
|
-
markReadUpTo
|
|
3800
|
+
markReadUpTo,
|
|
3801
|
+
refetch
|
|
3526
3802
|
} = useMessages(conversationId);
|
|
3527
3803
|
const { typing, signalTyping } = useTyping(conversationId);
|
|
3804
|
+
const realtimeStatus = useRealtimeStatus();
|
|
3805
|
+
useEffect(() => {
|
|
3806
|
+
const sub = AppState.addEventListener("change", (next) => {
|
|
3807
|
+
if (next === "active") void refetch();
|
|
3808
|
+
});
|
|
3809
|
+
return () => sub.remove();
|
|
3810
|
+
}, [refetch]);
|
|
3811
|
+
const prevStatusRef = useRef(realtimeStatus);
|
|
3812
|
+
useEffect(() => {
|
|
3813
|
+
const prev = prevStatusRef.current;
|
|
3814
|
+
prevStatusRef.current = realtimeStatus;
|
|
3815
|
+
if (realtimeStatus === "connected" && (prev === "reconnecting" || prev === "closed" || prev === "connecting")) {
|
|
3816
|
+
void refetch();
|
|
3817
|
+
}
|
|
3818
|
+
}, [realtimeStatus, refetch]);
|
|
3528
3819
|
const wantsMembers = mentions || senderLabels === "auto" || avatars === "auto";
|
|
3529
3820
|
const { members } = useMembers(wantsMembers ? conversationId : "");
|
|
3530
3821
|
const isGroupChat = members.length > 2;
|
|
@@ -3555,10 +3846,24 @@ function ConversationView({
|
|
|
3555
3846
|
});
|
|
3556
3847
|
onMarkedRead?.(conversationId, latest.id);
|
|
3557
3848
|
}, [messages, meId, readReceipts, markReadUpTo, onMarkedRead, conversationId]);
|
|
3849
|
+
const groupPositionById = useMemo(() => {
|
|
3850
|
+
const map = /* @__PURE__ */ new Map();
|
|
3851
|
+
for (let i = 0; i < messages.length; i++) {
|
|
3852
|
+
const msg = messages[i];
|
|
3853
|
+
if (!msg) continue;
|
|
3854
|
+
const prev = i > 0 ? messages[i - 1] ?? null : null;
|
|
3855
|
+
const next = i < messages.length - 1 ? messages[i + 1] ?? null : null;
|
|
3856
|
+
map.set(msg.id, computeGroupPosition(msg, prev, next, GROUPING_WINDOW_MS));
|
|
3857
|
+
}
|
|
3858
|
+
return map;
|
|
3859
|
+
}, [messages]);
|
|
3558
3860
|
const renderItem = useCallback(
|
|
3559
3861
|
(msg) => {
|
|
3560
3862
|
if (renderMessage) return /* @__PURE__ */ jsx(Fragment, { children: renderMessage(msg, meId) });
|
|
3561
3863
|
const readState = readReceipts && meId !== null && msg.sender_id === meId ? readStateForMessage(msg, members, meId, sequenceByMessageId) : void 0;
|
|
3864
|
+
const isSelf = meId !== null && msg.sender_id === meId;
|
|
3865
|
+
const canEdit = isSelf && !msg.deleted_at && (readState ?? "sent") === "sent";
|
|
3866
|
+
const groupPosition = groupPositionById.get(msg.id) ?? "standalone";
|
|
3562
3867
|
return /* @__PURE__ */ jsx(
|
|
3563
3868
|
MessageRow,
|
|
3564
3869
|
{
|
|
@@ -3570,13 +3875,8 @@ function ConversationView({
|
|
|
3570
3875
|
threads,
|
|
3571
3876
|
quotations,
|
|
3572
3877
|
...readState ? { readState } : {},
|
|
3573
|
-
|
|
3574
|
-
onStartEdit: () => setEditingId(msg.id),
|
|
3575
|
-
onCancelEdit: () => setEditingId(null),
|
|
3576
|
-
onSaveEdit: async (body) => {
|
|
3577
|
-
await edit(msg.id, body);
|
|
3578
|
-
setEditingId(null);
|
|
3579
|
-
},
|
|
3878
|
+
isEditing: editingId === msg.id,
|
|
3879
|
+
...canEdit ? { onStartEdit: () => setEditingId(msg.id) } : {},
|
|
3580
3880
|
onDelete: () => deleteMsg(msg.id),
|
|
3581
3881
|
...threads ? { onOpenThread: () => setThreadRoot(msg) } : {},
|
|
3582
3882
|
...quotations ? { onQuote: () => setReplyingTo(msg) } : {},
|
|
@@ -3584,7 +3884,9 @@ function ConversationView({
|
|
|
3584
3884
|
...avatarFor ? { avatarFor } : {},
|
|
3585
3885
|
showSenderName: showSenderLabels,
|
|
3586
3886
|
showAvatar: showAvatars,
|
|
3587
|
-
maxBodyLength
|
|
3887
|
+
maxBodyLength,
|
|
3888
|
+
groupPosition,
|
|
3889
|
+
markdown
|
|
3588
3890
|
}
|
|
3589
3891
|
);
|
|
3590
3892
|
},
|
|
@@ -3599,16 +3901,38 @@ function ConversationView({
|
|
|
3599
3901
|
threads,
|
|
3600
3902
|
quotations,
|
|
3601
3903
|
editingId,
|
|
3602
|
-
edit,
|
|
3603
3904
|
deleteMsg,
|
|
3604
3905
|
labelFor,
|
|
3605
3906
|
avatarFor,
|
|
3606
3907
|
showSenderLabels,
|
|
3607
3908
|
showAvatars,
|
|
3608
3909
|
maxBodyLength,
|
|
3609
|
-
sequenceByMessageId
|
|
3910
|
+
sequenceByMessageId,
|
|
3911
|
+
groupPositionById,
|
|
3912
|
+
markdown
|
|
3610
3913
|
]
|
|
3611
3914
|
);
|
|
3915
|
+
const editingMessage = useMemo(
|
|
3916
|
+
() => editingId ? messages.find((m) => m.id === editingId) ?? null : null,
|
|
3917
|
+
[editingId, messages]
|
|
3918
|
+
);
|
|
3919
|
+
const listItems = useMemo(() => {
|
|
3920
|
+
const out = [];
|
|
3921
|
+
let prev = null;
|
|
3922
|
+
for (const msg of messages) {
|
|
3923
|
+
if (prev === null || !sameDay(msg.inserted_at, prev.inserted_at)) {
|
|
3924
|
+
const label = formatDayLabel(msg.inserted_at);
|
|
3925
|
+
out.push({
|
|
3926
|
+
kind: "separator",
|
|
3927
|
+
id: `sep-${msg.id}`,
|
|
3928
|
+
node: /* @__PURE__ */ jsx(DaySeparator, { label })
|
|
3929
|
+
});
|
|
3930
|
+
}
|
|
3931
|
+
out.push({ kind: "msg", msg });
|
|
3932
|
+
prev = msg;
|
|
3933
|
+
}
|
|
3934
|
+
return out;
|
|
3935
|
+
}, [messages]);
|
|
3612
3936
|
const composerProps = useMemo(
|
|
3613
3937
|
() => ({
|
|
3614
3938
|
onSend: async (body, opts) => {
|
|
@@ -3622,9 +3946,28 @@ function ConversationView({
|
|
|
3622
3946
|
onCancelReply: () => setReplyingTo(null),
|
|
3623
3947
|
...labelFor ? { labelFor } : {},
|
|
3624
3948
|
attachments,
|
|
3625
|
-
keyboardOffset
|
|
3949
|
+
keyboardOffset,
|
|
3950
|
+
editingMessage,
|
|
3951
|
+
onCancelEdit: () => setEditingId(null),
|
|
3952
|
+
onSaveEdit: async (body) => {
|
|
3953
|
+
if (!editingId) return;
|
|
3954
|
+
await edit(editingId, body);
|
|
3955
|
+
setEditingId(null);
|
|
3956
|
+
}
|
|
3626
3957
|
}),
|
|
3627
|
-
[
|
|
3958
|
+
[
|
|
3959
|
+
send,
|
|
3960
|
+
signalTyping,
|
|
3961
|
+
attachments,
|
|
3962
|
+
replyingTo,
|
|
3963
|
+
labelFor,
|
|
3964
|
+
keyboardOffset,
|
|
3965
|
+
onSent,
|
|
3966
|
+
conversationId,
|
|
3967
|
+
editingMessage,
|
|
3968
|
+
editingId,
|
|
3969
|
+
edit
|
|
3970
|
+
]
|
|
3628
3971
|
);
|
|
3629
3972
|
const headerNode = header === false ? null : header === true ? /* @__PURE__ */ jsx(
|
|
3630
3973
|
ChatHeader,
|
|
@@ -3644,18 +3987,30 @@ function ConversationView({
|
|
|
3644
3987
|
keyboardVerticalOffset: keyboardOffset,
|
|
3645
3988
|
children: [
|
|
3646
3989
|
headerNode,
|
|
3647
|
-
/* @__PURE__ */
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3990
|
+
/* @__PURE__ */ jsxs(View, { style: styles20.listWrap, children: [
|
|
3991
|
+
/* @__PURE__ */ jsx(
|
|
3992
|
+
MessageList,
|
|
3993
|
+
{
|
|
3994
|
+
messages,
|
|
3995
|
+
items: listItems,
|
|
3996
|
+
meId,
|
|
3997
|
+
loading,
|
|
3998
|
+
hasMore,
|
|
3999
|
+
onLoadMore: loadMore,
|
|
4000
|
+
renderItem,
|
|
4001
|
+
ListHeader: /* @__PURE__ */ jsx(TypingIndicator, { typing, ...labelFor ? { labelFor } : {} }),
|
|
4002
|
+
...emptyState !== void 0 ? { emptyState } : {}
|
|
4003
|
+
}
|
|
4004
|
+
),
|
|
4005
|
+
editingId ? /* @__PURE__ */ jsx(
|
|
4006
|
+
Pressable,
|
|
4007
|
+
{
|
|
4008
|
+
style: styles20.editBackdrop,
|
|
4009
|
+
onPress: () => setEditingId(null),
|
|
4010
|
+
accessibilityLabel: "Cancel editing"
|
|
4011
|
+
}
|
|
4012
|
+
) : null
|
|
4013
|
+
] }),
|
|
3659
4014
|
mentions ? /* @__PURE__ */ jsx(MentionInput, { conversationId, ...composerProps }) : /* @__PURE__ */ jsx(MessageComposer, { ...composerProps }),
|
|
3660
4015
|
attachments ? /* @__PURE__ */ jsx(AttachmentPicker, { visible: pickerOpen, onClose: () => setPickerOpen(false) }) : null,
|
|
3661
4016
|
threads && threadRoot ? /* @__PURE__ */ jsx(
|
|
@@ -3691,6 +4046,14 @@ var styles20 = StyleSheet.create({
|
|
|
3691
4046
|
},
|
|
3692
4047
|
listWrap: {
|
|
3693
4048
|
flex: 1
|
|
4049
|
+
},
|
|
4050
|
+
editBackdrop: {
|
|
4051
|
+
...StyleSheet.absoluteFillObject,
|
|
4052
|
+
backgroundColor: "rgba(0,0,0,0.45)",
|
|
4053
|
+
// Sit above message rows by default but below any row that
|
|
4054
|
+
// explicitly elevates itself (MessageRow bumps zIndex/elevation
|
|
4055
|
+
// when isEditing is true).
|
|
4056
|
+
zIndex: 5
|
|
3694
4057
|
}
|
|
3695
4058
|
});
|
|
3696
4059
|
function UserPickerSheet({
|
|
@@ -4549,6 +4912,6 @@ function useAppStateLifecycle() {
|
|
|
4549
4912
|
}, [poolse]);
|
|
4550
4913
|
}
|
|
4551
4914
|
|
|
4552
|
-
export { AttachmentPicker, AttachmentPreview, Avatar, ChatHeader, ConversationList, ConversationView,
|
|
4915
|
+
export { AttachmentPicker, AttachmentPreview, Avatar, ChatHeader, ConversationList, ConversationView, DaySeparator, GroupDetailsSheet, ImageMosaic, MemberList, MentionInput, MessageActions, MessageBubble, MessageComposer, MessageList, MessageRow, PoolseIcon, PoolseInbox, PoolseLogo, PoolseTheme, primitives as Primitives, ReactionPicker, ReactionStrip, ThreadView, TypingIndicator, UploadQueueStrip, UserName, defaultDarkTheme, defaultLightTheme, useAppStateLifecycle, useDisplayName, usePoolseTheme };
|
|
4553
4916
|
//# sourceMappingURL=index.js.map
|
|
4554
4917
|
//# sourceMappingURL=index.js.map
|