@poolse/react-native 2.18.0 → 2.20.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/CHANGELOG.md +65 -0
- package/dist/index.cjs +324 -192
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -28
- package/dist/index.d.ts +22 -28
- package/dist/index.js +324 -192
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -86,7 +86,7 @@ var radii = {
|
|
|
86
86
|
lg: 16,
|
|
87
87
|
xl: 24,
|
|
88
88
|
pill: 999,
|
|
89
|
-
bubble:
|
|
89
|
+
bubble: 18
|
|
90
90
|
};
|
|
91
91
|
var spacing = {
|
|
92
92
|
xs: 4,
|
|
@@ -2318,7 +2318,12 @@ var styles12 = StyleSheet.create({
|
|
|
2318
2318
|
fontWeight: "500"
|
|
2319
2319
|
}
|
|
2320
2320
|
});
|
|
2321
|
-
function AttachmentPreview({
|
|
2321
|
+
function AttachmentPreview({
|
|
2322
|
+
attachment,
|
|
2323
|
+
onPress,
|
|
2324
|
+
maxWidth = 280,
|
|
2325
|
+
tone = "default"
|
|
2326
|
+
}) {
|
|
2322
2327
|
const theme = usePoolseTheme();
|
|
2323
2328
|
const isImage = (attachment.content_type ?? "").startsWith("image/");
|
|
2324
2329
|
const { url } = useAttachmentUrl(attachment.id);
|
|
@@ -2352,6 +2357,9 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
|
|
|
2352
2357
|
}
|
|
2353
2358
|
) });
|
|
2354
2359
|
}
|
|
2360
|
+
const onBrand = tone === "onBrand";
|
|
2361
|
+
const ink = onBrand ? theme.colors.onBrand : theme.colors.ink;
|
|
2362
|
+
const inkSoft = onBrand ? theme.colors.onBrand : theme.colors.ink3;
|
|
2355
2363
|
return /* @__PURE__ */ jsxs(
|
|
2356
2364
|
Pressable,
|
|
2357
2365
|
{
|
|
@@ -2360,23 +2368,23 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
|
|
|
2360
2368
|
style: [
|
|
2361
2369
|
styles13.fileCard,
|
|
2362
2370
|
{
|
|
2363
|
-
backgroundColor: theme.colors.surface2,
|
|
2364
|
-
borderColor: theme.colors.border,
|
|
2371
|
+
backgroundColor: onBrand ? "rgba(255,255,255,0.18)" : theme.colors.surface2,
|
|
2372
|
+
borderColor: onBrand ? "transparent" : theme.colors.border,
|
|
2365
2373
|
borderRadius: theme.radii.md
|
|
2366
2374
|
}
|
|
2367
2375
|
],
|
|
2368
2376
|
children: [
|
|
2369
|
-
/* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 18, color: theme.colors.ink2 }),
|
|
2377
|
+
/* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 18, color: onBrand ? ink : theme.colors.ink2 }),
|
|
2370
2378
|
/* @__PURE__ */ jsxs(View, { style: { flex: 1, marginLeft: 8 }, children: [
|
|
2371
2379
|
/* @__PURE__ */ jsx(
|
|
2372
2380
|
Text,
|
|
2373
2381
|
{
|
|
2374
|
-
style: [styles13.fileName, { color:
|
|
2382
|
+
style: [styles13.fileName, { color: ink, fontFamily: theme.type.fontBody }],
|
|
2375
2383
|
numberOfLines: 1,
|
|
2376
2384
|
children: attachment.original_filename ?? "file"
|
|
2377
2385
|
}
|
|
2378
2386
|
),
|
|
2379
|
-
/* @__PURE__ */ jsx(Text, { style: [styles13.fileSize, { color:
|
|
2387
|
+
/* @__PURE__ */ jsx(Text, { style: [styles13.fileSize, { color: inkSoft, opacity: onBrand ? 0.8 : 1 }], children: formatBytes2(attachment.byte_size) })
|
|
2380
2388
|
] })
|
|
2381
2389
|
]
|
|
2382
2390
|
}
|
|
@@ -2404,9 +2412,10 @@ var styles13 = StyleSheet.create({
|
|
|
2404
2412
|
fontSize: 11
|
|
2405
2413
|
}
|
|
2406
2414
|
});
|
|
2407
|
-
function ImageMosaic({ images, maxWidth, radius }) {
|
|
2415
|
+
function ImageMosaic({ images, maxWidth, radius, squareBottom = false }) {
|
|
2408
2416
|
const theme = usePoolseTheme();
|
|
2409
2417
|
const r = radius ?? theme.radii.md;
|
|
2418
|
+
const rBottom = squareBottom ? 5 : r;
|
|
2410
2419
|
const [viewerIndex, setViewerIndex] = useState(null);
|
|
2411
2420
|
if (images.length === 0) return null;
|
|
2412
2421
|
const visible = images.slice(0, 4);
|
|
@@ -2419,8 +2428,17 @@ function ImageMosaic({ images, maxWidth, radius }) {
|
|
|
2419
2428
|
style: [
|
|
2420
2429
|
styles14.root,
|
|
2421
2430
|
{
|
|
2422
|
-
|
|
2423
|
-
|
|
2431
|
+
borderTopLeftRadius: r,
|
|
2432
|
+
borderTopRightRadius: r,
|
|
2433
|
+
borderBottomLeftRadius: rBottom,
|
|
2434
|
+
borderBottomRightRadius: rBottom,
|
|
2435
|
+
// Explicit pixel width, not just a cap: the mosaic sits
|
|
2436
|
+
// inside an auto-sized bubble, and a percentage width
|
|
2437
|
+
// against an indefinite parent collapses (or, worse,
|
|
2438
|
+
// resolves against the image's intrinsic size and blows
|
|
2439
|
+
// the row open). Media bubbles are fixed-width — the
|
|
2440
|
+
// WhatsApp convention anyway.
|
|
2441
|
+
...maxWidth ? { width: maxWidth } : {}
|
|
2424
2442
|
}
|
|
2425
2443
|
],
|
|
2426
2444
|
children: [
|
|
@@ -2657,6 +2675,27 @@ var styles14 = StyleSheet.create({
|
|
|
2657
2675
|
borderRadius: 20
|
|
2658
2676
|
}
|
|
2659
2677
|
});
|
|
2678
|
+
|
|
2679
|
+
// src/internal/bubbleText.ts
|
|
2680
|
+
function hasMarkdown(body) {
|
|
2681
|
+
return /[*_~`#>|\\]|\[.*\]|^\s*(?:[-+]|\d+[.)])\s|https?:\/\/|www\./ms.test(body);
|
|
2682
|
+
}
|
|
2683
|
+
function emojiOnlyCount(body) {
|
|
2684
|
+
const text = body.trim();
|
|
2685
|
+
if (!text || text.length > 32) return 0;
|
|
2686
|
+
try {
|
|
2687
|
+
const unit = "(?:\\p{Regional_Indicator}{2}|\\p{Extended_Pictographic})[\\u{1F3FB}-\\u{1F3FF}]?\\uFE0F?(?:\\u200D\\p{Extended_Pictographic}[\\u{1F3FB}-\\u{1F3FF}]?\\uFE0F?)*";
|
|
2688
|
+
const whole = new RegExp(`^(?:${unit}){1,3}$`, "u");
|
|
2689
|
+
if (!whole.test(text)) return 0;
|
|
2690
|
+
const each = new RegExp(unit, "gu");
|
|
2691
|
+
return (text.match(each) ?? []).length;
|
|
2692
|
+
} catch {
|
|
2693
|
+
return 0;
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
function emojiFontSize(count) {
|
|
2697
|
+
return count === 1 ? 44 : count === 2 ? 38 : 32;
|
|
2698
|
+
}
|
|
2660
2699
|
var markdownit = MarkdownIt({ typographer: false, linkify: true });
|
|
2661
2700
|
function renderMarkdown(body, args) {
|
|
2662
2701
|
const baseText = {
|
|
@@ -2723,100 +2762,154 @@ function MessageBubble({
|
|
|
2723
2762
|
const isSelf = currentUserId !== null && message.sender_id === currentUserId;
|
|
2724
2763
|
const [expanded, setExpanded] = useState(false);
|
|
2725
2764
|
const bubbleMaxWidth = Math.floor(screenWidth * 0.78);
|
|
2726
|
-
const
|
|
2765
|
+
const mediaWidth = Math.min(bubbleMaxWidth - 6, 420);
|
|
2727
2766
|
const sender = useUser(!isSelf ? message.sender_external_id : null);
|
|
2728
2767
|
const senderFallbackName = message.sender_external_id ?? "Unknown";
|
|
2729
2768
|
const senderLabel = sender.profile?.displayName ?? (message.sender_external_id ? labelFor?.(message.sender_external_id) ?? senderFallbackName : senderFallbackName);
|
|
2730
2769
|
const senderTint = userColor(message.sender_external_id ?? message.sender_id ?? message.id);
|
|
2731
2770
|
const tailCorner = groupPosition === "last" || groupPosition === "standalone";
|
|
2732
|
-
const
|
|
2771
|
+
const bubbleRadius = theme.radii.bubble;
|
|
2733
2772
|
const textColor = isSelf ? theme.colors.selfBubbleText : theme.colors.otherBubbleText;
|
|
2773
|
+
const mutedOnBubble = isSelf ? theme.colors.onBrand : theme.colors.ink3;
|
|
2734
2774
|
const radii2 = {
|
|
2735
|
-
topLeft:
|
|
2736
|
-
topRight:
|
|
2737
|
-
bottomLeft:
|
|
2738
|
-
bottomRight:
|
|
2775
|
+
topLeft: bubbleRadius,
|
|
2776
|
+
topRight: bubbleRadius,
|
|
2777
|
+
bottomLeft: bubbleRadius,
|
|
2778
|
+
bottomRight: bubbleRadius
|
|
2739
2779
|
};
|
|
2740
2780
|
if (tailCorner) {
|
|
2741
|
-
if (isSelf) radii2.bottomRight =
|
|
2742
|
-
else radii2.bottomLeft =
|
|
2781
|
+
if (isSelf) radii2.bottomRight = 5;
|
|
2782
|
+
else radii2.bottomLeft = 5;
|
|
2743
2783
|
}
|
|
2744
2784
|
const body = message.body ?? "";
|
|
2745
|
-
const truncated = maxBodyLength > 0 && body.length > maxBodyLength && !expanded ? body.slice(0, maxBodyLength) + "\u2026" : body;
|
|
2746
|
-
const time = formatTime(message.inserted_at);
|
|
2747
2785
|
const isDeleted = !!message.deleted_at;
|
|
2748
2786
|
const isEdited = !!message.edited_at && !isDeleted;
|
|
2749
|
-
const
|
|
2750
|
-
const
|
|
2751
|
-
const
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2787
|
+
const trimmable = maxBodyLength > 0 && body.length > maxBodyLength;
|
|
2788
|
+
const truncated = trimmable && !expanded ? body.slice(0, maxBodyLength) + "\u2026" : body;
|
|
2789
|
+
const time = formatTime(message.inserted_at);
|
|
2790
|
+
const images = showAttachments && !isDeleted ? (message.attachments ?? []).filter((a) => isImageContentType(a.content_type)) : [];
|
|
2791
|
+
const files = showAttachments && !isDeleted ? (message.attachments ?? []).filter((a) => !isImageContentType(a.content_type)) : [];
|
|
2792
|
+
const hasMedia = images.length > 0;
|
|
2793
|
+
const hasFiles = files.length > 0;
|
|
2794
|
+
const hasBody = !isDeleted && body.length > 0;
|
|
2795
|
+
const metaOverlay = hasMedia && !hasBody && !hasFiles;
|
|
2796
|
+
const jumboCount = hasBody && !hasMedia && !hasFiles && !message.quoted_message_id ? emojiOnlyCount(body) : 0;
|
|
2797
|
+
const inlineMeta = hasBody && jumboCount === 0 && !trimmable && (!markdown || !hasMarkdown(truncated));
|
|
2798
|
+
const sectionAbove = hasMedia || hasFiles || !!message.quoted_message || isDeleted;
|
|
2799
|
+
const ghost = ` ${isEdited ? "edited " : ""}${time}${isSelf && readState ? " \u2713\u2713" : ""}`;
|
|
2800
|
+
const metaRow = (variant) => {
|
|
2801
|
+
const color = variant === "overlay" ? "#FFFFFF" : variant === "jumbo" ? theme.colors.ink3 : mutedOnBubble;
|
|
2802
|
+
return /* @__PURE__ */ jsxs(
|
|
2803
|
+
View,
|
|
2804
|
+
{
|
|
2805
|
+
style: [
|
|
2806
|
+
styles15.metaRow,
|
|
2807
|
+
variant === "inline" && styles15.metaInline,
|
|
2808
|
+
variant === "overlay" && styles15.metaOverlay,
|
|
2809
|
+
variant === "block" && styles15.metaBlock,
|
|
2810
|
+
// File-only bubble: the meta row follows the cards directly —
|
|
2811
|
+
// give it air so the time doesn't sit glued to the card.
|
|
2812
|
+
variant === "block" && hasFiles && !hasBody && styles15.metaBlockAfterFiles,
|
|
2813
|
+
variant === "jumbo" && styles15.metaJumbo
|
|
2814
|
+
],
|
|
2815
|
+
pointerEvents: "none",
|
|
2816
|
+
children: [
|
|
2817
|
+
isEdited ? /* @__PURE__ */ jsx(Text, { style: [styles15.editedTag, { color, opacity: 0.8 }], children: "edited" }) : null,
|
|
2818
|
+
/* @__PURE__ */ jsx(Text, { style: [styles15.meta, { color, opacity: variant === "overlay" ? 1 : 0.75 }], children: time }),
|
|
2819
|
+
isSelf && readState ? /* @__PURE__ */ jsx(View, { style: styles15.readTick, children: /* @__PURE__ */ jsx(
|
|
2820
|
+
PoolseIcon,
|
|
2771
2821
|
{
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2822
|
+
name: readState === "read" ? "check-double" : "check",
|
|
2823
|
+
size: 13,
|
|
2824
|
+
color
|
|
2825
|
+
}
|
|
2826
|
+
) }) : null
|
|
2827
|
+
]
|
|
2828
|
+
}
|
|
2829
|
+
);
|
|
2830
|
+
};
|
|
2831
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles15.wrap, { alignSelf: isSelf ? "flex-end" : "flex-start", maxWidth: bubbleMaxWidth }], children: [
|
|
2832
|
+
/* @__PURE__ */ jsxs(
|
|
2833
|
+
View,
|
|
2834
|
+
{
|
|
2835
|
+
style: [
|
|
2836
|
+
jumboCount === 0 && {
|
|
2837
|
+
backgroundColor: isSelf ? theme.colors.selfBubble : theme.colors.otherBubble,
|
|
2838
|
+
borderColor: isSelf ? "transparent" : theme.colors.border,
|
|
2839
|
+
borderWidth: isSelf ? 0 : StyleSheet.hairlineWidth,
|
|
2840
|
+
borderTopLeftRadius: radii2.topLeft,
|
|
2841
|
+
borderTopRightRadius: radii2.topRight,
|
|
2842
|
+
borderBottomLeftRadius: radii2.bottomLeft,
|
|
2843
|
+
borderBottomRightRadius: radii2.bottomRight
|
|
2844
|
+
},
|
|
2845
|
+
jumboCount === 0 && theme.shadows.sm
|
|
2846
|
+
],
|
|
2847
|
+
children: [
|
|
2848
|
+
showSenderName && !isSelf && jumboCount === 0 && (groupPosition === "first" || groupPosition === "standalone") ? /* @__PURE__ */ jsx(
|
|
2849
|
+
Text,
|
|
2850
|
+
{
|
|
2851
|
+
style: [styles15.senderName, { color: senderTint, fontFamily: theme.type.fontBody }],
|
|
2852
|
+
children: senderLabel
|
|
2853
|
+
}
|
|
2854
|
+
) : null,
|
|
2855
|
+
message.quoted_message ? /* @__PURE__ */ jsx(
|
|
2856
|
+
QuotedCard,
|
|
2857
|
+
{
|
|
2858
|
+
quoted: message.quoted_message,
|
|
2859
|
+
isSelf,
|
|
2860
|
+
...labelFor ? { labelFor } : {},
|
|
2861
|
+
...onQuotedClick ? { onPress: () => onQuotedClick(message.quoted_message.id) } : {}
|
|
2862
|
+
}
|
|
2863
|
+
) : null,
|
|
2864
|
+
hasMedia ? (
|
|
2865
|
+
// 3px of bubble color framing the picture; the mosaic clips
|
|
2866
|
+
// itself 3px tighter than the bubble so the curves stay
|
|
2867
|
+
// concentric.
|
|
2868
|
+
/* @__PURE__ */ jsxs(View, { style: styles15.mediaFrame, children: [
|
|
2869
|
+
/* @__PURE__ */ jsx(
|
|
2870
|
+
ImageMosaic,
|
|
2871
|
+
{
|
|
2872
|
+
images,
|
|
2873
|
+
maxWidth: mediaWidth,
|
|
2874
|
+
radius: Math.max(bubbleRadius - 3, 8),
|
|
2875
|
+
squareBottom: !metaOverlay
|
|
2876
|
+
}
|
|
2877
|
+
),
|
|
2878
|
+
metaOverlay ? metaRow("overlay") : null
|
|
2879
|
+
] })
|
|
2880
|
+
) : null,
|
|
2881
|
+
hasFiles ? /* @__PURE__ */ jsx(View, { style: styles15.filesBlock, children: files.map((att) => /* @__PURE__ */ jsx(
|
|
2882
|
+
AttachmentPreview,
|
|
2883
|
+
{
|
|
2884
|
+
attachment: att,
|
|
2885
|
+
maxWidth: bubbleMaxWidth - 12,
|
|
2886
|
+
tone: isSelf ? "onBrand" : "default"
|
|
2788
2887
|
},
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
children:
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
fontStyle: "italic"
|
|
2815
|
-
}
|
|
2816
|
-
],
|
|
2817
|
-
children: "Message deleted"
|
|
2818
|
-
}
|
|
2819
|
-
) : markdown ? renderMarkdown(truncated, {
|
|
2888
|
+
att.id
|
|
2889
|
+
)) }) : null,
|
|
2890
|
+
isDeleted ? /* @__PURE__ */ jsx(View, { style: [styles15.textBlock, sectionAbove && styles15.textBlockAfter], children: /* @__PURE__ */ jsx(
|
|
2891
|
+
Text,
|
|
2892
|
+
{
|
|
2893
|
+
style: {
|
|
2894
|
+
color: mutedOnBubble,
|
|
2895
|
+
opacity: 0.7,
|
|
2896
|
+
fontFamily: theme.type.fontBody,
|
|
2897
|
+
fontSize: theme.type.bodySize,
|
|
2898
|
+
lineHeight: theme.type.lineHeight,
|
|
2899
|
+
fontStyle: "italic"
|
|
2900
|
+
},
|
|
2901
|
+
children: "Message deleted"
|
|
2902
|
+
}
|
|
2903
|
+
) }) : null,
|
|
2904
|
+
hasBody && jumboCount > 0 ? /* @__PURE__ */ jsx(View, { style: styles15.jumboBlock, children: /* @__PURE__ */ jsx(
|
|
2905
|
+
Text,
|
|
2906
|
+
{
|
|
2907
|
+
style: { fontSize: emojiFontSize(jumboCount), lineHeight: emojiFontSize(jumboCount) + 8 },
|
|
2908
|
+
children: body
|
|
2909
|
+
}
|
|
2910
|
+
) }) : null,
|
|
2911
|
+
hasBody && jumboCount === 0 ? /* @__PURE__ */ jsxs(View, { style: [styles15.textBlock, sectionAbove && styles15.textBlockAfter], children: [
|
|
2912
|
+
markdown && !inlineMeta ? renderMarkdown(truncated, {
|
|
2820
2913
|
textColor,
|
|
2821
2914
|
linkColor: isSelf ? theme.colors.onBrand : theme.colors.brand,
|
|
2822
2915
|
fontFamily: theme.type.fontBody,
|
|
@@ -2826,22 +2919,29 @@ function MessageBubble({
|
|
|
2826
2919
|
void Linking.openURL(url).catch(() => void 0);
|
|
2827
2920
|
return false;
|
|
2828
2921
|
}
|
|
2829
|
-
}) : /* @__PURE__ */
|
|
2922
|
+
}) : /* @__PURE__ */ jsxs(
|
|
2830
2923
|
Text,
|
|
2831
2924
|
{
|
|
2832
|
-
style:
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2925
|
+
style: {
|
|
2926
|
+
color: textColor,
|
|
2927
|
+
fontFamily: theme.type.fontBody,
|
|
2928
|
+
fontSize: theme.type.bodySize,
|
|
2929
|
+
lineHeight: theme.type.lineHeight
|
|
2930
|
+
},
|
|
2931
|
+
children: [
|
|
2932
|
+
truncated,
|
|
2933
|
+
inlineMeta ? (
|
|
2934
|
+
// The invisible twin: reserves the meta's footprint
|
|
2935
|
+
// at the end of the last line so the absolutely-
|
|
2936
|
+
// positioned real meta never covers a glyph — and
|
|
2937
|
+
// when the line is full, the reservation wraps and
|
|
2938
|
+
// the meta drops down, flush right.
|
|
2939
|
+
/* @__PURE__ */ jsx(Text, { style: styles15.ghost, children: ghost })
|
|
2940
|
+
) : null
|
|
2941
|
+
]
|
|
2842
2942
|
}
|
|
2843
|
-
)
|
|
2844
|
-
|
|
2943
|
+
),
|
|
2944
|
+
trimmable ? /* @__PURE__ */ jsx(Pressable, { onPress: () => setExpanded((e) => !e), children: /* @__PURE__ */ jsx(
|
|
2845
2945
|
Text,
|
|
2846
2946
|
{
|
|
2847
2947
|
style: [
|
|
@@ -2851,48 +2951,15 @@ function MessageBubble({
|
|
|
2851
2951
|
children: expanded ? "Show less" : "Read more"
|
|
2852
2952
|
}
|
|
2853
2953
|
) }) : null,
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
}
|
|
2864
|
-
],
|
|
2865
|
-
children: "edited"
|
|
2866
|
-
}
|
|
2867
|
-
) : null,
|
|
2868
|
-
/* @__PURE__ */ jsx(
|
|
2869
|
-
Text,
|
|
2870
|
-
{
|
|
2871
|
-
style: [
|
|
2872
|
-
styles15.meta,
|
|
2873
|
-
{
|
|
2874
|
-
color: isSelf ? theme.colors.onBrand : theme.colors.ink3,
|
|
2875
|
-
opacity: isSelf ? 0.85 : 1
|
|
2876
|
-
}
|
|
2877
|
-
],
|
|
2878
|
-
children: time
|
|
2879
|
-
}
|
|
2880
|
-
),
|
|
2881
|
-
isSelf && readState ? /* @__PURE__ */ jsx(View, { style: styles15.readTick, children: /* @__PURE__ */ jsx(
|
|
2882
|
-
PoolseIcon,
|
|
2883
|
-
{
|
|
2884
|
-
name: readState === "read" ? "check-double" : "check",
|
|
2885
|
-
size: 13,
|
|
2886
|
-
color: theme.colors.onBrand
|
|
2887
|
-
}
|
|
2888
|
-
) }) : null
|
|
2889
|
-
] }),
|
|
2890
|
-
actionsTrigger ? /* @__PURE__ */ jsx(View, { style: styles15.actionsSlot, children: actionsTrigger }) : null
|
|
2891
|
-
]
|
|
2892
|
-
}
|
|
2893
|
-
)
|
|
2894
|
-
}
|
|
2895
|
-
);
|
|
2954
|
+
inlineMeta ? metaRow("inline") : null
|
|
2955
|
+
] }) : null,
|
|
2956
|
+
!inlineMeta && !metaOverlay && jumboCount === 0 ? metaRow("block") : null,
|
|
2957
|
+
actionsTrigger ? /* @__PURE__ */ jsx(View, { style: styles15.actionsSlot, children: actionsTrigger }) : null
|
|
2958
|
+
]
|
|
2959
|
+
}
|
|
2960
|
+
),
|
|
2961
|
+
jumboCount > 0 ? /* @__PURE__ */ jsx(View, { style: { alignSelf: isSelf ? "flex-end" : "flex-start" }, children: metaRow("jumbo") }) : null
|
|
2962
|
+
] });
|
|
2896
2963
|
}
|
|
2897
2964
|
function QuotedCard({
|
|
2898
2965
|
quoted,
|
|
@@ -2903,7 +2970,7 @@ function QuotedCard({
|
|
|
2903
2970
|
const theme = usePoolseTheme();
|
|
2904
2971
|
const resolved = useUser(quoted.sender_external_id);
|
|
2905
2972
|
const senderName = quoted.sender_external_id ? labelFor?.(quoted.sender_external_id) ?? resolved.profile?.displayName ?? quoted.sender_external_id : "Unknown";
|
|
2906
|
-
const bg = isSelf ? "rgba(
|
|
2973
|
+
const bg = isSelf ? "rgba(0,0,0,0.16)" : theme.colors.surface2;
|
|
2907
2974
|
const accent = isSelf ? theme.colors.onBrand : theme.colors.brand;
|
|
2908
2975
|
const txt = isSelf ? theme.colors.onBrand : theme.colors.ink;
|
|
2909
2976
|
return /* @__PURE__ */ jsxs(
|
|
@@ -2919,8 +2986,8 @@ function QuotedCard({
|
|
|
2919
2986
|
}
|
|
2920
2987
|
],
|
|
2921
2988
|
children: [
|
|
2922
|
-
/* @__PURE__ */ jsx(Text, { style: [styles15.quotedSender, { color: accent }], children: senderName }),
|
|
2923
|
-
/* @__PURE__ */ jsx(Text, { style: [styles15.quotedBody, { color: txt }], numberOfLines: 2, children: quoted.body ?? "\u2026" })
|
|
2989
|
+
/* @__PURE__ */ jsx(Text, { style: [styles15.quotedSender, { color: accent }], numberOfLines: 1, children: senderName }),
|
|
2990
|
+
/* @__PURE__ */ jsx(Text, { style: [styles15.quotedBody, { color: txt }], numberOfLines: 2, children: quoted.deleted_at ? "Original message deleted" : quoted.body ?? "\u2026" })
|
|
2924
2991
|
]
|
|
2925
2992
|
}
|
|
2926
2993
|
);
|
|
@@ -2956,16 +3023,34 @@ var styles15 = StyleSheet.create({
|
|
|
2956
3023
|
wrap: {
|
|
2957
3024
|
marginBottom: 2
|
|
2958
3025
|
},
|
|
2959
|
-
bubble: {
|
|
2960
|
-
position: "relative"
|
|
2961
|
-
},
|
|
2962
3026
|
senderName: {
|
|
2963
3027
|
fontSize: 12,
|
|
2964
|
-
fontWeight: "
|
|
2965
|
-
|
|
3028
|
+
fontWeight: "700",
|
|
3029
|
+
paddingTop: 7,
|
|
3030
|
+
paddingHorizontal: 12
|
|
2966
3031
|
},
|
|
2967
|
-
|
|
2968
|
-
|
|
3032
|
+
mediaFrame: {
|
|
3033
|
+
margin: 3
|
|
3034
|
+
},
|
|
3035
|
+
filesBlock: {
|
|
3036
|
+
paddingHorizontal: 6,
|
|
3037
|
+
paddingTop: 6,
|
|
3038
|
+
gap: 4
|
|
3039
|
+
},
|
|
3040
|
+
textBlock: {
|
|
3041
|
+
paddingHorizontal: 12,
|
|
3042
|
+
paddingVertical: 7
|
|
3043
|
+
},
|
|
3044
|
+
// A hair less air when media / files / a quote already sit above.
|
|
3045
|
+
textBlockAfter: {
|
|
3046
|
+
paddingTop: 6
|
|
3047
|
+
},
|
|
3048
|
+
jumboBlock: {
|
|
3049
|
+
paddingVertical: 2
|
|
3050
|
+
},
|
|
3051
|
+
ghost: {
|
|
3052
|
+
color: "transparent",
|
|
3053
|
+
fontSize: 11
|
|
2969
3054
|
},
|
|
2970
3055
|
readMore: {
|
|
2971
3056
|
fontSize: 12,
|
|
@@ -2975,10 +3060,36 @@ var styles15 = StyleSheet.create({
|
|
|
2975
3060
|
metaRow: {
|
|
2976
3061
|
flexDirection: "row",
|
|
2977
3062
|
alignItems: "center",
|
|
3063
|
+
gap: 4
|
|
3064
|
+
},
|
|
3065
|
+
metaInline: {
|
|
3066
|
+
position: "absolute",
|
|
3067
|
+
right: 12,
|
|
3068
|
+
// Optically centers the meta against the body's last line (22px
|
|
3069
|
+
// line over the 7px bottom padding): 7 + (22 - 14) / 2. At 8 it
|
|
3070
|
+
// sat visibly low beside a one-line message.
|
|
3071
|
+
bottom: 11
|
|
3072
|
+
},
|
|
3073
|
+
metaOverlay: {
|
|
3074
|
+
position: "absolute",
|
|
3075
|
+
right: 6,
|
|
3076
|
+
bottom: 6,
|
|
3077
|
+
paddingHorizontal: 8,
|
|
3078
|
+
paddingVertical: 3,
|
|
3079
|
+
borderRadius: 999,
|
|
3080
|
+
backgroundColor: "rgba(0,0,0,0.42)"
|
|
3081
|
+
},
|
|
3082
|
+
metaBlock: {
|
|
2978
3083
|
justifyContent: "flex-end",
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3084
|
+
paddingHorizontal: 12,
|
|
3085
|
+
paddingBottom: 6,
|
|
3086
|
+
marginTop: -2
|
|
3087
|
+
},
|
|
3088
|
+
metaBlockAfterFiles: {
|
|
3089
|
+
marginTop: 6
|
|
3090
|
+
},
|
|
3091
|
+
metaJumbo: {
|
|
3092
|
+
paddingTop: 2
|
|
2982
3093
|
},
|
|
2983
3094
|
meta: {
|
|
2984
3095
|
fontSize: 11,
|
|
@@ -2991,7 +3102,7 @@ var styles15 = StyleSheet.create({
|
|
|
2991
3102
|
letterSpacing: 0.1
|
|
2992
3103
|
},
|
|
2993
3104
|
readTick: {
|
|
2994
|
-
marginLeft:
|
|
3105
|
+
marginLeft: 1,
|
|
2995
3106
|
marginTop: 1
|
|
2996
3107
|
},
|
|
2997
3108
|
actionsSlot: {
|
|
@@ -3001,20 +3112,21 @@ var styles15 = StyleSheet.create({
|
|
|
3001
3112
|
},
|
|
3002
3113
|
quoted: {
|
|
3003
3114
|
borderLeftWidth: 3,
|
|
3004
|
-
paddingLeft:
|
|
3005
|
-
paddingVertical:
|
|
3006
|
-
paddingRight:
|
|
3007
|
-
|
|
3008
|
-
|
|
3115
|
+
paddingLeft: 9,
|
|
3116
|
+
paddingVertical: 5,
|
|
3117
|
+
paddingRight: 8,
|
|
3118
|
+
margin: 4,
|
|
3119
|
+
marginBottom: 0,
|
|
3120
|
+
borderRadius: 10
|
|
3009
3121
|
},
|
|
3010
3122
|
quotedSender: {
|
|
3011
3123
|
fontSize: 12,
|
|
3012
|
-
fontWeight: "
|
|
3013
|
-
marginBottom:
|
|
3124
|
+
fontWeight: "700",
|
|
3125
|
+
marginBottom: 1
|
|
3014
3126
|
},
|
|
3015
3127
|
quotedBody: {
|
|
3016
|
-
fontSize: 12,
|
|
3017
|
-
opacity: 0.
|
|
3128
|
+
fontSize: 12.5,
|
|
3129
|
+
opacity: 0.8
|
|
3018
3130
|
}
|
|
3019
3131
|
});
|
|
3020
3132
|
var DEFAULT_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F389}", "\u{1F64F}", "\u{1F525}"];
|
|
@@ -3330,15 +3442,7 @@ function MessageRow({
|
|
|
3330
3442
|
maxBodyLength,
|
|
3331
3443
|
showSenderName,
|
|
3332
3444
|
showAttachments: attachments,
|
|
3333
|
-
markdown
|
|
3334
|
-
actionsTrigger: actions ? /* @__PURE__ */ jsx(Pressable, { onPress: () => setActionsOpen(true), hitSlop: 8, children: /* @__PURE__ */ jsx(
|
|
3335
|
-
PoolseIcon,
|
|
3336
|
-
{
|
|
3337
|
-
name: "chevron-down",
|
|
3338
|
-
size: 14,
|
|
3339
|
-
color: isSelf ? theme.colors.onBrand : theme.colors.ink3
|
|
3340
|
-
}
|
|
3341
|
-
) }) : null
|
|
3445
|
+
markdown
|
|
3342
3446
|
}
|
|
3343
3447
|
)
|
|
3344
3448
|
}
|
|
@@ -5809,6 +5913,10 @@ function format(total) {
|
|
|
5809
5913
|
return `${hours > 0 ? `${hours}:` : ""}${mm}:${String(seconds).padStart(2, "0")}`;
|
|
5810
5914
|
}
|
|
5811
5915
|
var CHROME_IDLE_MS = 4500;
|
|
5916
|
+
var ENDED_LINGER_MS = 2500;
|
|
5917
|
+
function isOver(phase) {
|
|
5918
|
+
return phase === "declined" || phase === "busy" || phase === "timed-out";
|
|
5919
|
+
}
|
|
5812
5920
|
function CallScreen({
|
|
5813
5921
|
userId,
|
|
5814
5922
|
labelFor,
|
|
@@ -5924,6 +6032,11 @@ function CallScreen({
|
|
|
5924
6032
|
endedOnEmpty.current = true;
|
|
5925
6033
|
hangUp();
|
|
5926
6034
|
}, [alone, calls.phase, hangUp]);
|
|
6035
|
+
useEffect(() => {
|
|
6036
|
+
if (!isOver(calls.phase)) return;
|
|
6037
|
+
const timer = setTimeout(calls.reset, ENDED_LINGER_MS);
|
|
6038
|
+
return () => clearTimeout(timer);
|
|
6039
|
+
}, [calls.phase, calls.reset]);
|
|
5927
6040
|
const micOn = usingSfu ? sfu.micEnabled : !mesh.muted;
|
|
5928
6041
|
const toggleMic = useCallback(() => {
|
|
5929
6042
|
if (usingSfu) void sfu.toggleMic();
|
|
@@ -5937,7 +6050,7 @@ function CallScreen({
|
|
|
5937
6050
|
const label = useMemo(() => labelFor ?? ((id) => `User ${id.slice(0, 6)}`), [labelFor]);
|
|
5938
6051
|
const ringingIn = calls.phase === "ringing-in";
|
|
5939
6052
|
const ringingOut = calls.phase === "ringing-out";
|
|
5940
|
-
const ended = calls.phase
|
|
6053
|
+
const ended = isOver(calls.phase);
|
|
5941
6054
|
const visible = calls.phase !== "idle" && !(nativeIncomingUi && ringingIn);
|
|
5942
6055
|
const duration = useCallDuration(connected);
|
|
5943
6056
|
const participants = usingSfu ? sfu.participants : [];
|
|
@@ -6179,6 +6292,26 @@ function isStalePush(sentAt, now, staleAfterMs) {
|
|
|
6179
6292
|
if (age < 0) return false;
|
|
6180
6293
|
return age > staleAfterMs;
|
|
6181
6294
|
}
|
|
6295
|
+
function readPush(payload) {
|
|
6296
|
+
const callId = payload.call_id;
|
|
6297
|
+
if (!callId) return { kind: "ignore" };
|
|
6298
|
+
if (payload.event === "ended") return { kind: "teardown", callId };
|
|
6299
|
+
const conversationId = payload.conversation_id;
|
|
6300
|
+
if (!conversationId) return { kind: "ignore" };
|
|
6301
|
+
return {
|
|
6302
|
+
kind: "ring",
|
|
6303
|
+
sentAt: payload.sent_at,
|
|
6304
|
+
call: {
|
|
6305
|
+
callId,
|
|
6306
|
+
conversationId,
|
|
6307
|
+
callerUserId: payload.caller_user_id ?? "",
|
|
6308
|
+
callerName: payload.caller_name ?? "Incoming call",
|
|
6309
|
+
// `has_video` is what older servers sent; `media` supersedes it.
|
|
6310
|
+
// Either saying video means the system UI offers video.
|
|
6311
|
+
media: payload.media === "video" || payload.has_video ? "video" : "audio"
|
|
6312
|
+
}
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6182
6315
|
function useVoipCalls({
|
|
6183
6316
|
voipPush,
|
|
6184
6317
|
callKeep,
|
|
@@ -6250,21 +6383,20 @@ function useVoipCalls({
|
|
|
6250
6383
|
useEffect(() => {
|
|
6251
6384
|
let cancelled = false;
|
|
6252
6385
|
const fromPush = (payload) => {
|
|
6253
|
-
const
|
|
6254
|
-
|
|
6255
|
-
if (
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
);
|
|
6386
|
+
const intent = readPush(payload);
|
|
6387
|
+
if (intent.kind === "ignore") return;
|
|
6388
|
+
if (intent.kind === "teardown") {
|
|
6389
|
+
dismiss.current(intent.callId);
|
|
6390
|
+
handlers.current.calls?.dropped(intent.callId);
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
6393
|
+
present.current(intent.call, intent.sentAt);
|
|
6394
|
+
handlers.current.calls?.ring({
|
|
6395
|
+
callId: intent.call.callId,
|
|
6396
|
+
conversationId: intent.call.conversationId,
|
|
6397
|
+
callerUserId: intent.call.callerUserId,
|
|
6398
|
+
media: intent.call.media
|
|
6399
|
+
});
|
|
6268
6400
|
};
|
|
6269
6401
|
ready.current = callKeep.setup({
|
|
6270
6402
|
ios: { appName },
|