@poolse/react-native 2.12.1 → 2.13.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 CHANGED
@@ -1,15 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var reactNative = require('react-native');
3
+ var react$1 = require('@poolse/react');
4
4
  var react = require('react');
5
+ var reactNative = require('react-native');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
- var react$1 = require('@poolse/react');
7
7
  var reactNativeSvg = require('react-native-svg');
8
8
  var ImagePicker = require('expo-image-picker');
9
9
  var DocumentPicker = require('expo-document-picker');
10
10
  var expoImageManipulator = require('expo-image-manipulator');
11
11
  var Clipboard = require('expo-clipboard');
12
12
  var Markdown = require('react-native-markdown-display');
13
+ var reactNativeSafeAreaContext = require('react-native-safe-area-context');
13
14
 
14
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
16
 
@@ -206,79 +207,6 @@ function PoolseTheme({ mode, theme, children }) {
206
207
  function usePoolseTheme() {
207
208
  return react.useContext(ThemeContext) ?? defaultLightTheme;
208
209
  }
209
- function CallVideoGrid({ participants, livekitReactNative, labelFor }) {
210
- const theme = usePoolseTheme();
211
- const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
212
- const VideoTrack = livekitReactNative?.VideoTrack;
213
- if (participants.length === 0) return null;
214
- const columns = participants.length <= 1 ? 1 : 2;
215
- const basis = columns === 1 ? "100%" : "48%";
216
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.grid, children: participants.map((participant) => /* @__PURE__ */ jsxRuntime.jsxs(
217
- reactNative.View,
218
- {
219
- style: [
220
- styles.tile,
221
- {
222
- flexBasis: basis,
223
- backgroundColor: theme.colors.surface,
224
- // A ring rather than a badge: it reads at a glance in a
225
- // grid, and doesn't cover a face.
226
- borderColor: participant.speaking ? theme.colors.brand : "transparent"
227
- }
228
- ],
229
- children: [
230
- participant.videoTrackRef && VideoTrack ? /* @__PURE__ */ jsxRuntime.jsx(
231
- VideoTrack,
232
- {
233
- trackRef: participant.videoTrackRef,
234
- style: styles.video,
235
- objectFit: "cover",
236
- mirror: participant.isSelf,
237
- zOrder: participant.isSelf ? 1 : 0
238
- }
239
- ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.placeholder, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.initial, { color: theme.colors.ink3 }], children: label(participant.userId).slice(0, 1).toUpperCase() }) }),
240
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.caption, children: [
241
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: [styles.name, { color: theme.colors.onBrand }], children: participant.isSelf ? "You" : participant.name ?? label(participant.userId) }),
242
- !participant.micEnabled && // Muted is worth showing even for yourself — it is the
243
- // answer to "why can nobody hear me".
244
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.muted, { color: theme.colors.onBrand }], children: "muted" })
245
- ] })
246
- ]
247
- },
248
- participant.userId
249
- )) });
250
- }
251
- var styles = reactNative.StyleSheet.create({
252
- grid: {
253
- flex: 1,
254
- flexDirection: "row",
255
- flexWrap: "wrap",
256
- justifyContent: "center",
257
- alignContent: "center",
258
- gap: 8
259
- },
260
- tile: {
261
- aspectRatio: 3 / 4,
262
- borderRadius: 16,
263
- borderWidth: 2,
264
- overflow: "hidden",
265
- justifyContent: "flex-end"
266
- },
267
- video: { ...reactNative.StyleSheet.absoluteFillObject },
268
- placeholder: { ...reactNative.StyleSheet.absoluteFillObject, alignItems: "center", justifyContent: "center" },
269
- initial: { fontSize: 48, fontWeight: "600" },
270
- caption: {
271
- flexDirection: "row",
272
- alignItems: "center",
273
- gap: 6,
274
- paddingHorizontal: 10,
275
- paddingVertical: 6,
276
- // Legible over both a bright video frame and a flat placeholder.
277
- backgroundColor: "rgba(0,0,0,0.45)"
278
- },
279
- name: { flexShrink: 1, fontSize: 13, fontWeight: "600" },
280
- muted: { fontSize: 11, opacity: 0.85 }
281
- });
282
210
 
283
211
  // src/primitives/userColor.ts
284
212
  var PALETTE = [
@@ -317,7 +245,7 @@ function Avatar({ src, name, seed, online, size = "md" }) {
317
245
  reactNative.View,
318
246
  {
319
247
  style: [
320
- styles2.root,
248
+ styles.root,
321
249
  {
322
250
  width: dim,
323
251
  height: dim,
@@ -351,7 +279,7 @@ function Avatar({ src, name, seed, online, size = "md" }) {
351
279
  reactNative.View,
352
280
  {
353
281
  style: [
354
- styles2.presence,
282
+ styles.presence,
355
283
  {
356
284
  backgroundColor: theme.colors.presence,
357
285
  borderColor: theme.colors.surface,
@@ -371,7 +299,7 @@ function computeInitials(name) {
371
299
  const parts = name.trim().split(/\s+/).slice(0, 2);
372
300
  return parts.map((p) => p.charAt(0).toUpperCase()).join("").slice(0, 2) || "?";
373
301
  }
374
- var styles2 = reactNative.StyleSheet.create({
302
+ var styles = reactNative.StyleSheet.create({
375
303
  root: {
376
304
  alignItems: "center",
377
305
  justifyContent: "center",
@@ -395,10 +323,18 @@ var ICONS = {
395
323
  vb: "0 0 24 24",
396
324
  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"/>'
397
325
  },
326
+ bluetooth: {
327
+ vb: "0 0 24 24",
328
+ d: '<path d="M7.5 7.5 16.5 16.5 12 21V3l4.5 4.5L7.5 16.5"/>'
329
+ },
398
330
  camera: {
399
331
  vb: "0 0 24 24",
400
332
  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"/>'
401
333
  },
334
+ "camera-flip": {
335
+ vb: "0 0 24 24",
336
+ 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"/><path d="M14.6 13a2.6 2.6 0 0 1-4.5 1.8"/><path d="M9.4 13a2.6 2.6 0 0 1 4.5-1.8"/><path d="M13.9 9.6v1.7h-1.7"/><path d="M10.1 16.4v-1.7h1.7"/>'
337
+ },
402
338
  "check-double": {
403
339
  vb: "0 0 24 24",
404
340
  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"/>'
@@ -415,6 +351,10 @@ var ICONS = {
415
351
  vb: "0 0 24 24",
416
352
  d: '<path d="M9 5 16 12 9 19"/>'
417
353
  },
354
+ "chevron-up": {
355
+ vb: "0 0 24 24",
356
+ d: '<path d="M6 14.5 12 8.5 18 14.5"/>'
357
+ },
418
358
  close: {
419
359
  vb: "0 0 24 24",
420
360
  d: '<path d="M6 6 18 18M18 6 6 18"/>'
@@ -443,6 +383,10 @@ var ICONS = {
443
383
  vb: "0 0 24 24",
444
384
  d: '<circle cx="12" cy="12" r="8.6"/><circle cx="9" cy="10" r="1" fill="currentColor" stroke="none"/><circle cx="15" cy="10" r="1" fill="currentColor" stroke="none"/><path d="M8.4 14.2a4.6 4.6 0 0 0 7.2 0"/>'
445
385
  },
386
+ headphones: {
387
+ vb: "0 0 24 24",
388
+ d: '<path d="M4 14v-2a8 8 0 0 1 16 0v2"/><path d="M4 14h2.6a1.4 1.4 0 0 1 1.4 1.4v3.2A1.4 1.4 0 0 1 6.6 20H5.4A1.4 1.4 0 0 1 4 18.6Z"/><path d="M20 14h-2.6a1.4 1.4 0 0 0-1.4 1.4v3.2A1.4 1.4 0 0 0 17.4 20h1.2A1.4 1.4 0 0 0 20 18.6Z"/>'
389
+ },
446
390
  heart: {
447
391
  vb: "0 0 24 24",
448
392
  d: '<path d="M12 20.3 4.4 12.9a4.6 4.6 0 1 1 6.5-6.5l1.1 1.1 1.1-1.1a4.6 4.6 0 1 1 6.5 6.5L12 20.3Z"/>'
@@ -479,6 +423,10 @@ var ICONS = {
479
423
  vb: "0 0 24 24",
480
424
  d: '<rect x="9" y="2.6" width="6" height="11.2" rx="3"/><path d="M5.6 11.2a6.4 6.4 0 0 0 12.8 0"/><path d="M12 17.6v3.4"/><path d="M8.6 21h6.8"/>'
481
425
  },
426
+ "mic-off": {
427
+ vb: "0 0 24 24",
428
+ d: '<rect x="9" y="2.6" width="6" height="11.2" rx="3"/><path d="M5.6 11.2a6.4 6.4 0 0 0 12.8 0"/><path d="M12 17.6v3.4"/><path d="M8.6 21h6.8"/><path d="M3.6 3.2 20.4 20.8"/>'
429
+ },
482
430
  "more-h": {
483
431
  vb: "0 0 24 24",
484
432
  d: '<circle cx="5.4" cy="12" r="1.6" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.6" fill="currentColor" stroke="none"/><circle cx="18.6" cy="12" r="1.6" fill="currentColor" stroke="none"/>'
@@ -487,6 +435,14 @@ var ICONS = {
487
435
  vb: "0 0 24 24",
488
436
  d: '<circle cx="12" cy="5.4" r="1.6" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.6" fill="currentColor" stroke="none"/><circle cx="12" cy="18.6" r="1.6" fill="currentColor" stroke="none"/>'
489
437
  },
438
+ phone: {
439
+ vb: "0 0 24 24",
440
+ d: '<path d="M6.6 3.5h3l1.5 3.7-1.9 1.4a12.5 12.5 0 0 0 6.2 6.2l1.4-1.9 3.7 1.5v3a2 2 0 0 1-2.2 2A16.8 16.8 0 0 1 4.6 5.7a2 2 0 0 1 2-2.2Z"/>'
441
+ },
442
+ "phone-off": {
443
+ vb: "0 0 24 24",
444
+ d: '<g transform="rotate(135 12 12)"><path d="M6.6 3.5h3l1.5 3.7-1.9 1.4a12.5 12.5 0 0 0 6.2 6.2l1.4-1.9 3.7 1.5v3a2 2 0 0 1-2.2 2A16.8 16.8 0 0 1 4.6 5.7a2 2 0 0 1 2-2.2Z"/></g>'
445
+ },
490
446
  pin: {
491
447
  vb: "0 0 24 24",
492
448
  d: '<path d="M9 3.4h6v1.8l-1 5.6 3 3.2H7l3-3.2-1-5.6V3.4Z"/><path d="M12 17v3.6"/>'
@@ -527,6 +483,14 @@ var ICONS = {
527
483
  vb: "0 0 24 24",
528
484
  d: '<path d="M4 7h9M17 7h3"/><circle cx="15" cy="7" r="2.1"/><path d="M4 17h3M11 17h9"/><circle cx="9" cy="17" r="2.1"/>'
529
485
  },
486
+ speaker: {
487
+ vb: "0 0 24 24",
488
+ d: '<path d="M4 9.5h3.2L12 5.4v13.2L7.2 14.5H4a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1Z"/><path d="M15.6 9.2a4 4 0 0 1 0 5.6"/><path d="M18.2 6.6a7.7 7.7 0 0 1 0 10.8"/>'
489
+ },
490
+ "speaker-off": {
491
+ vb: "0 0 24 24",
492
+ d: '<path d="M4 9.5h3.2L12 5.4v13.2L7.2 14.5H4a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1Z"/><path d="M16.4 9.6 21.4 14.4"/><path d="M21.4 9.6 16.4 14.4"/>'
493
+ },
530
494
  "thumbs-up": {
531
495
  vb: "0 0 24 24",
532
496
  d: '<path d="M7 10.4V20H4.2V10.4Z"/><path d="M7 10.4 11 3.2a2 2 0 0 1 1.9 2.2L12.4 9H18a2 2 0 0 1 2 2.4l-1.1 6A2 2 0 0 1 16.9 20H7"/>'
@@ -551,6 +515,14 @@ var ICONS = {
551
515
  vb: "0 0 24 24",
552
516
  d: '<circle cx="9" cy="8" r="3.6"/><path d="M2.6 20a6.4 6.4 0 0 1 12.8 0"/><path d="M15.8 4.7a3.6 3.6 0 0 1 0 6.6"/><path d="M17.6 20a6.4 6.4 0 0 0-3.1-5.5"/>'
553
517
  },
518
+ video: {
519
+ vb: "0 0 24 24",
520
+ d: '<rect x="2.6" y="6" width="13" height="12" rx="2.4"/><path d="M15.6 11.2 21.4 8v8l-5.8-3.2Z"/>'
521
+ },
522
+ "video-off": {
523
+ vb: "0 0 24 24",
524
+ d: '<rect x="2.6" y="6" width="13" height="12" rx="2.4"/><path d="M15.6 11.2 21.4 8v8l-5.8-3.2Z"/><path d="M3.6 3.2 20.4 20.8"/>'
525
+ },
554
526
  webhook: {
555
527
  vb: "0 0 24 24",
556
528
  d: '<circle cx="6" cy="6" r="2.6"/><circle cx="18" cy="9" r="2.6"/><circle cx="9.5" cy="18" r="2.6"/><path d="M7.9 7.9 12 12l3.7-1.5"/><path d="M11.6 12.7 9.8 16"/>'
@@ -581,16 +553,16 @@ function MemberList({
581
553
  );
582
554
  const effectiveOnline = onlineExternalIds ?? presenceOnline;
583
555
  if (loading && members.length === 0) {
584
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.brand }) });
556
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.brand }) });
585
557
  }
586
558
  if (error) {
587
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.placeholder, { backgroundColor: theme.colors.paper }], children: [
559
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles2.placeholder, { backgroundColor: theme.colors.paper }], children: [
588
560
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.error, fontFamily: theme.type.fontBody }, children: "Failed to load members." }),
589
- error.message ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.errorDetail, { color: theme.colors.ink3 }], children: error.message }) : null
561
+ error.message ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.errorDetail, { color: theme.colors.ink3 }], children: error.message }) : null
590
562
  ] });
591
563
  }
592
564
  if (members.length === 0) {
593
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No members." }) : emptyState });
565
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No members." }) : emptyState });
594
566
  }
595
567
  return /* @__PURE__ */ jsxRuntime.jsx(
596
568
  reactNative.FlatList,
@@ -629,23 +601,23 @@ function MemberRow({
629
601
  const resolved = react$1.useUser(externalId);
630
602
  const name = labelFor?.(externalId) ?? resolved.profile?.displayName ?? externalId;
631
603
  const avatarUrl = avatarFor?.(externalId) ?? resolved.profile?.avatarUrl ?? null;
632
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.row, children: [
604
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.row, children: [
633
605
  /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: avatarUrl, name, seed: externalId, online, size: "md" }),
634
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.body, children: [
606
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.body, children: [
635
607
  /* @__PURE__ */ jsxRuntime.jsx(
636
608
  reactNative.Text,
637
609
  {
638
- style: [styles3.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
610
+ style: [styles2.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
639
611
  numberOfLines: 1,
640
612
  children: name
641
613
  }
642
614
  ),
643
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.role, { color: theme.colors.ink3 }], children: role })
615
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.role, { color: theme.colors.ink3 }], children: role })
644
616
  ] }),
645
- onRemove ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onRemove, hitSlop: 8, style: styles3.removeBtn, children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
617
+ onRemove ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onRemove, hitSlop: 8, style: styles2.removeBtn, children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
646
618
  ] });
647
619
  }
648
- var styles3 = reactNative.StyleSheet.create({
620
+ var styles2 = reactNative.StyleSheet.create({
649
621
  placeholder: {
650
622
  flex: 1,
651
623
  alignItems: "center",
@@ -748,11 +720,11 @@ function GroupDetailsSheet({
748
720
  animationType: "slide",
749
721
  onRequestClose: onClose,
750
722
  statusBarTranslucent: true,
751
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.modalRoot, children: [
723
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.modalRoot, children: [
752
724
  /* @__PURE__ */ jsxRuntime.jsx(
753
725
  reactNative.Pressable,
754
726
  {
755
- style: styles4.backdrop,
727
+ style: styles3.backdrop,
756
728
  onPress: onClose,
757
729
  accessibilityLabel: "Close group details"
758
730
  }
@@ -761,7 +733,7 @@ function GroupDetailsSheet({
761
733
  reactNative.Animated.View,
762
734
  {
763
735
  style: [
764
- styles4.sheet,
736
+ styles3.sheet,
765
737
  {
766
738
  backgroundColor: theme.colors.paper,
767
739
  borderTopLeftRadius: theme.radii.xl,
@@ -771,14 +743,14 @@ function GroupDetailsSheet({
771
743
  theme.shadows.lg
772
744
  ],
773
745
  children: [
774
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ...panHandlers, style: styles4.dragHandle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
775
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.headerRow, children: [
746
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ...panHandlers, style: styles3.dragHandle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
747
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.headerRow, children: [
776
748
  /* @__PURE__ */ jsxRuntime.jsx(
777
749
  reactNative.Pressable,
778
750
  {
779
751
  onPress: onClose,
780
752
  hitSlop: 16,
781
- style: [styles4.closeBtn, { backgroundColor: theme.colors.surface2 }],
753
+ style: [styles3.closeBtn, { backgroundColor: theme.colors.surface2 }],
782
754
  accessibilityLabel: "Close",
783
755
  children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 18, color: theme.colors.ink })
784
756
  }
@@ -787,34 +759,34 @@ function GroupDetailsSheet({
787
759
  reactNative.Text,
788
760
  {
789
761
  style: [
790
- styles4.headerTitle,
762
+ styles3.headerTitle,
791
763
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
792
764
  ],
793
765
  children: "Group details"
794
766
  }
795
767
  )
796
768
  ] }),
797
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.summary, children: [
769
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.summary, children: [
798
770
  /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: title, seed: conversationId, size: "lg" }),
799
771
  /* @__PURE__ */ jsxRuntime.jsx(
800
772
  reactNative.Text,
801
773
  {
802
774
  style: [
803
- styles4.title,
775
+ styles3.title,
804
776
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
805
777
  ],
806
778
  numberOfLines: 1,
807
779
  children: title
808
780
  }
809
781
  ),
810
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.subtitle, { color: theme.colors.ink3 }], children: subtitle })
782
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.subtitle, { color: theme.colors.ink3 }], children: subtitle })
811
783
  ] }),
812
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.divider, { backgroundColor: theme.colors.border }] }),
813
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.sectionLabelRow, children: /* @__PURE__ */ jsxRuntime.jsx(
784
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.divider, { backgroundColor: theme.colors.border }] }),
785
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.sectionLabelRow, children: /* @__PURE__ */ jsxRuntime.jsx(
814
786
  reactNative.Text,
815
787
  {
816
788
  style: [
817
- styles4.sectionLabel,
789
+ styles3.sectionLabel,
818
790
  { color: theme.colors.ink2, fontFamily: theme.type.fontBody }
819
791
  ],
820
792
  children: "Members"
@@ -837,7 +809,7 @@ function GroupDetailsSheet({
837
809
  }
838
810
  );
839
811
  }
840
- var styles4 = reactNative.StyleSheet.create({
812
+ var styles3 = reactNative.StyleSheet.create({
841
813
  modalRoot: {
842
814
  flex: 1,
843
815
  justifyContent: "flex-end"
@@ -931,7 +903,7 @@ function ChatHeader({
931
903
  reactNative.View,
932
904
  {
933
905
  style: [
934
- styles5.root,
906
+ styles4.root,
935
907
  {
936
908
  backgroundColor: theme.colors.surface,
937
909
  borderBottomColor: theme.colors.border
@@ -956,32 +928,32 @@ function ChatHeader({
956
928
  {
957
929
  ...headerOnPress ? { onPress: headerOnPress } : {},
958
930
  style: [
959
- styles5.root,
931
+ styles4.root,
960
932
  {
961
933
  backgroundColor: theme.colors.surface,
962
934
  borderBottomColor: theme.colors.border
963
935
  }
964
936
  ],
965
937
  children: [
966
- onBack ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onBack, hitSlop: 12, style: styles5.backBtn, accessibilityLabel: "Back", children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "reply", size: 20, color: theme.colors.ink }) }) : null,
938
+ onBack ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onBack, hitSlop: 12, style: styles4.backBtn, accessibilityLabel: "Back", children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "reply", size: 20, color: theme.colors.ink }) }) : null,
967
939
  /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: avatarUrl, name: title, seed: avatarSeed, online: isOnline, size: "md" }),
968
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.titleCol, children: [
940
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.titleCol, children: [
969
941
  /* @__PURE__ */ jsxRuntime.jsx(
970
942
  reactNative.Text,
971
943
  {
972
- style: [styles5.title, { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }],
944
+ style: [styles4.title, { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }],
973
945
  numberOfLines: 1,
974
946
  children: title
975
947
  }
976
948
  ),
977
- subtitle ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: subtitle }) : null
949
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: subtitle }) : null
978
950
  ] }),
979
951
  onMembersPress ? /* @__PURE__ */ jsxRuntime.jsx(
980
952
  reactNative.Pressable,
981
953
  {
982
954
  onPress: onMembersPress,
983
955
  hitSlop: 12,
984
- style: styles5.actionBtn,
956
+ style: styles4.actionBtn,
985
957
  accessibilityLabel: "Show members",
986
958
  children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "users", size: 20, color: theme.colors.ink2 })
987
959
  }
@@ -1001,7 +973,7 @@ function ChatHeader({
1001
973
  ) : null
1002
974
  ] });
1003
975
  }
1004
- var styles5 = reactNative.StyleSheet.create({
976
+ var styles4 = reactNative.StyleSheet.create({
1005
977
  root: {
1006
978
  flexDirection: "row",
1007
979
  alignItems: "center",
@@ -1059,13 +1031,13 @@ function ConversationList({
1059
1031
  const error = isControlled ? controlledError ?? null : auto.error;
1060
1032
  const counts = unreadCounts ?? (isControlled ? void 0 : auto.unreadCounts);
1061
1033
  if (loading && conversations.length === 0) {
1062
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.brand }) });
1034
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.brand }) });
1063
1035
  }
1064
1036
  if (error) {
1065
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.error, fontFamily: theme.type.fontBody }, children: "Failed to load conversations." }) });
1037
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.error, fontFamily: theme.type.fontBody }, children: "Failed to load conversations." }) });
1066
1038
  }
1067
1039
  if (conversations.length === 0) {
1068
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No conversations yet." }) : emptyState });
1040
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No conversations yet." }) : emptyState });
1069
1041
  }
1070
1042
  return /* @__PURE__ */ jsxRuntime.jsx(
1071
1043
  reactNative.FlatList,
@@ -1115,25 +1087,25 @@ function ConversationRow({
1115
1087
  {
1116
1088
  onPress: () => onSelect?.(conv),
1117
1089
  style: [
1118
- styles6.row,
1090
+ styles5.row,
1119
1091
  {
1120
1092
  backgroundColor: selected ? theme.colors.surface2 : "transparent"
1121
1093
  }
1122
1094
  ],
1123
1095
  children: [
1124
- avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: avatarUrl }, style: styles6.rowAvatar }) : /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
1125
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.body, children: [
1096
+ avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: avatarUrl }, style: styles5.rowAvatar }) : /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
1097
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.body, children: [
1126
1098
  /* @__PURE__ */ jsxRuntime.jsx(
1127
1099
  reactNative.Text,
1128
1100
  {
1129
- style: [styles6.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1101
+ style: [styles5.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1130
1102
  numberOfLines: 1,
1131
1103
  children: title
1132
1104
  }
1133
1105
  ),
1134
- conv.last_message_preview ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview }) : conv.last_message_at ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: formatRelative(conv.last_message_at) }) : null
1106
+ conv.last_message_preview ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview }) : conv.last_message_at ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: formatRelative(conv.last_message_at) }) : null
1135
1107
  ] }),
1136
- unread > 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
1108
+ unread > 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
1137
1109
  ]
1138
1110
  }
1139
1111
  );
@@ -1151,7 +1123,7 @@ function formatRelative(iso) {
1151
1123
  return "";
1152
1124
  }
1153
1125
  }
1154
- var styles6 = reactNative.StyleSheet.create({
1126
+ var styles5 = reactNative.StyleSheet.create({
1155
1127
  placeholder: {
1156
1128
  flex: 1,
1157
1129
  alignItems: "center",
@@ -1244,11 +1216,11 @@ function AttachmentPicker({
1244
1216
  onClose();
1245
1217
  }
1246
1218
  };
1247
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles7.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs(
1219
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles6.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs(
1248
1220
  reactNative.View,
1249
1221
  {
1250
1222
  style: [
1251
- styles7.sheet,
1223
+ styles6.sheet,
1252
1224
  {
1253
1225
  backgroundColor: theme.colors.surface,
1254
1226
  borderTopLeftRadius: theme.radii.lg,
@@ -1257,19 +1229,19 @@ function AttachmentPicker({
1257
1229
  theme.shadows.lg
1258
1230
  ],
1259
1231
  children: [
1260
- camera ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles7.item, onPress: () => pickFromKind("camera"), children: [
1232
+ camera ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles6.item, onPress: () => pickFromKind("camera"), children: [
1261
1233
  /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "camera", size: 20, color: theme.colors.ink2 }),
1262
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.itemText, { color: theme.colors.ink }], children: "Camera" })
1234
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Camera" })
1263
1235
  ] }) : null,
1264
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles7.item, onPress: () => pickFromKind("image"), children: [
1236
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles6.item, onPress: () => pickFromKind("image"), children: [
1265
1237
  /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "image", size: 20, color: theme.colors.ink2 }),
1266
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.itemText, { color: theme.colors.ink }], children: "Photo or video" })
1238
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Photo or video" })
1267
1239
  ] }),
1268
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles7.item, onPress: () => pickFromKind("document"), children: [
1240
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles6.item, onPress: () => pickFromKind("document"), children: [
1269
1241
  /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "attachment", size: 20, color: theme.colors.ink2 }),
1270
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.itemText, { color: theme.colors.ink }], children: "File" })
1242
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "File" })
1271
1243
  ] }),
1272
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles7.cancel, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.itemText, { color: theme.colors.brand }], children: "Cancel" }) })
1244
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles6.cancel, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.itemText, { color: theme.colors.brand }], children: "Cancel" }) })
1273
1245
  ]
1274
1246
  }
1275
1247
  ) }) });
@@ -1415,7 +1387,7 @@ async function defaultAdapter(kind) {
1415
1387
  })
1416
1388
  );
1417
1389
  }
1418
- var styles7 = reactNative.StyleSheet.create({
1390
+ var styles6 = reactNative.StyleSheet.create({
1419
1391
  backdrop: {
1420
1392
  flex: 1,
1421
1393
  backgroundColor: "rgba(0,0,0,0.35)",
@@ -1445,11 +1417,11 @@ var styles7 = reactNative.StyleSheet.create({
1445
1417
  });
1446
1418
  function DaySeparator({ label }) {
1447
1419
  const theme = usePoolseTheme();
1448
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.wrap, children: /* @__PURE__ */ jsxRuntime.jsx(
1420
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.wrap, children: /* @__PURE__ */ jsxRuntime.jsx(
1449
1421
  reactNative.View,
1450
1422
  {
1451
1423
  style: [
1452
- styles8.chip,
1424
+ styles7.chip,
1453
1425
  {
1454
1426
  backgroundColor: theme.colors.surface2,
1455
1427
  borderColor: theme.colors.border
@@ -1459,7 +1431,7 @@ function DaySeparator({ label }) {
1459
1431
  reactNative.Text,
1460
1432
  {
1461
1433
  style: [
1462
- styles8.label,
1434
+ styles7.label,
1463
1435
  {
1464
1436
  color: theme.colors.ink2,
1465
1437
  fontFamily: theme.type.fontBody
@@ -1471,7 +1443,7 @@ function DaySeparator({ label }) {
1471
1443
  }
1472
1444
  ) });
1473
1445
  }
1474
- var styles8 = reactNative.StyleSheet.create({
1446
+ var styles7 = reactNative.StyleSheet.create({
1475
1447
  wrap: {
1476
1448
  alignItems: "center",
1477
1449
  marginVertical: 8
@@ -1498,8 +1470,8 @@ function UploadQueueStrip() {
1498
1470
  {
1499
1471
  horizontal: true,
1500
1472
  showsHorizontalScrollIndicator: false,
1501
- contentContainerStyle: styles9.scroll,
1502
- style: [styles9.scrollStyle, { marginBottom: 8 }],
1473
+ contentContainerStyle: styles8.scroll,
1474
+ style: [styles8.scrollStyle, { marginBottom: 8 }],
1503
1475
  children: upload.queue.map((item) => {
1504
1476
  const isError = item.status === "error";
1505
1477
  const isUploading = item.status === "uploading" || item.status === "pending";
@@ -1510,7 +1482,7 @@ function UploadQueueStrip() {
1510
1482
  if (item.status === "ready" || isError) upload.remove(item.localId);
1511
1483
  else upload.cancel(item.localId);
1512
1484
  };
1513
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles9.tileWrap, children: [
1485
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.tileWrap, children: [
1514
1486
  isImage && previewUri ? /* @__PURE__ */ jsxRuntime.jsx(ImageTile, { uri: previewUri, uploading: isUploading, error: isError }) : /* @__PURE__ */ jsxRuntime.jsx(
1515
1487
  FileTile,
1516
1488
  {
@@ -1525,13 +1497,13 @@ function UploadQueueStrip() {
1525
1497
  {
1526
1498
  onPress: handleClose,
1527
1499
  hitSlop: 8,
1528
- style: styles9.closeBtn,
1500
+ style: styles8.closeBtn,
1529
1501
  accessibilityLabel: isError ? "Remove failed upload" : "Cancel upload",
1530
1502
  children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 11, color: "#fff" })
1531
1503
  }
1532
1504
  ) : null,
1533
- isUploading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles9.progressOverlay, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles9.spinner, { borderColor: theme.colors.onBrand }] }) }) : null,
1534
- isError ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles9.errorOverlay, { backgroundColor: "rgba(229,72,77,0.85)" }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.errorText, { color: "#fff" }], children: "Failed" }) }) : null
1505
+ isUploading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.progressOverlay, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles8.spinner, { borderColor: theme.colors.onBrand }] }) }) : null,
1506
+ 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
1535
1507
  ] }, item.localId);
1536
1508
  })
1537
1509
  }
@@ -1544,7 +1516,7 @@ function ImageTile({ uri, uploading, error }) {
1544
1516
  {
1545
1517
  source: { uri },
1546
1518
  style: [
1547
- styles9.imageTile,
1519
+ styles8.imageTile,
1548
1520
  {
1549
1521
  backgroundColor: theme.colors.surface2,
1550
1522
  borderColor: error ? theme.colors.error : "transparent",
@@ -1566,24 +1538,24 @@ function FileTile({
1566
1538
  reactNative.View,
1567
1539
  {
1568
1540
  style: [
1569
- styles9.fileTile,
1541
+ styles8.fileTile,
1570
1542
  {
1571
1543
  backgroundColor: theme.colors.surface2,
1572
1544
  borderColor: error ? theme.colors.error : uploading ? theme.colors.brand : theme.colors.border
1573
1545
  }
1574
1546
  ],
1575
1547
  children: [
1576
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles9.fileIconBox, { backgroundColor: theme.colors.brandSoft }], children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "attachment", size: 18, color: theme.colors.brand }) }),
1577
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles9.fileInfo, children: [
1548
+ /* @__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 }) }),
1549
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.fileInfo, children: [
1578
1550
  /* @__PURE__ */ jsxRuntime.jsx(
1579
1551
  reactNative.Text,
1580
1552
  {
1581
- style: [styles9.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1553
+ style: [styles8.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1582
1554
  numberOfLines: 1,
1583
1555
  children: filename
1584
1556
  }
1585
1557
  ),
1586
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.fileSize, { color: theme.colors.ink3 }], children: formatBytes(size) })
1558
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.fileSize, { color: theme.colors.ink3 }], children: formatBytes(size) })
1587
1559
  ] })
1588
1560
  ]
1589
1561
  }
@@ -1597,7 +1569,7 @@ function formatBytes(n) {
1597
1569
  return `${(n / 1024 / 1024 / 1024).toFixed(1)} GB`;
1598
1570
  }
1599
1571
  var TILE = 64;
1600
- var styles9 = reactNative.StyleSheet.create({
1572
+ var styles8 = reactNative.StyleSheet.create({
1601
1573
  scrollStyle: {
1602
1574
  overflow: "visible"
1603
1575
  },
@@ -1813,7 +1785,7 @@ var MessageComposer = react.forwardRef(
1813
1785
  reactNative.View,
1814
1786
  {
1815
1787
  style: [
1816
- styles10.wrap,
1788
+ styles9.wrap,
1817
1789
  {
1818
1790
  backgroundColor: theme.colors.surface,
1819
1791
  borderTopColor: theme.colors.border,
@@ -1836,13 +1808,13 @@ var MessageComposer = react.forwardRef(
1836
1808
  }
1837
1809
  ) : null,
1838
1810
  attachments && !isEditing ? /* @__PURE__ */ jsxRuntime.jsx(UploadQueueStrip, {}) : null,
1839
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles10.row, children: [
1811
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles9.row, children: [
1840
1812
  attachments && onAttachPress ? /* @__PURE__ */ jsxRuntime.jsx(
1841
1813
  reactNative.Pressable,
1842
1814
  {
1843
1815
  onPress: onAttachPress,
1844
1816
  hitSlop: 8,
1845
- style: styles10.iconBtn,
1817
+ style: styles9.iconBtn,
1846
1818
  accessibilityLabel: "Attach file",
1847
1819
  children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "attachment", size: 22, color: theme.colors.ink2 })
1848
1820
  }
@@ -1861,7 +1833,7 @@ var MessageComposer = react.forwardRef(
1861
1833
  multiline: true,
1862
1834
  editable: !disabled,
1863
1835
  style: [
1864
- styles10.input,
1836
+ styles9.input,
1865
1837
  {
1866
1838
  color: theme.colors.ink,
1867
1839
  backgroundColor: theme.colors.surface2,
@@ -1878,7 +1850,7 @@ var MessageComposer = react.forwardRef(
1878
1850
  onPress: handleSend,
1879
1851
  disabled: submitDisabled,
1880
1852
  style: [
1881
- styles10.sendBtn,
1853
+ styles9.sendBtn,
1882
1854
  {
1883
1855
  backgroundColor: submitDisabled ? theme.colors.ink3 : theme.colors.brand,
1884
1856
  opacity: submitDisabled ? 0.6 : 1
@@ -1900,7 +1872,7 @@ function EditChip({ message, onCancel }) {
1900
1872
  reactNative.View,
1901
1873
  {
1902
1874
  style: [
1903
- styles10.replyChip,
1875
+ styles9.replyChip,
1904
1876
  {
1905
1877
  backgroundColor: theme.colors.surface2,
1906
1878
  borderLeftColor: theme.colors.brand
@@ -1908,10 +1880,10 @@ function EditChip({ message, onCancel }) {
1908
1880
  ],
1909
1881
  children: [
1910
1882
  /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
1911
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles10.replyName, { color: theme.colors.brand }], children: "Editing message" }),
1912
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles10.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1883
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: "Editing message" }),
1884
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1913
1885
  ] }),
1914
- onCancel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onCancel, hitSlop: 8, style: styles10.replyCancel, children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
1886
+ 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
1915
1887
  ]
1916
1888
  }
1917
1889
  );
@@ -1928,7 +1900,7 @@ function ReplyChip({
1928
1900
  reactNative.View,
1929
1901
  {
1930
1902
  style: [
1931
- styles10.replyChip,
1903
+ styles9.replyChip,
1932
1904
  {
1933
1905
  backgroundColor: theme.colors.surface2,
1934
1906
  borderLeftColor: theme.colors.brand
@@ -1936,18 +1908,18 @@ function ReplyChip({
1936
1908
  ],
1937
1909
  children: [
1938
1910
  /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
1939
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles10.replyName, { color: theme.colors.brand }], children: [
1911
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: [
1940
1912
  "Replying to ",
1941
1913
  senderName
1942
1914
  ] }),
1943
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles10.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1915
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1944
1916
  ] }),
1945
- onCancel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onCancel, hitSlop: 8, style: styles10.replyCancel, children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
1917
+ 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
1946
1918
  ]
1947
1919
  }
1948
1920
  );
1949
1921
  }
1950
- var styles10 = reactNative.StyleSheet.create({
1922
+ var styles9 = reactNative.StyleSheet.create({
1951
1923
  wrap: {
1952
1924
  borderTopWidth: reactNative.StyleSheet.hairlineWidth,
1953
1925
  paddingTop: 8,
@@ -2044,11 +2016,11 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
2044
2016
  transparent: true,
2045
2017
  animationType: "slide",
2046
2018
  onRequestClose: () => setPickerOpen(false),
2047
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles11.backdrop, onPress: () => setPickerOpen(false), children: /* @__PURE__ */ jsxRuntime.jsxs(
2019
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles10.backdrop, onPress: () => setPickerOpen(false), children: /* @__PURE__ */ jsxRuntime.jsxs(
2048
2020
  reactNative.View,
2049
2021
  {
2050
2022
  style: [
2051
- styles11.sheet,
2023
+ styles10.sheet,
2052
2024
  {
2053
2025
  backgroundColor: theme.colors.surface,
2054
2026
  borderTopLeftRadius: theme.radii.lg,
@@ -2059,7 +2031,7 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
2059
2031
  /* @__PURE__ */ jsxRuntime.jsx(
2060
2032
  reactNative.Text,
2061
2033
  {
2062
- style: [styles11.title, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2034
+ style: [styles10.title, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2063
2035
  children: "Mention a member"
2064
2036
  }
2065
2037
  ),
@@ -2090,12 +2062,12 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
2090
2062
  }
2091
2063
  function MentionRow({ member, onPress }) {
2092
2064
  const theme = usePoolseTheme();
2093
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress, style: styles11.row, children: [
2065
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress, style: styles10.row, children: [
2094
2066
  /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: member.external_id, seed: member.external_id, size: "sm" }),
2095
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles11.rowText, { color: theme.colors.ink, fontFamily: theme.type.fontBody }], children: member.external_id })
2067
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles10.rowText, { color: theme.colors.ink, fontFamily: theme.type.fontBody }], children: member.external_id })
2096
2068
  ] });
2097
2069
  }
2098
- var styles11 = reactNative.StyleSheet.create({
2070
+ var styles10 = reactNative.StyleSheet.create({
2099
2071
  backdrop: {
2100
2072
  flex: 1,
2101
2073
  backgroundColor: "rgba(0,0,0,0.35)",
@@ -2175,12 +2147,12 @@ function MessageList({
2175
2147
  [renderItem, data.length]
2176
2148
  );
2177
2149
  if (loading && messages.length === 0) {
2178
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles12.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.brand }) });
2150
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles11.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.brand }) });
2179
2151
  }
2180
2152
  if (messages.length === 0) {
2181
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles12.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 });
2153
+ 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 });
2182
2154
  }
2183
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles12.root, children: [
2155
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles11.root, children: [
2184
2156
  /* @__PURE__ */ jsxRuntime.jsx(
2185
2157
  reactNative.FlatList,
2186
2158
  {
@@ -2207,7 +2179,7 @@ function MessageList({
2207
2179
  {
2208
2180
  onPress: scrollToBottom,
2209
2181
  style: [
2210
- styles12.jumpButton,
2182
+ styles11.jumpButton,
2211
2183
  {
2212
2184
  backgroundColor: theme.colors.brand
2213
2185
  },
@@ -2216,7 +2188,7 @@ function MessageList({
2216
2188
  accessibilityLabel: `${newCount} new ${newCount === 1 ? "message" : "messages"}, jump to latest`,
2217
2189
  children: [
2218
2190
  /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "chevron-down", size: 14, color: theme.colors.onBrand }),
2219
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles12.jumpText, { color: theme.colors.onBrand }], children: [
2191
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles11.jumpText, { color: theme.colors.onBrand }], children: [
2220
2192
  newCount,
2221
2193
  " new ",
2222
2194
  newCount === 1 ? "message" : "messages"
@@ -2226,7 +2198,7 @@ function MessageList({
2226
2198
  ) : null
2227
2199
  ] });
2228
2200
  }
2229
- var styles12 = reactNative.StyleSheet.create({
2201
+ var styles11 = reactNative.StyleSheet.create({
2230
2202
  root: {
2231
2203
  flex: 1
2232
2204
  },
@@ -2277,11 +2249,11 @@ function MessageActions({
2277
2249
  if (isSelf && onDelete)
2278
2250
  items.push({ label: "Delete", icon: "trash", onPress: onDelete, danger: true });
2279
2251
  const showEmojiRow = !!onPickReaction && emojis.length > 0;
2280
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles13.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs(
2252
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles12.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs(
2281
2253
  reactNative.View,
2282
2254
  {
2283
2255
  style: [
2284
- styles13.sheet,
2256
+ styles12.sheet,
2285
2257
  {
2286
2258
  backgroundColor: theme.colors.surface,
2287
2259
  borderTopLeftRadius: theme.radii.lg,
@@ -2290,21 +2262,21 @@ function MessageActions({
2290
2262
  theme.shadows.lg
2291
2263
  ],
2292
2264
  children: [
2293
- showEmojiRow ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles13.emojiRow, { borderBottomColor: theme.colors.border }], children: emojis.map((emoji) => /* @__PURE__ */ jsxRuntime.jsx(
2265
+ showEmojiRow ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles12.emojiRow, { borderBottomColor: theme.colors.border }], children: emojis.map((emoji) => /* @__PURE__ */ jsxRuntime.jsx(
2294
2266
  reactNative.Pressable,
2295
2267
  {
2296
2268
  onPress: () => {
2297
2269
  onPickReaction(emoji);
2298
2270
  onClose();
2299
2271
  },
2300
- style: styles13.emojiBtn,
2272
+ style: styles12.emojiBtn,
2301
2273
  hitSlop: 6,
2302
2274
  accessibilityLabel: `React with ${emoji}`,
2303
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles13.emoji, children: emoji })
2275
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles12.emoji, children: emoji })
2304
2276
  },
2305
2277
  emoji
2306
2278
  )) }) : null,
2307
- items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress: item.onPress, style: styles13.item, children: [
2279
+ items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress: item.onPress, style: styles12.item, children: [
2308
2280
  /* @__PURE__ */ jsxRuntime.jsx(
2309
2281
  PoolseIcon,
2310
2282
  {
@@ -2317,7 +2289,7 @@ function MessageActions({
2317
2289
  reactNative.Text,
2318
2290
  {
2319
2291
  style: [
2320
- styles13.itemText,
2292
+ styles12.itemText,
2321
2293
  {
2322
2294
  color: item.danger ? theme.colors.error : theme.colors.ink,
2323
2295
  fontFamily: theme.type.fontBody
@@ -2331,7 +2303,7 @@ function MessageActions({
2331
2303
  }
2332
2304
  ) }) });
2333
2305
  }
2334
- var styles13 = reactNative.StyleSheet.create({
2306
+ var styles12 = reactNative.StyleSheet.create({
2335
2307
  backdrop: {
2336
2308
  flex: 1,
2337
2309
  backgroundColor: "rgba(0,0,0,0.35)",
@@ -2396,7 +2368,7 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
2396
2368
  reactNative.View,
2397
2369
  {
2398
2370
  style: [
2399
- styles14.imagePlaceholder,
2371
+ styles13.imagePlaceholder,
2400
2372
  {
2401
2373
  width: maxWidth,
2402
2374
  height: 200,
@@ -2413,7 +2385,7 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
2413
2385
  onPress,
2414
2386
  disabled: !onPress,
2415
2387
  style: [
2416
- styles14.fileCard,
2388
+ styles13.fileCard,
2417
2389
  {
2418
2390
  backgroundColor: theme.colors.surface2,
2419
2391
  borderColor: theme.colors.border,
@@ -2426,12 +2398,12 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
2426
2398
  /* @__PURE__ */ jsxRuntime.jsx(
2427
2399
  reactNative.Text,
2428
2400
  {
2429
- style: [styles14.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2401
+ style: [styles13.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2430
2402
  numberOfLines: 1,
2431
2403
  children: attachment.original_filename ?? "file"
2432
2404
  }
2433
2405
  ),
2434
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles14.fileSize, { color: theme.colors.ink3 }], children: formatBytes2(attachment.byte_size) })
2406
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles13.fileSize, { color: theme.colors.ink3 }], children: formatBytes2(attachment.byte_size) })
2435
2407
  ] })
2436
2408
  ]
2437
2409
  }
@@ -2443,7 +2415,7 @@ function formatBytes2(n) {
2443
2415
  if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
2444
2416
  return `${(n / 1024 / 1024 / 1024).toFixed(1)} GB`;
2445
2417
  }
2446
- var styles14 = reactNative.StyleSheet.create({
2418
+ var styles13 = reactNative.StyleSheet.create({
2447
2419
  imagePlaceholder: {},
2448
2420
  fileCard: {
2449
2421
  flexDirection: "row",
@@ -2472,7 +2444,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2472
2444
  reactNative.View,
2473
2445
  {
2474
2446
  style: [
2475
- styles15.root,
2447
+ styles14.root,
2476
2448
  {
2477
2449
  borderRadius: r,
2478
2450
  ...maxWidth ? { maxWidth } : {}
@@ -2483,17 +2455,17 @@ function ImageMosaic({ images, maxWidth, radius }) {
2483
2455
  Tile,
2484
2456
  {
2485
2457
  attachment: visible[0],
2486
- style: styles15.tileFull,
2458
+ style: styles14.tileFull,
2487
2459
  onPress: () => setViewerIndex(0),
2488
2460
  overlay: null
2489
2461
  }
2490
2462
  ) : null,
2491
- n === 2 ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles15.rowEven, children: [
2463
+ n === 2 ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles14.rowEven, children: [
2492
2464
  /* @__PURE__ */ jsxRuntime.jsx(
2493
2465
  Tile,
2494
2466
  {
2495
2467
  attachment: visible[0],
2496
- style: styles15.tileHalfSquare,
2468
+ style: styles14.tileHalfSquare,
2497
2469
  onPress: () => setViewerIndex(0),
2498
2470
  overlay: null
2499
2471
  }
@@ -2502,7 +2474,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2502
2474
  Tile,
2503
2475
  {
2504
2476
  attachment: visible[1],
2505
- style: styles15.tileHalfSquare,
2477
+ style: styles14.tileHalfSquare,
2506
2478
  onPress: () => setViewerIndex(1),
2507
2479
  overlay: null
2508
2480
  }
@@ -2513,17 +2485,17 @@ function ImageMosaic({ images, maxWidth, radius }) {
2513
2485
  Tile,
2514
2486
  {
2515
2487
  attachment: visible[0],
2516
- style: styles15.tileHero,
2488
+ style: styles14.tileHero,
2517
2489
  onPress: () => setViewerIndex(0),
2518
2490
  overlay: null
2519
2491
  }
2520
2492
  ),
2521
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles15.rowEven, { marginTop: 2 }], children: [
2493
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles14.rowEven, { marginTop: 2 }], children: [
2522
2494
  /* @__PURE__ */ jsxRuntime.jsx(
2523
2495
  Tile,
2524
2496
  {
2525
2497
  attachment: visible[1],
2526
- style: styles15.tileHalfSquare,
2498
+ style: styles14.tileHalfSquare,
2527
2499
  onPress: () => setViewerIndex(1),
2528
2500
  overlay: null
2529
2501
  }
@@ -2532,7 +2504,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2532
2504
  Tile,
2533
2505
  {
2534
2506
  attachment: visible[2],
2535
- style: styles15.tileHalfSquare,
2507
+ style: styles14.tileHalfSquare,
2536
2508
  onPress: () => setViewerIndex(2),
2537
2509
  overlay: null
2538
2510
  }
@@ -2540,12 +2512,12 @@ function ImageMosaic({ images, maxWidth, radius }) {
2540
2512
  ] })
2541
2513
  ] }) : null,
2542
2514
  n === 4 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2543
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles15.rowEven, children: [
2515
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles14.rowEven, children: [
2544
2516
  /* @__PURE__ */ jsxRuntime.jsx(
2545
2517
  Tile,
2546
2518
  {
2547
2519
  attachment: visible[0],
2548
- style: styles15.tileHalfSquare,
2520
+ style: styles14.tileHalfSquare,
2549
2521
  onPress: () => setViewerIndex(0),
2550
2522
  overlay: null
2551
2523
  }
@@ -2554,18 +2526,18 @@ function ImageMosaic({ images, maxWidth, radius }) {
2554
2526
  Tile,
2555
2527
  {
2556
2528
  attachment: visible[1],
2557
- style: styles15.tileHalfSquare,
2529
+ style: styles14.tileHalfSquare,
2558
2530
  onPress: () => setViewerIndex(1),
2559
2531
  overlay: null
2560
2532
  }
2561
2533
  )
2562
2534
  ] }),
2563
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles15.rowEven, { marginTop: 2 }], children: [
2535
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles14.rowEven, { marginTop: 2 }], children: [
2564
2536
  /* @__PURE__ */ jsxRuntime.jsx(
2565
2537
  Tile,
2566
2538
  {
2567
2539
  attachment: visible[2],
2568
- style: styles15.tileHalfSquare,
2540
+ style: styles14.tileHalfSquare,
2569
2541
  onPress: () => setViewerIndex(2),
2570
2542
  overlay: null
2571
2543
  }
@@ -2574,7 +2546,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2574
2546
  Tile,
2575
2547
  {
2576
2548
  attachment: visible[3],
2577
- style: styles15.tileHalfSquare,
2549
+ style: styles14.tileHalfSquare,
2578
2550
  onPress: () => setViewerIndex(3),
2579
2551
  overlay: overflow > 0 ? `+${overflow}` : null
2580
2552
  }
@@ -2610,18 +2582,18 @@ function Tile({
2610
2582
  }) {
2611
2583
  const theme = usePoolseTheme();
2612
2584
  const { url } = react$1.useAttachmentUrl(attachment.id);
2613
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress, style: [styles15.tileBase, style], children: [
2585
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress, style: [styles14.tileBase, style], children: [
2614
2586
  url ? /* @__PURE__ */ jsxRuntime.jsx(
2615
2587
  reactNative.Image,
2616
2588
  {
2617
2589
  source: { uri: url },
2618
- style: [styles15.tileImage, { backgroundColor: theme.colors.surface2 }],
2590
+ style: [styles14.tileImage, { backgroundColor: theme.colors.surface2 }],
2619
2591
  resizeMode: "cover",
2620
2592
  accessible: true,
2621
2593
  accessibilityLabel: attachment.original_filename ?? "image"
2622
2594
  }
2623
- ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles15.tileImage, { backgroundColor: theme.colors.surface2 }] }),
2624
- overlay ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles15.overflow, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles15.overflowText, children: overlay }) }) : null
2595
+ ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles14.tileImage, { backgroundColor: theme.colors.surface2 }] }),
2596
+ overlay ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles14.overflow, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles14.overflowText, children: overlay }) }) : null
2625
2597
  ] });
2626
2598
  }
2627
2599
  function FullScreenViewer({
@@ -2630,7 +2602,7 @@ function FullScreenViewer({
2630
2602
  }) {
2631
2603
  const { url } = react$1.useAttachmentUrl(attachment.id);
2632
2604
  const { width, height } = reactNative.Dimensions.get("window");
2633
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles15.viewer, onPress: onClose, children: [
2605
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles14.viewer, onPress: onClose, children: [
2634
2606
  url ? /* @__PURE__ */ jsxRuntime.jsx(
2635
2607
  reactNative.Image,
2636
2608
  {
@@ -2646,14 +2618,14 @@ function FullScreenViewer({
2646
2618
  {
2647
2619
  onPress: onClose,
2648
2620
  hitSlop: 12,
2649
- style: styles15.closeBtn,
2621
+ style: styles14.closeBtn,
2650
2622
  accessibilityLabel: "Close viewer",
2651
2623
  children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 20, color: "#ffffff" })
2652
2624
  }
2653
2625
  )
2654
2626
  ] });
2655
2627
  }
2656
- var styles15 = reactNative.StyleSheet.create({
2628
+ var styles14 = reactNative.StyleSheet.create({
2657
2629
  root: {
2658
2630
  width: "100%",
2659
2631
  overflow: "hidden",
@@ -2808,7 +2780,7 @@ function MessageBubble({
2808
2780
  reactNative.View,
2809
2781
  {
2810
2782
  style: [
2811
- styles16.wrap,
2783
+ styles15.wrap,
2812
2784
  {
2813
2785
  alignSelf: isSelf ? "flex-end" : "flex-start",
2814
2786
  // Pixel maxWidth (not percentage) so single-line and
@@ -2822,7 +2794,7 @@ function MessageBubble({
2822
2794
  reactNative.View,
2823
2795
  {
2824
2796
  style: [
2825
- styles16.bubble,
2797
+ styles15.bubble,
2826
2798
  {
2827
2799
  backgroundColor: bubbleColor,
2828
2800
  borderColor: isSelf ? "transparent" : theme.colors.border,
@@ -2844,7 +2816,7 @@ function MessageBubble({
2844
2816
  theme.shadows.sm
2845
2817
  ],
2846
2818
  children: [
2847
- showSenderName && !isSelf && (groupPosition === "first" || groupPosition === "standalone") ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles16.senderName, { color: senderTint, fontFamily: theme.type.fontBody }], children: senderLabel }) : null,
2819
+ showSenderName && !isSelf && (groupPosition === "first" || groupPosition === "standalone") ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles15.senderName, { color: senderTint, fontFamily: theme.type.fontBody }], children: senderLabel }) : null,
2848
2820
  message.quoted_message ? /* @__PURE__ */ jsxRuntime.jsx(
2849
2821
  QuotedCard,
2850
2822
  {
@@ -2860,7 +2832,7 @@ function MessageBubble({
2860
2832
  reactNative.Text,
2861
2833
  {
2862
2834
  style: [
2863
- styles16.body,
2835
+ styles15.body,
2864
2836
  {
2865
2837
  color: theme.colors.ink3,
2866
2838
  fontFamily: theme.type.fontBody,
@@ -2885,7 +2857,7 @@ function MessageBubble({
2885
2857
  reactNative.Text,
2886
2858
  {
2887
2859
  style: [
2888
- styles16.body,
2860
+ styles15.body,
2889
2861
  {
2890
2862
  color: textColor,
2891
2863
  fontFamily: theme.type.fontBody,
@@ -2900,18 +2872,18 @@ function MessageBubble({
2900
2872
  reactNative.Text,
2901
2873
  {
2902
2874
  style: [
2903
- styles16.readMore,
2875
+ styles15.readMore,
2904
2876
  { color: isSelf ? theme.colors.onBrand : theme.colors.brand }
2905
2877
  ],
2906
2878
  children: expanded ? "Show less" : "Read more"
2907
2879
  }
2908
2880
  ) }) : null,
2909
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles16.metaRow, children: [
2881
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles15.metaRow, children: [
2910
2882
  isEdited ? /* @__PURE__ */ jsxRuntime.jsx(
2911
2883
  reactNative.Text,
2912
2884
  {
2913
2885
  style: [
2914
- styles16.editedTag,
2886
+ styles15.editedTag,
2915
2887
  {
2916
2888
  color: isSelf ? theme.colors.onBrand : theme.colors.ink3,
2917
2889
  opacity: 0.7
@@ -2924,7 +2896,7 @@ function MessageBubble({
2924
2896
  reactNative.Text,
2925
2897
  {
2926
2898
  style: [
2927
- styles16.meta,
2899
+ styles15.meta,
2928
2900
  {
2929
2901
  color: isSelf ? theme.colors.onBrand : theme.colors.ink3,
2930
2902
  opacity: isSelf ? 0.85 : 1
@@ -2933,7 +2905,7 @@ function MessageBubble({
2933
2905
  children: time
2934
2906
  }
2935
2907
  ),
2936
- isSelf && readState ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles16.readTick, children: /* @__PURE__ */ jsxRuntime.jsx(
2908
+ isSelf && readState ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles15.readTick, children: /* @__PURE__ */ jsxRuntime.jsx(
2937
2909
  PoolseIcon,
2938
2910
  {
2939
2911
  name: readState === "read" ? "check-double" : "check",
@@ -2942,7 +2914,7 @@ function MessageBubble({
2942
2914
  }
2943
2915
  ) }) : null
2944
2916
  ] }),
2945
- actionsTrigger ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles16.actionsSlot, children: actionsTrigger }) : null
2917
+ actionsTrigger ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles15.actionsSlot, children: actionsTrigger }) : null
2946
2918
  ]
2947
2919
  }
2948
2920
  )
@@ -2967,15 +2939,15 @@ function QuotedCard({
2967
2939
  onPress,
2968
2940
  disabled: !onPress,
2969
2941
  style: [
2970
- styles16.quoted,
2942
+ styles15.quoted,
2971
2943
  {
2972
2944
  backgroundColor: bg,
2973
2945
  borderLeftColor: accent
2974
2946
  }
2975
2947
  ],
2976
2948
  children: [
2977
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles16.quotedSender, { color: accent }], children: senderName }),
2978
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles16.quotedBody, { color: txt }], numberOfLines: 2, children: quoted.body ?? "\u2026" })
2949
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles15.quotedSender, { color: accent }], children: senderName }),
2950
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles15.quotedBody, { color: txt }], numberOfLines: 2, children: quoted.body ?? "\u2026" })
2979
2951
  ]
2980
2952
  }
2981
2953
  );
@@ -3007,7 +2979,7 @@ function formatTime(iso) {
3007
2979
  function isSameDay(a, b) {
3008
2980
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
3009
2981
  }
3010
- var styles16 = reactNative.StyleSheet.create({
2982
+ var styles15 = reactNative.StyleSheet.create({
3011
2983
  wrap: {
3012
2984
  marginBottom: 2
3013
2985
  },
@@ -3087,7 +3059,7 @@ function ReactionStrip({
3087
3059
  });
3088
3060
  const entries = Object.entries(reactions);
3089
3061
  if (entries.length === 0) return null;
3090
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles17.strip, children: entries.map(([emoji, userIds]) => {
3062
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles16.strip, children: entries.map(([emoji, userIds]) => {
3091
3063
  const userArr = Array.isArray(userIds) ? userIds : [];
3092
3064
  const meReacted = meId !== null && userArr.includes(meId);
3093
3065
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -3095,7 +3067,7 @@ function ReactionStrip({
3095
3067
  {
3096
3068
  onPress: () => meReacted ? removeReaction(emoji) : addReaction(emoji),
3097
3069
  style: [
3098
- styles17.chip,
3070
+ styles16.chip,
3099
3071
  {
3100
3072
  backgroundColor: meReacted ? theme.colors.brandSoft : theme.colors.surface2,
3101
3073
  borderColor: meReacted ? theme.colors.brand : theme.colors.border,
@@ -3104,12 +3076,12 @@ function ReactionStrip({
3104
3076
  }
3105
3077
  ],
3106
3078
  children: [
3107
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles17.emoji, children: emoji }),
3079
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles16.emoji, children: emoji }),
3108
3080
  /* @__PURE__ */ jsxRuntime.jsx(
3109
3081
  reactNative.Text,
3110
3082
  {
3111
3083
  style: [
3112
- styles17.count,
3084
+ styles16.count,
3113
3085
  {
3114
3086
  color: meReacted ? theme.colors.brand : theme.colors.ink2,
3115
3087
  fontWeight: meReacted ? "700" : "600"
@@ -3131,11 +3103,11 @@ function ReactionPicker({
3131
3103
  emojis = DEFAULT_EMOJIS
3132
3104
  }) {
3133
3105
  const theme = usePoolseTheme();
3134
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles17.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
3106
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles16.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
3135
3107
  reactNative.View,
3136
3108
  {
3137
3109
  style: [
3138
- styles17.sheet,
3110
+ styles16.sheet,
3139
3111
  {
3140
3112
  backgroundColor: theme.colors.surface,
3141
3113
  borderRadius: theme.radii.lg
@@ -3145,19 +3117,19 @@ function ReactionPicker({
3145
3117
  children: emojis.map((emoji) => /* @__PURE__ */ jsxRuntime.jsx(
3146
3118
  reactNative.Pressable,
3147
3119
  {
3148
- style: styles17.emojiBtn,
3120
+ style: styles16.emojiBtn,
3149
3121
  onPress: () => {
3150
3122
  onPick(emoji);
3151
3123
  onClose();
3152
3124
  },
3153
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles17.bigEmoji, children: emoji })
3125
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles16.bigEmoji, children: emoji })
3154
3126
  },
3155
3127
  emoji
3156
3128
  ))
3157
3129
  }
3158
3130
  ) }) });
3159
3131
  }
3160
- var styles17 = reactNative.StyleSheet.create({
3132
+ var styles16 = reactNative.StyleSheet.create({
3161
3133
  strip: {
3162
3134
  flexDirection: "row",
3163
3135
  flexWrap: "wrap",
@@ -3325,15 +3297,15 @@ function MessageRow({
3325
3297
  reactNative.View,
3326
3298
  {
3327
3299
  style: [
3328
- styles18.row,
3300
+ styles17.row,
3329
3301
  { alignSelf: isSelf ? "flex-end" : "flex-start" },
3330
3302
  // Lift this row above the dim backdrop the parent renders
3331
3303
  // during edit mode. zIndex covers iOS / web; elevation does
3332
3304
  // the equivalent on Android.
3333
- isEditing ? styles18.rowEditing : null
3305
+ isEditing ? styles17.rowEditing : null
3334
3306
  ],
3335
3307
  children: [
3336
- showAvatarSlot ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles18.avatarSlot, children: showAvatarRender ? /* @__PURE__ */ jsxRuntime.jsx(
3308
+ showAvatarSlot ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles17.avatarSlot, children: showAvatarRender ? /* @__PURE__ */ jsxRuntime.jsx(
3337
3309
  Avatar,
3338
3310
  {
3339
3311
  src: avatarUrl ?? null,
@@ -3342,14 +3314,14 @@ function MessageRow({
3342
3314
  size: "sm"
3343
3315
  }
3344
3316
  ) : null }) : null,
3345
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.bubbleCol, children: [
3317
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles17.bubbleCol, children: [
3346
3318
  swipeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(
3347
3319
  reactNative.Animated.View,
3348
3320
  {
3349
3321
  pointerEvents: "none",
3350
3322
  style: [
3351
- styles18.replyHint,
3352
- isSelf ? styles18.replyHintRight : styles18.replyHintLeft,
3323
+ styles17.replyHint,
3324
+ isSelf ? styles17.replyHintRight : styles17.replyHintLeft,
3353
3325
  {
3354
3326
  opacity: replyIconOpacity,
3355
3327
  transform: [{ scale: replyIconScale }],
@@ -3400,13 +3372,13 @@ function MessageRow({
3400
3372
  )
3401
3373
  }
3402
3374
  ),
3403
- threads && (msg.reply_count ?? 0) > 0 && onOpenThread ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onOpenThread, style: styles18.threadPill, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles18.threadPillText, { color: theme.colors.brand }], children: [
3375
+ 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: [
3404
3376
  "\u{1F4AC} ",
3405
3377
  msg.reply_count,
3406
3378
  " ",
3407
3379
  msg.reply_count === 1 ? "reply" : "replies"
3408
3380
  ] }) }) : null,
3409
- reactions ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles18.reactionsRow, { alignSelf: isSelf ? "flex-end" : "flex-start" }], children: /* @__PURE__ */ jsxRuntime.jsx(
3381
+ reactions ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles17.reactionsRow, { alignSelf: isSelf ? "flex-end" : "flex-start" }], children: /* @__PURE__ */ jsxRuntime.jsx(
3410
3382
  ReactionStrip,
3411
3383
  {
3412
3384
  messageId: msg.id,
@@ -3465,7 +3437,7 @@ function MessageRow({
3465
3437
  }
3466
3438
  );
3467
3439
  }
3468
- var styles18 = reactNative.StyleSheet.create({
3440
+ var styles17 = reactNative.StyleSheet.create({
3469
3441
  row: {
3470
3442
  flexDirection: "row",
3471
3443
  alignItems: "flex-end",
@@ -3625,13 +3597,13 @@ function ThreadSheet({
3625
3597
  ),
3626
3598
  [rootMessage, meId, labelFor, avatarFor]
3627
3599
  );
3628
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles19.modalRoot, children: [
3629
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles19.backdrop, onPress: onClose, accessibilityLabel: "Close thread" }),
3600
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.modalRoot, children: [
3601
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles18.backdrop, onPress: onClose, accessibilityLabel: "Close thread" }),
3630
3602
  /* @__PURE__ */ jsxRuntime.jsxs(
3631
3603
  reactNative.Animated.View,
3632
3604
  {
3633
3605
  style: [
3634
- styles19.sheet,
3606
+ styles18.sheet,
3635
3607
  {
3636
3608
  height: sheetHeight,
3637
3609
  backgroundColor: theme.colors.paper,
@@ -3643,14 +3615,14 @@ function ThreadSheet({
3643
3615
  ],
3644
3616
  children: [
3645
3617
  /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ...panHandlers, children: [
3646
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles19.dragHandle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles19.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
3647
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles19.header, { borderBottomColor: theme.colors.border }], children: [
3618
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles18.dragHandle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles18.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
3619
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles18.header, { borderBottomColor: theme.colors.border }], children: [
3648
3620
  /* @__PURE__ */ jsxRuntime.jsx(
3649
3621
  reactNative.Pressable,
3650
3622
  {
3651
3623
  onPress: onClose,
3652
3624
  hitSlop: 16,
3653
- style: [styles19.closeBtn, { backgroundColor: theme.colors.surface2 }],
3625
+ style: [styles18.closeBtn, { backgroundColor: theme.colors.surface2 }],
3654
3626
  accessibilityLabel: "Close thread",
3655
3627
  children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 18, color: theme.colors.ink })
3656
3628
  }
@@ -3659,7 +3631,7 @@ function ThreadSheet({
3659
3631
  reactNative.Text,
3660
3632
  {
3661
3633
  style: [
3662
- styles19.title,
3634
+ styles18.title,
3663
3635
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
3664
3636
  ],
3665
3637
  children: "Thread"
@@ -3670,12 +3642,12 @@ function ThreadSheet({
3670
3642
  /* @__PURE__ */ jsxRuntime.jsxs(
3671
3643
  reactNative.KeyboardAvoidingView,
3672
3644
  {
3673
- style: styles19.kavBody,
3645
+ style: styles18.kavBody,
3674
3646
  behavior: reactNative.Platform.OS === "ios" ? "padding" : "height",
3675
3647
  keyboardVerticalOffset: reactNative.Platform.OS === "ios" ? gutter : 0,
3676
3648
  children: [
3677
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles19.rootPreview, { backgroundColor: theme.colors.surface2 }], children: rootRow }),
3678
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles19.listWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
3649
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles18.rootPreview, { backgroundColor: theme.colors.surface2 }], children: rootRow }),
3650
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles18.listWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
3679
3651
  MessageList,
3680
3652
  {
3681
3653
  messages: replies,
@@ -3711,7 +3683,7 @@ function ThreadSheet({
3711
3683
  )
3712
3684
  ] });
3713
3685
  }
3714
- var styles19 = reactNative.StyleSheet.create({
3686
+ var styles18 = reactNative.StyleSheet.create({
3715
3687
  modalRoot: {
3716
3688
  flex: 1,
3717
3689
  justifyContent: "flex-end"
@@ -3783,7 +3755,7 @@ function TypingIndicator({ typing, labelFor }) {
3783
3755
  const ids = Array.from(typing);
3784
3756
  const isTyping = ids.length > 0;
3785
3757
  if (!isTyping) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { height: 0 } });
3786
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles20.row, children: [
3758
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles19.row, children: [
3787
3759
  /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}),
3788
3760
  /* @__PURE__ */ jsxRuntime.jsx(TypingLabel, { ids, ...labelFor ? { labelFor } : {} })
3789
3761
  ] });
@@ -3828,7 +3800,7 @@ function TypingDots() {
3828
3800
  ],
3829
3801
  opacity: val.interpolate({ inputRange: [0, 1], outputRange: [0.5, 1] })
3830
3802
  });
3831
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles20.dotsWrap, children: [
3803
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles19.dotsWrap, children: [
3832
3804
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: dot(a) }),
3833
3805
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: dot(b) }),
3834
3806
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: dot(c) })
@@ -3844,9 +3816,9 @@ function TypingLabel({
3844
3816
  const name1 = useDisplayName(id1, labelFor);
3845
3817
  const name2 = useDisplayName(id2, labelFor);
3846
3818
  const text = ids.length === 1 ? `${name1} is typing` : ids.length === 2 ? `${name1} and ${name2} are typing` : `${ids.length} people are typing`;
3847
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles20.label, { color: theme.colors.ink2, fontFamily: theme.type.fontBody }], children: text });
3819
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles19.label, { color: theme.colors.ink2, fontFamily: theme.type.fontBody }], children: text });
3848
3820
  }
3849
- var styles20 = reactNative.StyleSheet.create({
3821
+ var styles19 = reactNative.StyleSheet.create({
3850
3822
  row: {
3851
3823
  flexDirection: "row",
3852
3824
  alignItems: "center",
@@ -4084,12 +4056,12 @@ function ConversationView({
4084
4056
  return /* @__PURE__ */ jsxRuntime.jsx(UploadProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
4085
4057
  reactNative.KeyboardAvoidingView,
4086
4058
  {
4087
- style: [styles21.root, { backgroundColor: theme.colors.paper }],
4059
+ style: [styles20.root, { backgroundColor: theme.colors.paper }],
4088
4060
  behavior: reactNative.Platform.OS === "ios" ? "padding" : "height",
4089
4061
  keyboardVerticalOffset: keyboardOffset,
4090
4062
  children: [
4091
4063
  headerNode,
4092
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles21.listWrap, children: [
4064
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles20.listWrap, children: [
4093
4065
  /* @__PURE__ */ jsxRuntime.jsx(
4094
4066
  MessageList,
4095
4067
  {
@@ -4107,7 +4079,7 @@ function ConversationView({
4107
4079
  editingId ? /* @__PURE__ */ jsxRuntime.jsx(
4108
4080
  reactNative.Pressable,
4109
4081
  {
4110
- style: styles21.editBackdrop,
4082
+ style: styles20.editBackdrop,
4111
4083
  onPress: () => setEditingId(null),
4112
4084
  accessibilityLabel: "Cancel editing"
4113
4085
  }
@@ -4142,7 +4114,7 @@ function readStateForMessage(msg, members, meId, sequenceByMessageId) {
4142
4114
  });
4143
4115
  return anyRead ? "read" : "sent";
4144
4116
  }
4145
- var styles21 = reactNative.StyleSheet.create({
4117
+ var styles20 = reactNative.StyleSheet.create({
4146
4118
  root: {
4147
4119
  flex: 1
4148
4120
  },
@@ -4224,13 +4196,13 @@ function UserPickerSheet({
4224
4196
  animationType: "slide",
4225
4197
  onRequestClose: handleClose,
4226
4198
  statusBarTranslucent: true,
4227
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles22.modalRoot, children: [
4228
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles22.backdrop, onPress: handleClose }),
4199
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles21.modalRoot, children: [
4200
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles21.backdrop, onPress: handleClose }),
4229
4201
  /* @__PURE__ */ jsxRuntime.jsxs(
4230
4202
  reactNative.Animated.View,
4231
4203
  {
4232
4204
  style: [
4233
- styles22.sheet,
4205
+ styles21.sheet,
4234
4206
  {
4235
4207
  backgroundColor: theme.colors.paper,
4236
4208
  borderTopLeftRadius: theme.radii.xl,
@@ -4240,14 +4212,14 @@ function UserPickerSheet({
4240
4212
  theme.shadows.lg
4241
4213
  ],
4242
4214
  children: [
4243
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ...panHandlers, style: styles22.dragHandle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles22.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
4244
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles22.header, children: [
4215
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ...panHandlers, style: styles21.dragHandle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles21.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
4216
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles21.header, children: [
4245
4217
  /* @__PURE__ */ jsxRuntime.jsx(
4246
4218
  reactNative.Pressable,
4247
4219
  {
4248
4220
  onPress: handleClose,
4249
4221
  hitSlop: 16,
4250
- style: [styles22.closeBtn, { backgroundColor: theme.colors.surface2 }],
4222
+ style: [styles21.closeBtn, { backgroundColor: theme.colors.surface2 }],
4251
4223
  accessibilityLabel: "Close picker",
4252
4224
  children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "close", size: 18, color: theme.colors.ink })
4253
4225
  }
@@ -4256,7 +4228,7 @@ function UserPickerSheet({
4256
4228
  reactNative.Text,
4257
4229
  {
4258
4230
  style: [
4259
- styles22.title,
4231
+ styles21.title,
4260
4232
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
4261
4233
  ],
4262
4234
  numberOfLines: 1,
@@ -4270,7 +4242,7 @@ function UserPickerSheet({
4270
4242
  disabled: !canCreate,
4271
4243
  hitSlop: 6,
4272
4244
  style: [
4273
- styles22.createBtn,
4245
+ styles21.createBtn,
4274
4246
  {
4275
4247
  backgroundColor: canCreate ? theme.colors.brand : theme.colors.surface2,
4276
4248
  opacity: canCreate ? 1 : 0.6
@@ -4301,7 +4273,7 @@ function UserPickerSheet({
4301
4273
  value: groupName,
4302
4274
  onChangeText: setGroupName,
4303
4275
  style: [
4304
- styles22.groupNameInput,
4276
+ styles21.groupNameInput,
4305
4277
  {
4306
4278
  color: theme.colors.ink,
4307
4279
  backgroundColor: theme.colors.surface2,
@@ -4320,7 +4292,7 @@ function UserPickerSheet({
4320
4292
  value: query,
4321
4293
  onChangeText: setQuery,
4322
4294
  style: [
4323
- styles22.searchInput,
4295
+ styles21.searchInput,
4324
4296
  {
4325
4297
  color: theme.colors.ink,
4326
4298
  backgroundColor: theme.colors.surface2,
@@ -4336,7 +4308,7 @@ function UserPickerSheet({
4336
4308
  {
4337
4309
  data: filtered,
4338
4310
  keyExtractor: (u) => u.externalId,
4339
- ListEmptyComponent: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles22.empty, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles22.emptyText, { color: theme.colors.ink3 }], children: query ? "No matches." : "No users to show." }) }),
4311
+ ListEmptyComponent: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles21.empty, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles21.emptyText, { color: theme.colors.ink3 }], children: query ? "No matches." : "No users to show." }) }),
4340
4312
  renderItem: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(
4341
4313
  UserRow,
4342
4314
  {
@@ -4374,29 +4346,29 @@ function UserRow({
4374
4346
  {
4375
4347
  onPress,
4376
4348
  style: [
4377
- styles22.row,
4349
+ styles21.row,
4378
4350
  {
4379
4351
  backgroundColor: selected ? theme.colors.brandSoft : "transparent"
4380
4352
  }
4381
4353
  ],
4382
4354
  children: [
4383
- user.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: user.avatarUrl }, style: styles22.avatarImg }) : /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: user.name, seed: user.externalId, size: "md" }),
4384
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles22.body, children: [
4355
+ user.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: user.avatarUrl }, style: styles21.avatarImg }) : /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: user.name, seed: user.externalId, size: "md" }),
4356
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles21.body, children: [
4385
4357
  /* @__PURE__ */ jsxRuntime.jsx(
4386
4358
  reactNative.Text,
4387
4359
  {
4388
- style: [styles22.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4360
+ style: [styles21.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4389
4361
  numberOfLines: 1,
4390
4362
  children: user.name
4391
4363
  }
4392
4364
  ),
4393
- user.subtitle ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles22.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: user.subtitle }) : null
4365
+ user.subtitle ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles21.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: user.subtitle }) : null
4394
4366
  ] }),
4395
4367
  isGroup ? /* @__PURE__ */ jsxRuntime.jsx(
4396
4368
  reactNative.View,
4397
4369
  {
4398
4370
  style: [
4399
- styles22.checkbox,
4371
+ styles21.checkbox,
4400
4372
  {
4401
4373
  borderColor: selected ? theme.colors.brand : theme.colors.border,
4402
4374
  backgroundColor: selected ? theme.colors.brand : "transparent"
@@ -4409,7 +4381,7 @@ function UserRow({
4409
4381
  }
4410
4382
  );
4411
4383
  }
4412
- var styles22 = reactNative.StyleSheet.create({
4384
+ var styles21 = reactNative.StyleSheet.create({
4413
4385
  modalRoot: {
4414
4386
  flex: 1,
4415
4387
  justifyContent: "flex-end"
@@ -4669,7 +4641,7 @@ var PoolseInbox = react.forwardRef(
4669
4641
  },
4670
4642
  [renderConversationRow, meExternalId, labelFor, avatarFor, unreadCounts]
4671
4643
  );
4672
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles23.root, { backgroundColor: theme.colors.paper }], children: [
4644
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles22.root, { backgroundColor: theme.colors.paper }], children: [
4673
4645
  /* @__PURE__ */ jsxRuntime.jsxs(
4674
4646
  reactNative.Animated.View,
4675
4647
  {
@@ -4680,7 +4652,7 @@ var PoolseInbox = react.forwardRef(
4680
4652
  reactNative.View,
4681
4653
  {
4682
4654
  style: [
4683
- styles23.listHeader,
4655
+ styles22.listHeader,
4684
4656
  {
4685
4657
  backgroundColor: theme.colors.surface,
4686
4658
  borderBottomColor: theme.colors.border
@@ -4691,13 +4663,13 @@ var PoolseInbox = react.forwardRef(
4691
4663
  reactNative.Text,
4692
4664
  {
4693
4665
  style: [
4694
- styles23.listTitle,
4666
+ styles22.listTitle,
4695
4667
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
4696
4668
  ],
4697
4669
  children: title
4698
4670
  }
4699
4671
  ),
4700
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles23.headerActions, children: [
4672
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles22.headerActions, children: [
4701
4673
  users ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4702
4674
  /* @__PURE__ */ jsxRuntime.jsx(
4703
4675
  HeaderIconButton,
@@ -4810,7 +4782,7 @@ function HeaderIconButton({
4810
4782
  hitSlop: 10,
4811
4783
  accessibilityLabel: label,
4812
4784
  style: ({ pressed }) => [
4813
- styles23.iconBtn,
4785
+ styles22.iconBtn,
4814
4786
  {
4815
4787
  backgroundColor: pressed ? theme.colors.surface2 : "transparent"
4816
4788
  }
@@ -4840,31 +4812,31 @@ function DefaultConversationRow({
4840
4812
  {
4841
4813
  onPress,
4842
4814
  style: [
4843
- styles23.row,
4815
+ styles22.row,
4844
4816
  {
4845
4817
  backgroundColor: selected ? theme.colors.surface2 : "transparent",
4846
4818
  borderBottomColor: theme.colors.border
4847
4819
  }
4848
4820
  ],
4849
4821
  children: [
4850
- avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: avatarUrl }, style: styles23.rowAvatar }) : /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
4851
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles23.rowBody, children: [
4822
+ avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Image, { source: { uri: avatarUrl }, style: styles22.rowAvatar }) : /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
4823
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles22.rowBody, children: [
4852
4824
  /* @__PURE__ */ jsxRuntime.jsx(
4853
4825
  reactNative.Text,
4854
4826
  {
4855
- style: [styles23.rowTitle, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4827
+ style: [styles22.rowTitle, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4856
4828
  numberOfLines: 1,
4857
4829
  children: title
4858
4830
  }
4859
4831
  ),
4860
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.rowSubtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview ?? (isDirect ? otherExtId ?? "" : `${(conv.member_external_ids ?? []).length} members`) })
4832
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles22.rowSubtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview ?? (isDirect ? otherExtId ?? "" : `${(conv.member_external_ids ?? []).length} members`) })
4861
4833
  ] }),
4862
- unread > 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles23.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
4834
+ unread > 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles22.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles22.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
4863
4835
  ]
4864
4836
  }
4865
4837
  );
4866
4838
  }
4867
- var styles23 = reactNative.StyleSheet.create({
4839
+ var styles22 = reactNative.StyleSheet.create({
4868
4840
  root: {
4869
4841
  flex: 1,
4870
4842
  overflow: "hidden"
@@ -5068,10 +5040,10 @@ function VoiceRoomBar({
5068
5040
  reactNative.View,
5069
5041
  {
5070
5042
  style: [
5071
- styles24.bar,
5043
+ styles23.bar,
5072
5044
  { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
5073
5045
  ],
5074
- children: unavailableSlot ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.hint, { color: theme.colors.ink3 }], children: "Voice needs react-native-webrtc and a development build." })
5046
+ children: unavailableSlot ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Voice needs react-native-webrtc and a development build." })
5075
5047
  }
5076
5048
  );
5077
5049
  }
@@ -5079,22 +5051,22 @@ function VoiceRoomBar({
5079
5051
  reactNative.View,
5080
5052
  {
5081
5053
  style: [
5082
- styles24.bar,
5054
+ styles23.bar,
5083
5055
  { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
5084
5056
  ],
5085
5057
  children: [
5086
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles24.row, children: [
5058
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles23.row, children: [
5087
5059
  /* @__PURE__ */ jsxRuntime.jsx(
5088
5060
  reactNative.View,
5089
5061
  {
5090
5062
  style: [
5091
- styles24.dot,
5063
+ styles23.dot,
5092
5064
  { backgroundColor: connected ? theme.colors.brand : theme.colors.ink3 }
5093
5065
  ]
5094
5066
  }
5095
5067
  ),
5096
5068
  connected ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5097
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles24.people, children: participants.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.hint, { color: theme.colors.ink3 }], children: "Waiting for others\u2026" }) : participants.map((p) => /* @__PURE__ */ jsxRuntime.jsx(ParticipantChip, { participant: p, label: label(p.userId) }, p.userId)) }),
5069
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles23.people, children: participants.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Waiting for others\u2026" }) : participants.map((p) => /* @__PURE__ */ jsxRuntime.jsx(ParticipantChip, { participant: p, label: label(p.userId) }, p.userId)) }),
5098
5070
  /* @__PURE__ */ jsxRuntime.jsx(
5099
5071
  reactNative.Pressable,
5100
5072
  {
@@ -5102,7 +5074,7 @@ function VoiceRoomBar({
5102
5074
  accessibilityRole: "button",
5103
5075
  accessibilityState: { selected: muted },
5104
5076
  style: [
5105
- styles24.btn,
5077
+ styles23.btn,
5106
5078
  {
5107
5079
  borderColor: muted ? theme.colors.brand : theme.colors.border,
5108
5080
  backgroundColor: muted ? theme.colors.brandSoft : theme.colors.paper
@@ -5116,25 +5088,25 @@ function VoiceRoomBar({
5116
5088
  {
5117
5089
  onPress: leave,
5118
5090
  accessibilityRole: "button",
5119
- style: [styles24.btn, { borderColor: theme.colors.error }],
5091
+ style: [styles23.btn, { borderColor: theme.colors.error }],
5120
5092
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.error }, children: "Leave" })
5121
5093
  }
5122
5094
  )
5123
5095
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5124
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.hint, { color: theme.colors.ink3 }], children: status === "connecting" ? "Connecting\u2026" : "Talk to everyone here" }),
5096
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: status === "connecting" ? "Connecting\u2026" : "Talk to everyone here" }),
5125
5097
  /* @__PURE__ */ jsxRuntime.jsx(
5126
5098
  reactNative.Pressable,
5127
5099
  {
5128
5100
  onPress: () => void join(),
5129
5101
  disabled: status === "connecting",
5130
5102
  accessibilityRole: "button",
5131
- style: [styles24.btn, styles24.btnPrimary, { backgroundColor: theme.colors.brand }],
5103
+ style: [styles23.btn, styles23.btnPrimary, { backgroundColor: theme.colors.brand }],
5132
5104
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.onBrand, fontWeight: "600" }, children: status === "connecting" ? "Joining\u2026" : "Join Voice" })
5133
5105
  }
5134
5106
  )
5135
5107
  ] })
5136
5108
  ] }),
5137
- error && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.error, { color: theme.colors.error }], children: error.message })
5109
+ error && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.error, { color: theme.colors.error }], children: error.message })
5138
5110
  ]
5139
5111
  }
5140
5112
  );
@@ -5145,7 +5117,7 @@ function ParticipantChip({ participant, label }) {
5145
5117
  reactNative.View,
5146
5118
  {
5147
5119
  style: [
5148
- styles24.chip,
5120
+ styles23.chip,
5149
5121
  {
5150
5122
  backgroundColor: theme.colors.paper,
5151
5123
  // The speaking ring is a border colour swap rather than an
@@ -5164,7 +5136,7 @@ function ParticipantChip({ participant, label }) {
5164
5136
  }
5165
5137
  );
5166
5138
  }
5167
- var styles24 = reactNative.StyleSheet.create({
5139
+ var styles23 = reactNative.StyleSheet.create({
5168
5140
  bar: { paddingHorizontal: 14, paddingVertical: 8, borderBottomWidth: reactNative.StyleSheet.hairlineWidth },
5169
5141
  row: { flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" },
5170
5142
  dot: { width: 8, height: 8, borderRadius: 4 },
@@ -5183,6 +5155,682 @@ var styles24 = reactNative.StyleSheet.create({
5183
5155
  hint: { fontSize: 13, flexShrink: 1 },
5184
5156
  error: { fontSize: 12, marginTop: 6 }
5185
5157
  });
5158
+
5159
+ // src/voice/callTheme.ts
5160
+ var callColors = {
5161
+ /** Behind everything. Near-black, warmed very slightly to match the brand. */
5162
+ bg: "#101012",
5163
+ /** Behind a participant tile with no video. */
5164
+ tile: "#1c1c20",
5165
+ /** Scrim under the top/bottom chrome so text stays legible over video. */
5166
+ scrim: "rgba(0,0,0,0.45)",
5167
+ text: "#ffffff",
5168
+ textDim: "rgba(255,255,255,0.72)",
5169
+ textFaint: "rgba(255,255,255,0.5)",
5170
+ /** A control that is off / inactive — translucent over whatever is behind. */
5171
+ control: "rgba(255,255,255,0.16)",
5172
+ /**
5173
+ * A control that is ENGAGED (muted, speaker on, camera off).
5174
+ *
5175
+ * Inverted rather than tinted: at a glance the user needs to see
5176
+ * *which* controls are doing something, and a solid white circle is
5177
+ * unmistakable against five translucent ones in a dim room.
5178
+ */
5179
+ controlActive: "#ffffff",
5180
+ controlActiveIcon: "#101012",
5181
+ danger: "#ef4444",
5182
+ accept: "#22c55e",
5183
+ onAction: "#ffffff"
5184
+ };
5185
+ var CONTROL_SIZE = 58;
5186
+ var CONTROL_SIZE_LARGE = 68;
5187
+ var ICONS2 = {
5188
+ speaker: "speaker",
5189
+ earpiece: "phone",
5190
+ headset: "headphones",
5191
+ bluetooth: "bluetooth",
5192
+ auto: "phone"
5193
+ };
5194
+ function AudioRouteSheet({
5195
+ visible,
5196
+ routes,
5197
+ selectedRouteId,
5198
+ onSelect,
5199
+ onClose
5200
+ }) {
5201
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles24.backdrop, onPress: onClose, accessibilityRole: "button", children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles24.sheet, onPress: () => {
5202
+ }, children: [
5203
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles24.grabber }),
5204
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles24.title, children: "Audio output" }),
5205
+ routes.map((route) => {
5206
+ const selected = route.id === selectedRouteId;
5207
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5208
+ reactNative.Pressable,
5209
+ {
5210
+ onPress: () => {
5211
+ onSelect(route.id);
5212
+ onClose();
5213
+ },
5214
+ accessibilityRole: "button",
5215
+ accessibilityState: { selected },
5216
+ style: ({ pressed }) => [styles24.row, pressed && styles24.rowPressed],
5217
+ children: [
5218
+ /* @__PURE__ */ jsxRuntime.jsx(
5219
+ PoolseIcon,
5220
+ {
5221
+ name: ICONS2[route.kind],
5222
+ size: 21,
5223
+ color: selected ? callColors.accept : callColors.text,
5224
+ label: null
5225
+ }
5226
+ ),
5227
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.label, selected && styles24.labelSelected], children: route.label }),
5228
+ selected && /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "check", size: 19, color: callColors.accept, label: "selected" })
5229
+ ]
5230
+ },
5231
+ route.id
5232
+ );
5233
+ })
5234
+ ] }) }) });
5235
+ }
5236
+ var styles24 = reactNative.StyleSheet.create({
5237
+ backdrop: { flex: 1, backgroundColor: "rgba(0,0,0,0.55)", justifyContent: "flex-end" },
5238
+ sheet: {
5239
+ backgroundColor: callColors.tile,
5240
+ borderTopLeftRadius: 22,
5241
+ borderTopRightRadius: 22,
5242
+ paddingHorizontal: 12,
5243
+ paddingTop: 10,
5244
+ paddingBottom: 34,
5245
+ gap: 2
5246
+ },
5247
+ grabber: {
5248
+ alignSelf: "center",
5249
+ width: 38,
5250
+ height: 4,
5251
+ borderRadius: 2,
5252
+ backgroundColor: callColors.textFaint,
5253
+ marginBottom: 12
5254
+ },
5255
+ title: {
5256
+ color: callColors.textDim,
5257
+ fontSize: 12.5,
5258
+ fontWeight: "600",
5259
+ textTransform: "uppercase",
5260
+ letterSpacing: 0.6,
5261
+ paddingHorizontal: 12,
5262
+ paddingBottom: 8
5263
+ },
5264
+ row: {
5265
+ flexDirection: "row",
5266
+ alignItems: "center",
5267
+ gap: 14,
5268
+ paddingHorizontal: 12,
5269
+ paddingVertical: 15,
5270
+ borderRadius: 14
5271
+ },
5272
+ rowPressed: { backgroundColor: "rgba(255,255,255,0.08)" },
5273
+ label: { flex: 1, color: callColors.text, fontSize: 16, fontWeight: "500" },
5274
+ labelSelected: { color: callColors.accept }
5275
+ });
5276
+ function CallAvatar({ name, seed, size, speaking = false }) {
5277
+ const ring = Math.max(2, Math.round(size * 0.045));
5278
+ const outer = size + ring * 4;
5279
+ return /* @__PURE__ */ jsxRuntime.jsx(
5280
+ reactNative.View,
5281
+ {
5282
+ style: [
5283
+ styles25.ring,
5284
+ {
5285
+ width: outer,
5286
+ height: outer,
5287
+ borderRadius: outer / 2,
5288
+ borderWidth: ring,
5289
+ borderColor: speaking ? callColors.accept : "transparent"
5290
+ }
5291
+ ],
5292
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5293
+ reactNative.View,
5294
+ {
5295
+ style: [
5296
+ styles25.circle,
5297
+ {
5298
+ width: size,
5299
+ height: size,
5300
+ borderRadius: size / 2,
5301
+ backgroundColor: userColor(seed)
5302
+ }
5303
+ ],
5304
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5305
+ reactNative.Text,
5306
+ {
5307
+ style: [styles25.initials, { fontSize: Math.round(size * 0.38) }],
5308
+ numberOfLines: 1,
5309
+ allowFontScaling: false,
5310
+ children: initialsOf(name)
5311
+ }
5312
+ )
5313
+ }
5314
+ )
5315
+ }
5316
+ );
5317
+ }
5318
+ function initialsOf(name) {
5319
+ const parts = name.trim().split(/\s+/).filter(Boolean);
5320
+ if (parts.length === 0) return "?";
5321
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
5322
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
5323
+ }
5324
+ var styles25 = reactNative.StyleSheet.create({
5325
+ ring: { alignItems: "center", justifyContent: "center" },
5326
+ circle: { alignItems: "center", justifyContent: "center" },
5327
+ initials: { color: "#ffffff", fontWeight: "600" }
5328
+ });
5329
+ function CallControlButton({
5330
+ icon,
5331
+ label,
5332
+ active = false,
5333
+ tone = "neutral",
5334
+ large = false,
5335
+ disabled = false,
5336
+ onPress,
5337
+ accessibilityLabel
5338
+ }) {
5339
+ const size = large ? CONTROL_SIZE_LARGE : CONTROL_SIZE;
5340
+ const background = tone === "danger" ? callColors.danger : tone === "accept" ? callColors.accept : active ? callColors.controlActive : callColors.control;
5341
+ const iconColor = tone === "neutral" && active ? callColors.controlActiveIcon : callColors.onAction;
5342
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles26.slot, children: [
5343
+ /* @__PURE__ */ jsxRuntime.jsx(
5344
+ reactNative.Pressable,
5345
+ {
5346
+ onPress,
5347
+ disabled,
5348
+ accessibilityRole: "button",
5349
+ accessibilityState: { disabled, selected: active },
5350
+ accessibilityLabel: accessibilityLabel ?? label ?? icon,
5351
+ hitSlop: 8,
5352
+ style: ({ pressed }) => [
5353
+ styles26.button,
5354
+ {
5355
+ width: size,
5356
+ height: size,
5357
+ borderRadius: size / 2,
5358
+ backgroundColor: background,
5359
+ // Feedback without a layout shift — scaling a round button
5360
+ // inside a fixed slot keeps the row from twitching.
5361
+ opacity: disabled ? 0.4 : pressed ? 0.75 : 1
5362
+ }
5363
+ ],
5364
+ children: /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: icon, size: large ? 30 : 25, color: iconColor, label: null })
5365
+ }
5366
+ ),
5367
+ label ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: styles26.label, children: label }) : null
5368
+ ] });
5369
+ }
5370
+ var styles26 = reactNative.StyleSheet.create({
5371
+ // Fixed width so captions of different lengths cannot shift the row.
5372
+ slot: { width: 76, alignItems: "center", gap: 7 },
5373
+ button: { alignItems: "center", justifyContent: "center" },
5374
+ label: { color: callColors.textDim, fontSize: 11.5, fontWeight: "500" }
5375
+ });
5376
+ function CallParticipantTile({
5377
+ participant,
5378
+ VideoTrack,
5379
+ label,
5380
+ style,
5381
+ avatarSize = 72,
5382
+ bleed = false,
5383
+ hideName = false
5384
+ }) {
5385
+ const name = participant.isSelf ? "You" : participant.name ?? label(participant.userId);
5386
+ const hasVideo = Boolean(participant.videoTrackRef && VideoTrack);
5387
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5388
+ reactNative.View,
5389
+ {
5390
+ style: [
5391
+ styles27.tile,
5392
+ bleed ? styles27.bleed : styles27.rounded,
5393
+ {
5394
+ backgroundColor: callColors.tile,
5395
+ // A ring rather than a badge: it reads at a glance across a
5396
+ // grid, and it doesn't cover anyone's face.
5397
+ borderColor: participant.speaking ? callColors.accept : "transparent"
5398
+ },
5399
+ style
5400
+ ],
5401
+ children: [
5402
+ hasVideo && VideoTrack ? /* @__PURE__ */ jsxRuntime.jsx(
5403
+ VideoTrack,
5404
+ {
5405
+ trackRef: participant.videoTrackRef,
5406
+ style: reactNative.StyleSheet.absoluteFillObject,
5407
+ objectFit: "cover",
5408
+ mirror: participant.isSelf,
5409
+ zOrder: participant.isSelf ? 1 : 0
5410
+ }
5411
+ ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles27.placeholder, children: /* @__PURE__ */ jsxRuntime.jsx(
5412
+ CallAvatar,
5413
+ {
5414
+ name,
5415
+ seed: participant.userId,
5416
+ size: avatarSize,
5417
+ speaking: participant.speaking
5418
+ }
5419
+ ) }),
5420
+ !hideName && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles27.chip, bleed ? styles27.chipBleed : styles27.chipTile], children: [
5421
+ !participant.micEnabled && // Worth showing even for yourself — it is the answer to
5422
+ // "why can nobody hear me".
5423
+ /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "mic-off", size: 13, color: callColors.text, label: "muted" }),
5424
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: styles27.name, children: name })
5425
+ ] })
5426
+ ]
5427
+ }
5428
+ );
5429
+ }
5430
+ var styles27 = reactNative.StyleSheet.create({
5431
+ tile: { overflow: "hidden", justifyContent: "flex-end", borderWidth: 2 },
5432
+ rounded: { borderRadius: 18 },
5433
+ bleed: { borderRadius: 0, borderWidth: 0 },
5434
+ placeholder: { ...reactNative.StyleSheet.absoluteFillObject, alignItems: "center", justifyContent: "center" },
5435
+ chip: {
5436
+ flexDirection: "row",
5437
+ alignItems: "center",
5438
+ gap: 5,
5439
+ alignSelf: "flex-start",
5440
+ borderRadius: 999,
5441
+ // Legible over both a bright video frame and a flat placeholder.
5442
+ backgroundColor: "rgba(0,0,0,0.55)"
5443
+ },
5444
+ chipTile: { margin: 8, paddingHorizontal: 9, paddingVertical: 4.5, maxWidth: "92%" },
5445
+ chipBleed: { margin: 14, paddingHorizontal: 11, paddingVertical: 6, maxWidth: "80%" },
5446
+ name: { color: callColors.text, fontSize: 12.5, fontWeight: "600", flexShrink: 1 }
5447
+ });
5448
+ var PIP_WIDTH = 108;
5449
+ var PIP_HEIGHT = 152;
5450
+ var MARGIN = 14;
5451
+ function LocalVideoPip({
5452
+ participant,
5453
+ VideoTrack,
5454
+ label,
5455
+ bounds,
5456
+ insets,
5457
+ onPress
5458
+ }) {
5459
+ const top = insets?.top ?? 0;
5460
+ const bottom = insets?.bottom ?? 0;
5461
+ const corners = react.useMemo(() => {
5462
+ const right = Math.max(MARGIN, bounds.width - PIP_WIDTH - MARGIN);
5463
+ const low = Math.max(MARGIN, bounds.height - PIP_HEIGHT - bottom - MARGIN);
5464
+ const high = top + MARGIN;
5465
+ return [
5466
+ { x: MARGIN, y: high },
5467
+ { x: right, y: high },
5468
+ { x: MARGIN, y: low },
5469
+ { x: right, y: low }
5470
+ ];
5471
+ }, [bounds.width, bounds.height, top, bottom]);
5472
+ const [corner, setCorner] = react.useState(1);
5473
+ const pan = react.useRef(new reactNative.Animated.ValueXY(corners[1])).current;
5474
+ const cornersRef = react.useRef(corners);
5475
+ cornersRef.current = corners;
5476
+ const cornerRef = react.useRef(corner);
5477
+ cornerRef.current = corner;
5478
+ const dragged = react.useRef(false);
5479
+ react.useEffect(() => {
5480
+ if (dragged.current) return;
5481
+ const at = corners[corner];
5482
+ if (at) pan.setValue(at);
5483
+ }, [corners, corner, pan]);
5484
+ const responder = react.useMemo(
5485
+ () => reactNative.PanResponder.create({
5486
+ // Claim the gesture only once it is clearly a drag. A lower
5487
+ // threshold would swallow the taps that flip the camera.
5488
+ onMoveShouldSetPanResponder: (_evt, g) => Math.abs(g.dx) > 4 || Math.abs(g.dy) > 4,
5489
+ onPanResponderGrant: () => {
5490
+ dragged.current = true;
5491
+ const at = cornersRef.current[cornerRef.current] ?? cornersRef.current[1];
5492
+ pan.setOffset({ x: at.x, y: at.y });
5493
+ pan.setValue({ x: 0, y: 0 });
5494
+ },
5495
+ onPanResponderMove: reactNative.Animated.event([null, { dx: pan.x, dy: pan.y }], {
5496
+ useNativeDriver: false
5497
+ }),
5498
+ onPanResponderRelease: (_evt, g) => {
5499
+ pan.flattenOffset();
5500
+ const at = cornersRef.current[cornerRef.current] ?? cornersRef.current[1];
5501
+ const dropped = { x: at.x + g.dx, y: at.y + g.dy };
5502
+ let best = 0;
5503
+ let bestDistance = Number.POSITIVE_INFINITY;
5504
+ cornersRef.current.forEach((c, index) => {
5505
+ const distance = (c.x - dropped.x) ** 2 + (c.y - dropped.y) ** 2;
5506
+ if (distance < bestDistance) {
5507
+ bestDistance = distance;
5508
+ best = index;
5509
+ }
5510
+ });
5511
+ setCorner(best);
5512
+ reactNative.Animated.spring(pan, {
5513
+ toValue: cornersRef.current[best],
5514
+ useNativeDriver: false,
5515
+ friction: 8,
5516
+ tension: 70
5517
+ }).start(() => {
5518
+ dragged.current = false;
5519
+ });
5520
+ }
5521
+ }),
5522
+ [pan]
5523
+ );
5524
+ return /* @__PURE__ */ jsxRuntime.jsx(
5525
+ reactNative.Animated.View,
5526
+ {
5527
+ ...responder.panHandlers,
5528
+ style: [styles28.pip, { transform: pan.getTranslateTransform() }],
5529
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5530
+ reactNative.Pressable,
5531
+ {
5532
+ onPress: () => {
5533
+ if (dragged.current) return;
5534
+ onPress?.();
5535
+ },
5536
+ accessibilityRole: "button",
5537
+ accessibilityLabel: "Your camera. Drag to move, tap to switch camera.",
5538
+ style: styles28.fill,
5539
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles28.fill, children: /* @__PURE__ */ jsxRuntime.jsx(
5540
+ CallParticipantTile,
5541
+ {
5542
+ participant,
5543
+ VideoTrack,
5544
+ label,
5545
+ avatarSize: 48,
5546
+ hideName: true,
5547
+ style: styles28.fill
5548
+ }
5549
+ ) })
5550
+ }
5551
+ )
5552
+ }
5553
+ );
5554
+ }
5555
+ var styles28 = reactNative.StyleSheet.create({
5556
+ pip: {
5557
+ position: "absolute",
5558
+ top: 0,
5559
+ left: 0,
5560
+ width: PIP_WIDTH,
5561
+ height: PIP_HEIGHT,
5562
+ borderRadius: 16,
5563
+ overflow: "hidden",
5564
+ // Lifted off the video so it reads as floating rather than inset.
5565
+ shadowColor: "#000",
5566
+ shadowOpacity: 0.4,
5567
+ shadowRadius: 12,
5568
+ shadowOffset: { width: 0, height: 4 },
5569
+ elevation: 8
5570
+ },
5571
+ fill: { flex: 1 }
5572
+ });
5573
+ var GAP = 8;
5574
+ var MIN_TILE_HEIGHT = 132;
5575
+ function CallVideoGrid({
5576
+ participants,
5577
+ livekitReactNative,
5578
+ labelFor,
5579
+ onLocalPress,
5580
+ chromeInsets
5581
+ }) {
5582
+ const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
5583
+ const VideoTrack = livekitReactNative?.VideoTrack;
5584
+ const [size, setSize] = react.useState({ width: 0, height: 0 });
5585
+ const onLayout = (event) => {
5586
+ const { width: width2, height } = event.nativeEvent.layout;
5587
+ setSize(
5588
+ (current) => current.width === width2 && current.height === height ? current : { width: width2, height }
5589
+ );
5590
+ };
5591
+ const self = participants.find((p) => p.isSelf) ?? null;
5592
+ const others = react.useMemo(() => participants.filter((p) => !p.isSelf), [participants]);
5593
+ const columns = others.length + 1 <= 6 ? 2 : 3;
5594
+ const rows = Math.ceil((others.length + 1) / columns);
5595
+ const tileHeight = size.height > 0 ? (size.height - GAP * (rows - 1)) / rows : 0;
5596
+ const scrolls = tileHeight > 0 && tileHeight < MIN_TILE_HEIGHT;
5597
+ if (participants.length === 0) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles29.stage, onLayout });
5598
+ if (others.length <= 1) {
5599
+ const spotlight = others[0] ?? self;
5600
+ if (!spotlight) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles29.stage, onLayout });
5601
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles29.stage, onLayout, children: [
5602
+ /* @__PURE__ */ jsxRuntime.jsx(
5603
+ CallParticipantTile,
5604
+ {
5605
+ participant: spotlight,
5606
+ VideoTrack,
5607
+ label,
5608
+ avatarSize: Math.min(148, Math.max(96, Math.round(size.width * 0.36))),
5609
+ bleed: true,
5610
+ hideName: others.length === 1,
5611
+ style: styles29.fill
5612
+ }
5613
+ ),
5614
+ self && others.length === 1 && size.width > 0 && /* @__PURE__ */ jsxRuntime.jsx(
5615
+ LocalVideoPip,
5616
+ {
5617
+ participant: self,
5618
+ VideoTrack,
5619
+ label,
5620
+ bounds: size,
5621
+ insets: chromeInsets ?? { top: 0, bottom: 0 },
5622
+ onPress: onLocalPress
5623
+ }
5624
+ )
5625
+ ] });
5626
+ }
5627
+ const tiles = [...others, ...self ? [self] : []];
5628
+ const width = size.width > 0 ? (size.width - GAP * (columns - 1)) / columns : 0;
5629
+ const grid = /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles29.grid, scrolls ? styles29.gridScrolling : styles29.gridFilling], children: tiles.map((participant) => /* @__PURE__ */ jsxRuntime.jsx(
5630
+ CallParticipantTile,
5631
+ {
5632
+ participant,
5633
+ VideoTrack,
5634
+ label,
5635
+ avatarSize: columns === 2 ? 66 : 46,
5636
+ style: {
5637
+ width: width || void 0,
5638
+ height: scrolls ? MIN_TILE_HEIGHT : tileHeight || void 0
5639
+ }
5640
+ },
5641
+ participant.userId
5642
+ )) });
5643
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles29.stage, onLayout, children: scrolls ? /* @__PURE__ */ jsxRuntime.jsx(
5644
+ reactNative.ScrollView,
5645
+ {
5646
+ contentContainerStyle: [
5647
+ styles29.scroll,
5648
+ { paddingBottom: (chromeInsets?.bottom ?? 0) + GAP }
5649
+ ],
5650
+ showsVerticalScrollIndicator: false,
5651
+ children: grid
5652
+ }
5653
+ ) : grid });
5654
+ }
5655
+ var styles29 = reactNative.StyleSheet.create({
5656
+ stage: { flex: 1, overflow: "hidden" },
5657
+ fill: { ...reactNative.StyleSheet.absoluteFillObject },
5658
+ grid: { flexDirection: "row", flexWrap: "wrap", gap: GAP },
5659
+ gridFilling: { flex: 1, alignContent: "center" },
5660
+ gridScrolling: { alignContent: "flex-start" },
5661
+ scroll: { paddingVertical: GAP }
5662
+ });
5663
+
5664
+ // src/voice/livekit-rn.ts
5665
+ function describeRoute(id) {
5666
+ switch (id) {
5667
+ case "speaker":
5668
+ case "force_speaker":
5669
+ return { id, kind: "speaker", label: "Speaker" };
5670
+ case "earpiece":
5671
+ return { id, kind: "earpiece", label: "Phone" };
5672
+ case "headset":
5673
+ return { id, kind: "headset", label: "Headphones" };
5674
+ case "bluetooth":
5675
+ return { id, kind: "bluetooth", label: "Bluetooth" };
5676
+ case "default":
5677
+ return { id, kind: "auto", label: "iPhone" };
5678
+ default:
5679
+ return { id, kind: "auto", label: id };
5680
+ }
5681
+ }
5682
+
5683
+ // src/voice/useCallAudio.ts
5684
+ var IOS_AUTO = "default";
5685
+ var IOS_SPEAKER = "force_speaker";
5686
+ function useCallAudio({
5687
+ active,
5688
+ video,
5689
+ livekitReactNative
5690
+ }) {
5691
+ const session = livekitReactNative?.AudioSession;
5692
+ const ios = reactNative.Platform.OS === "ios";
5693
+ const [routes, setRoutes] = react.useState([]);
5694
+ const [selectedRouteId, setSelectedRouteId] = react.useState(null);
5695
+ const preferSpeaker = react.useRef(video);
5696
+ if (!active) preferSpeaker.current = video;
5697
+ const userChose = react.useRef(false);
5698
+ if (!active) userChose.current = false;
5699
+ const refreshRoutes = react.useCallback(() => {
5700
+ if (!session) return;
5701
+ void session.getAudioOutputs().then((ids) => setRoutes(ids.map(describeRoute))).catch(() => {
5702
+ });
5703
+ }, [session]);
5704
+ react.useEffect(() => {
5705
+ if (!active || !session) return;
5706
+ const wantsSpeaker = preferSpeaker.current;
5707
+ let disposePolicy;
5708
+ let released = false;
5709
+ void (async () => {
5710
+ try {
5711
+ await session.configureAudio({
5712
+ android: {
5713
+ // Best device first. A headset or car kit outranks the
5714
+ // built-in outputs either way; the media type only decides
5715
+ // which built-in output wins when nothing is plugged in.
5716
+ preferredOutputList: wantsSpeaker ? ["bluetooth", "headset", "speaker", "earpiece"] : ["bluetooth", "headset", "earpiece", "speaker"]
5717
+ },
5718
+ ios: { defaultOutput: wantsSpeaker ? "speaker" : "earpiece" }
5719
+ });
5720
+ } catch {
5721
+ }
5722
+ if (released) return;
5723
+ disposePolicy = livekitReactNative?.setupIOSAudioManagement?.(wantsSpeaker);
5724
+ try {
5725
+ await session.startAudioSession();
5726
+ } catch {
5727
+ }
5728
+ if (released) {
5729
+ void session.stopAudioSession().catch(() => {
5730
+ });
5731
+ disposePolicy?.();
5732
+ return;
5733
+ }
5734
+ refreshRoutes();
5735
+ })();
5736
+ return () => {
5737
+ released = true;
5738
+ void session.stopAudioSession().catch(() => {
5739
+ });
5740
+ disposePolicy?.();
5741
+ };
5742
+ }, [active, session, livekitReactNative, refreshRoutes]);
5743
+ react.useEffect(() => {
5744
+ if (active) return;
5745
+ setSelectedRouteId(null);
5746
+ setRoutes([]);
5747
+ }, [active]);
5748
+ react.useEffect(() => {
5749
+ if (!active) return;
5750
+ const events = livekitReactNative?.audioDeviceModuleEvents;
5751
+ if (!events) return;
5752
+ const onChange = () => refreshRoutes();
5753
+ try {
5754
+ events.addDevicesUpdatedListener(onChange);
5755
+ } catch {
5756
+ return;
5757
+ }
5758
+ return () => {
5759
+ try {
5760
+ events.removeDevicesUpdatedListener(onChange);
5761
+ } catch {
5762
+ }
5763
+ };
5764
+ }, [active, livekitReactNative, refreshRoutes]);
5765
+ const selectRoute = react.useCallback(
5766
+ (id) => {
5767
+ userChose.current = true;
5768
+ setSelectedRouteId(id);
5769
+ if (!session) return;
5770
+ void session.selectAudioOutput(id).catch(() => {
5771
+ refreshRoutes();
5772
+ });
5773
+ },
5774
+ [session, refreshRoutes]
5775
+ );
5776
+ const externalDevice = react.useMemo(
5777
+ () => routes.some((r) => r.kind === "headset" || r.kind === "bluetooth"),
5778
+ [routes]
5779
+ );
5780
+ const speakerOn = selectedRouteId === null ? preferSpeaker.current && !externalDevice : ios ? selectedRouteId === IOS_SPEAKER : selectedRouteId === "speaker";
5781
+ const setSpeaker = react.useCallback(
5782
+ (on) => {
5783
+ if (ios) {
5784
+ selectRoute(on ? IOS_SPEAKER : IOS_AUTO);
5785
+ return;
5786
+ }
5787
+ if (on) {
5788
+ selectRoute("speaker");
5789
+ return;
5790
+ }
5791
+ const fallback = routes.find((r) => r.kind === "bluetooth") ?? routes.find((r) => r.kind === "headset") ?? routes.find((r) => r.kind === "earpiece");
5792
+ if (fallback) selectRoute(fallback.id);
5793
+ },
5794
+ [ios, routes, selectRoute]
5795
+ );
5796
+ const toggleSpeaker = react.useCallback(() => setSpeaker(!speakerOn), [setSpeaker, speakerOn]);
5797
+ const openSystemRoutePicker = react.useCallback(() => {
5798
+ void session?.showAudioRoutePicker?.().catch(() => {
5799
+ });
5800
+ }, [session]);
5801
+ return {
5802
+ speakerOn,
5803
+ toggleSpeaker,
5804
+ setSpeaker,
5805
+ routes,
5806
+ selectedRouteId,
5807
+ selectRoute,
5808
+ externalDevice,
5809
+ canPickSystemRoute: ios && typeof session?.showAudioRoutePicker === "function",
5810
+ openSystemRoutePicker
5811
+ };
5812
+ }
5813
+ function useCallDuration(connected) {
5814
+ const startedAt = react.useRef(null);
5815
+ const [, tick] = react.useState(0);
5816
+ if (connected && startedAt.current === null) startedAt.current = Date.now();
5817
+ if (!connected && startedAt.current !== null) startedAt.current = null;
5818
+ react.useEffect(() => {
5819
+ if (!connected) return;
5820
+ const id = setInterval(() => tick((n) => n + 1), 1e3);
5821
+ return () => clearInterval(id);
5822
+ }, [connected]);
5823
+ if (!connected || startedAt.current === null) return null;
5824
+ return format(Math.max(0, Math.floor((Date.now() - startedAt.current) / 1e3)));
5825
+ }
5826
+ function format(total) {
5827
+ const hours = Math.floor(total / 3600);
5828
+ const minutes = Math.floor(total % 3600 / 60);
5829
+ const seconds = total % 60;
5830
+ const mm = hours > 0 ? String(minutes).padStart(2, "0") : String(minutes);
5831
+ return `${hours > 0 ? `${hours}:` : ""}${mm}:${String(seconds).padStart(2, "0")}`;
5832
+ }
5833
+ var CHROME_IDLE_MS = 4500;
5186
5834
  function CallScreen({
5187
5835
  userId,
5188
5836
  labelFor,
@@ -5195,7 +5843,7 @@ function CallScreen({
5195
5843
  onVideoChange,
5196
5844
  nativeIncomingUi
5197
5845
  }) {
5198
- const theme = usePoolseTheme();
5846
+ const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
5199
5847
  const ownCalls = react$1.useCalls(callsProp ? null : userId);
5200
5848
  const calls = callsProp ?? ownCalls;
5201
5849
  const meshAvailable = isNativeWebRtcAvailable(webrtc);
@@ -5204,19 +5852,21 @@ function CallScreen({
5204
5852
  [meshAvailable, webrtc]
5205
5853
  );
5206
5854
  const conversationId = calls.phase === "active" ? calls.activeConversationId : null;
5855
+ const isVideoCall = calls.media === "video";
5207
5856
  const sfu = react$1.useCallRoom(livekit ? conversationId : null, {
5208
5857
  livekit,
5209
- video: calls.media === "video"
5858
+ video: isVideoCall
5210
5859
  });
5211
5860
  const mesh = react$1.useVoiceRoom(conversationId, opts);
5212
5861
  const usingSfu = sfu.sfuAvailable === true;
5213
5862
  const status = usingSfu ? toVoiceStatus(sfu.status) : mesh.status;
5863
+ const connected = calls.phase === "active" && status === "connected";
5214
5864
  react.useEffect(() => {
5215
5865
  if (calls.phase !== "active" || !conversationId) return;
5216
5866
  let cancelled = false;
5217
5867
  void (async () => {
5218
- const connected2 = livekit ? await sfu.join() : false;
5219
- if (cancelled || connected2) return;
5868
+ const joined = livekit ? await sfu.join() : false;
5869
+ if (cancelled || joined) return;
5220
5870
  if (meshAvailable) await mesh.join();
5221
5871
  })();
5222
5872
  return () => {
@@ -5234,79 +5884,219 @@ function CallScreen({
5234
5884
  react.useEffect(() => {
5235
5885
  onVideoChange?.(videoOn);
5236
5886
  }, [videoOn, onVideoChange]);
5237
- const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
5238
- const ringingIn = calls.phase === "ringing-in";
5239
- const visible = calls.phase !== "idle" && !(nativeIncomingUi && ringingIn);
5887
+ const audio = useCallAudio({
5888
+ active: calls.phase !== "idle" && transport !== "mesh",
5889
+ video: isVideoCall,
5890
+ livekitReactNative
5891
+ });
5892
+ const [routesOpen, setRoutesOpen] = react.useState(false);
5893
+ const [chromeVisible, setChromeVisible] = react.useState(true);
5894
+ const [lastWake, setLastWake] = react.useState(0);
5895
+ const chromeHides = connected && isVideoCall;
5896
+ react.useEffect(() => {
5897
+ setChromeVisible(true);
5898
+ if (!chromeHides) return;
5899
+ const timer = setTimeout(() => setChromeVisible(false), CHROME_IDLE_MS);
5900
+ return () => clearTimeout(timer);
5901
+ }, [chromeHides, lastWake]);
5902
+ const wakeChrome = react.useCallback(() => setLastWake(Date.now()), []);
5903
+ const withWake = react.useCallback(
5904
+ (fn) => () => {
5905
+ wakeChrome();
5906
+ fn();
5907
+ },
5908
+ [wakeChrome]
5909
+ );
5240
5910
  const hangUp = react.useCallback(() => {
5241
5911
  sfu.leave();
5242
5912
  mesh.leave();
5243
5913
  void calls.hangUp();
5244
5914
  }, [sfu, mesh, calls]);
5915
+ const alone = usingSfu ? sfu.alone : mesh.alone;
5916
+ const endedOnEmpty = react.useRef(false);
5917
+ if (calls.phase !== "active") endedOnEmpty.current = false;
5918
+ react.useEffect(() => {
5919
+ if (!alone || calls.phase !== "active" || endedOnEmpty.current) return;
5920
+ endedOnEmpty.current = true;
5921
+ hangUp();
5922
+ }, [alone, calls.phase, hangUp]);
5245
5923
  const micOn = usingSfu ? sfu.micEnabled : !mesh.muted;
5246
5924
  const toggleMic = react.useCallback(() => {
5247
5925
  if (usingSfu) void sfu.toggleMic();
5248
5926
  else mesh.toggleMute();
5249
5927
  }, [usingSfu, sfu, mesh]);
5928
+ const onSpeakerPress = react.useCallback(() => {
5929
+ if (audio.routes.length > 2) setRoutesOpen(true);
5930
+ else if (audio.canPickSystemRoute && audio.externalDevice) audio.openSystemRoutePicker();
5931
+ else audio.toggleSpeaker();
5932
+ }, [audio]);
5933
+ const label = react.useMemo(() => labelFor ?? ((id) => `User ${id.slice(0, 6)}`), [labelFor]);
5934
+ const ringingIn = calls.phase === "ringing-in";
5935
+ const ringingOut = calls.phase === "ringing-out";
5936
+ const ended = calls.phase === "declined" || calls.phase === "busy" || calls.phase === "timed-out";
5937
+ const visible = calls.phase !== "idle" && !(nativeIncomingUi && ringingIn);
5938
+ const duration = useCallDuration(connected);
5939
+ const participants = usingSfu ? sfu.participants : [];
5940
+ const others = participants.filter((p) => !p.isSelf);
5941
+ const isGroup = others.length > 1 || (calls.outgoing?.calleeUserIds.length ?? 0) > 1;
5942
+ const peerIds = ringingIn && calls.incoming ? [calls.incoming.callerUserId] : others.length > 0 ? others.map((p) => p.userId) : calls.outgoing?.calleeUserIds ?? [];
5943
+ const title = peerIds.length === 0 ? "Call" : peerIds.length <= 2 ? peerIds.map(label).join(", ") : `${label(peerIds[0])} and ${peerIds.length - 1} others`;
5944
+ const subtitle = duration ?? statusLine(calls, status);
5945
+ const showStage = connected && usingSfu && participants.length > 0;
5250
5946
  const error = calls.error ?? (usingSfu ? sfu.error : mesh.error);
5251
- const connected = calls.phase === "active" && status === "connected";
5252
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, animationType: "slide", transparent: false, onRequestClose: hangUp, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles25.screen, { backgroundColor: theme.colors.paper }], children: [
5253
- connected && usingSfu && sfu.participants.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
5254
- CallVideoGrid,
5255
- {
5256
- participants: sfu.participants,
5257
- livekitReactNative,
5258
- labelFor
5259
- }
5260
- ) : /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles25.identity, children: [
5261
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles25.who, { color: theme.colors.ink }], children: ringingIn && calls.incoming ? label(calls.incoming.callerUserId) : calls.outgoing ? calls.outgoing.calleeUserIds.map(label).join(", ") : "" }),
5262
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles25.state, { color: theme.colors.ink3 }], children: statusLine(calls, status) })
5263
- ] }),
5264
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles25.actions, children: ringingIn ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5265
- /* @__PURE__ */ jsxRuntime.jsx(
5266
- CallButton,
5267
- {
5268
- label: "Decline",
5269
- color: theme.colors.error,
5270
- onPress: () => void calls.decline()
5271
- }
5272
- ),
5273
- /* @__PURE__ */ jsxRuntime.jsx(
5274
- CallButton,
5275
- {
5276
- label: "Accept",
5277
- color: theme.colors.brand,
5278
- onPress: () => void calls.accept()
5279
- }
5280
- )
5281
- ] }) : calls.phase === "ringing-out" ? /* @__PURE__ */ jsxRuntime.jsx(
5282
- CallButton,
5283
- {
5284
- label: "Cancel",
5285
- color: theme.colors.error,
5286
- onPress: () => void calls.cancel()
5287
- }
5288
- ) : calls.phase === "declined" || calls.phase === "busy" || calls.phase === "timed-out" ? /* @__PURE__ */ jsxRuntime.jsx(CallButton, { label: "Close", color: theme.colors.ink3, onPress: calls.reset }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5289
- /* @__PURE__ */ jsxRuntime.jsx(
5290
- CallButton,
5291
- {
5292
- label: micOn ? "Mute" : "Unmute",
5293
- color: micOn ? theme.colors.ink2 : theme.colors.brand,
5294
- onPress: toggleMic
5295
- }
5296
- ),
5297
- usingSfu && /* @__PURE__ */ jsxRuntime.jsx(
5298
- CallButton,
5299
- {
5300
- label: videoOn ? "Camera off" : "Camera on",
5301
- color: videoOn ? theme.colors.brand : theme.colors.ink2,
5302
- onPress: () => void sfu.toggleCamera()
5303
- }
5304
- ),
5305
- /* @__PURE__ */ jsxRuntime.jsx(CallButton, { label: "Hang up", color: theme.colors.error, onPress: hangUp })
5306
- ] }) }),
5307
- !meshAvailable && !livekit && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles25.warn, { color: theme.colors.error }], children: "No WebRTC module supplied \u2014 this call has no audio." }),
5308
- error && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles25.warn, { color: theme.colors.error }], children: error.message })
5309
- ] }) });
5947
+ const chromeInsets = {
5948
+ top: insets.top + 72,
5949
+ bottom: insets.bottom + 150
5950
+ };
5951
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5952
+ reactNative.Modal,
5953
+ {
5954
+ visible,
5955
+ animationType: "slide",
5956
+ transparent: false,
5957
+ supportedOrientations: ["portrait", "landscape"],
5958
+ onRequestClose: hangUp,
5959
+ children: [
5960
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.StatusBar, { barStyle: "light-content" }),
5961
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles30.screen, onPress: wakeChrome, accessible: false, children: [
5962
+ showStage ? /* @__PURE__ */ jsxRuntime.jsx(
5963
+ CallVideoGrid,
5964
+ {
5965
+ participants,
5966
+ livekitReactNative,
5967
+ labelFor: label,
5968
+ onLocalPress: () => void sfu.switchCamera(),
5969
+ chromeInsets
5970
+ }
5971
+ ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles30.identity, children: /* @__PURE__ */ jsxRuntime.jsx(
5972
+ CallAvatar,
5973
+ {
5974
+ name: peerIds.length === 1 ? label(peerIds[0]) : title,
5975
+ seed: peerIds[0] ?? "call",
5976
+ size: 132
5977
+ }
5978
+ ) }),
5979
+ chromeVisible && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles30.header, { paddingTop: insets.top + 14 }], pointerEvents: "box-none", children: [
5980
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: styles30.title, children: title }),
5981
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles30.subtitleRow, children: [
5982
+ isGroup && showStage && /* @__PURE__ */ jsxRuntime.jsx(PoolseIcon, { name: "users", size: 14, color: callColors.textDim, label: null }),
5983
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles30.subtitle, children: isGroup && showStage ? `${participants.length} \xB7 ${subtitle}` : subtitle })
5984
+ ] })
5985
+ ] }),
5986
+ chromeVisible && /* @__PURE__ */ jsxRuntime.jsxs(
5987
+ reactNative.View,
5988
+ {
5989
+ style: [styles30.controls, { paddingBottom: insets.bottom + 22 }],
5990
+ pointerEvents: "box-none",
5991
+ children: [
5992
+ error && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles30.error, children: error.message }),
5993
+ !meshAvailable && !livekit && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles30.error, children: "No WebRTC module supplied \u2014 this call has no audio." }),
5994
+ ringingIn ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles30.row, children: [
5995
+ /* @__PURE__ */ jsxRuntime.jsx(
5996
+ CallControlButton,
5997
+ {
5998
+ icon: "phone-off",
5999
+ tone: "danger",
6000
+ large: true,
6001
+ onPress: () => void calls.decline(),
6002
+ accessibilityLabel: "Decline call"
6003
+ }
6004
+ ),
6005
+ /* @__PURE__ */ jsxRuntime.jsx(
6006
+ CallControlButton,
6007
+ {
6008
+ icon: isVideoCall ? "video" : "phone",
6009
+ tone: "accept",
6010
+ large: true,
6011
+ onPress: () => void calls.accept(),
6012
+ accessibilityLabel: "Accept call"
6013
+ }
6014
+ )
6015
+ ] }) : ended ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles30.row, children: /* @__PURE__ */ jsxRuntime.jsx(
6016
+ CallControlButton,
6017
+ {
6018
+ icon: "close",
6019
+ onPress: calls.reset,
6020
+ label: "Close",
6021
+ accessibilityLabel: "Close"
6022
+ }
6023
+ ) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6024
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles30.row, children: [
6025
+ /* @__PURE__ */ jsxRuntime.jsx(
6026
+ CallControlButton,
6027
+ {
6028
+ icon: micOn ? "mic" : "mic-off",
6029
+ label: micOn ? "Mute" : "Unmute",
6030
+ active: !micOn,
6031
+ onPress: withWake(toggleMic),
6032
+ accessibilityLabel: micOn ? "Mute microphone" : "Unmute microphone"
6033
+ }
6034
+ ),
6035
+ /* @__PURE__ */ jsxRuntime.jsx(
6036
+ CallControlButton,
6037
+ {
6038
+ icon: speakerIcon(audio),
6039
+ label: speakerLabel(audio),
6040
+ active: audio.speakerOn,
6041
+ onPress: withWake(onSpeakerPress),
6042
+ accessibilityLabel: "Audio output"
6043
+ }
6044
+ ),
6045
+ usingSfu && /* @__PURE__ */ jsxRuntime.jsx(
6046
+ CallControlButton,
6047
+ {
6048
+ icon: videoOn ? "video" : "video-off",
6049
+ label: "Camera",
6050
+ active: !videoOn,
6051
+ onPress: withWake(() => void sfu.toggleCamera()),
6052
+ accessibilityLabel: videoOn ? "Turn camera off" : "Turn camera on"
6053
+ }
6054
+ ),
6055
+ usingSfu && videoOn && /* @__PURE__ */ jsxRuntime.jsx(
6056
+ CallControlButton,
6057
+ {
6058
+ icon: "camera-flip",
6059
+ label: "Flip",
6060
+ onPress: withWake(() => void sfu.switchCamera()),
6061
+ accessibilityLabel: "Switch camera"
6062
+ }
6063
+ )
6064
+ ] }),
6065
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles30.hangUpRow, children: /* @__PURE__ */ jsxRuntime.jsx(
6066
+ CallControlButton,
6067
+ {
6068
+ icon: "phone-off",
6069
+ tone: "danger",
6070
+ large: true,
6071
+ onPress: ringingOut ? () => void calls.cancel() : hangUp,
6072
+ accessibilityLabel: ringingOut ? "Cancel call" : "Hang up"
6073
+ }
6074
+ ) })
6075
+ ] })
6076
+ ]
6077
+ }
6078
+ ),
6079
+ /* @__PURE__ */ jsxRuntime.jsx(
6080
+ AudioRouteSheet,
6081
+ {
6082
+ visible: routesOpen,
6083
+ routes: audio.routes,
6084
+ selectedRouteId: audio.selectedRouteId,
6085
+ onSelect: audio.selectRoute,
6086
+ onClose: () => setRoutesOpen(false)
6087
+ }
6088
+ )
6089
+ ] })
6090
+ ]
6091
+ }
6092
+ );
6093
+ }
6094
+ function speakerIcon(audio) {
6095
+ if (audio.externalDevice) return "headphones";
6096
+ return audio.speakerOn ? "speaker" : "speaker-off";
6097
+ }
6098
+ function speakerLabel(audio) {
6099
+ return audio.externalDevice ? "Audio" : "Speaker";
5310
6100
  }
5311
6101
  function toVoiceStatus(status) {
5312
6102
  switch (status) {
@@ -5323,7 +6113,7 @@ function toVoiceStatus(status) {
5323
6113
  function statusLine(calls, mediaStatus) {
5324
6114
  switch (calls.phase) {
5325
6115
  case "ringing-in":
5326
- return calls.media === "video" ? "Incoming video call\u2026" : "Incoming call\u2026";
6116
+ return calls.media === "video" ? "Incoming video call" : "Incoming call";
5327
6117
  case "ringing-out":
5328
6118
  return "Ringing\u2026";
5329
6119
  case "declined":
@@ -5338,36 +6128,41 @@ function statusLine(calls, mediaStatus) {
5338
6128
  return "";
5339
6129
  }
5340
6130
  }
5341
- function CallButton({
5342
- label,
5343
- color,
5344
- onPress
5345
- }) {
5346
- return /* @__PURE__ */ jsxRuntime.jsx(
5347
- reactNative.Pressable,
5348
- {
5349
- onPress,
5350
- accessibilityRole: "button",
5351
- style: [styles25.callBtn, { backgroundColor: color }],
5352
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles25.callBtnText, children: label })
5353
- }
5354
- );
5355
- }
5356
- var styles25 = reactNative.StyleSheet.create({
5357
- screen: { flex: 1, justifyContent: "space-between", paddingVertical: 72, paddingHorizontal: 24 },
5358
- identity: { alignItems: "center", gap: 8, marginTop: 48 },
5359
- who: { fontSize: 28, fontWeight: "600", textAlign: "center" },
5360
- state: { fontSize: 15 },
5361
- actions: { flexDirection: "row", justifyContent: "center", gap: 12, flexWrap: "wrap" },
5362
- callBtn: {
5363
- paddingHorizontal: 22,
5364
- paddingVertical: 16,
5365
- borderRadius: 999,
5366
- minWidth: 108,
5367
- alignItems: "center"
5368
- },
5369
- callBtnText: { color: "#ffffff", fontSize: 16, fontWeight: "600" },
5370
- warn: { fontSize: 12, textAlign: "center", marginTop: 12 }
6131
+ var styles30 = reactNative.StyleSheet.create({
6132
+ screen: { flex: 1, backgroundColor: callColors.bg },
6133
+ identity: { flex: 1, alignItems: "center", justifyContent: "center" },
6134
+ header: {
6135
+ position: "absolute",
6136
+ top: 0,
6137
+ left: 0,
6138
+ right: 0,
6139
+ alignItems: "center",
6140
+ gap: 5,
6141
+ paddingHorizontal: 24,
6142
+ paddingBottom: 18,
6143
+ backgroundColor: callColors.scrim
6144
+ },
6145
+ title: { color: callColors.text, fontSize: 22, fontWeight: "600", textAlign: "center" },
6146
+ subtitleRow: { flexDirection: "row", alignItems: "center", gap: 6 },
6147
+ subtitle: { color: callColors.textDim, fontSize: 14.5 },
6148
+ controls: {
6149
+ position: "absolute",
6150
+ left: 0,
6151
+ right: 0,
6152
+ bottom: 0,
6153
+ alignItems: "center",
6154
+ gap: 18,
6155
+ paddingTop: 20,
6156
+ backgroundColor: callColors.scrim
6157
+ },
6158
+ row: { flexDirection: "row", justifyContent: "center", flexWrap: "wrap", rowGap: 16 },
6159
+ hangUpRow: { flexDirection: "row", justifyContent: "center" },
6160
+ error: {
6161
+ color: callColors.danger,
6162
+ fontSize: 12.5,
6163
+ textAlign: "center",
6164
+ paddingHorizontal: 24
6165
+ }
5371
6166
  });
5372
6167
  function isCallOver(phase) {
5373
6168
  if (phase === void 0) return false;
@@ -5549,7 +6344,11 @@ function guessApnsEnvironment() {
5549
6344
 
5550
6345
  exports.AttachmentPicker = AttachmentPicker;
5551
6346
  exports.AttachmentPreview = AttachmentPreview;
6347
+ exports.AudioRouteSheet = AudioRouteSheet;
5552
6348
  exports.Avatar = Avatar;
6349
+ exports.CallAvatar = CallAvatar;
6350
+ exports.CallControlButton = CallControlButton;
6351
+ exports.CallParticipantTile = CallParticipantTile;
5553
6352
  exports.CallScreen = CallScreen;
5554
6353
  exports.CallVideoGrid = CallVideoGrid;
5555
6354
  exports.ChatHeader = ChatHeader;
@@ -5558,6 +6357,7 @@ exports.ConversationView = ConversationView;
5558
6357
  exports.DaySeparator = DaySeparator;
5559
6358
  exports.GroupDetailsSheet = GroupDetailsSheet;
5560
6359
  exports.ImageMosaic = ImageMosaic;
6360
+ exports.LocalVideoPip = LocalVideoPip;
5561
6361
  exports.MemberList = MemberList;
5562
6362
  exports.MentionInput = MentionInput;
5563
6363
  exports.MessageActions = MessageActions;
@@ -5577,12 +6377,16 @@ exports.TypingIndicator = TypingIndicator;
5577
6377
  exports.UploadQueueStrip = UploadQueueStrip;
5578
6378
  exports.UserName = UserName;
5579
6379
  exports.VoiceRoomBar = VoiceRoomBar;
6380
+ exports.callColors = callColors;
5580
6381
  exports.createNativeWebRtcAdapter = createNativeWebRtcAdapter;
5581
6382
  exports.defaultDarkTheme = defaultDarkTheme;
5582
6383
  exports.defaultLightTheme = defaultLightTheme;
6384
+ exports.describeRoute = describeRoute;
5583
6385
  exports.isNativeWebRtcAvailable = isNativeWebRtcAvailable;
5584
6386
  exports.isStalePush = isStalePush;
5585
6387
  exports.useAppStateLifecycle = useAppStateLifecycle;
6388
+ exports.useCallAudio = useCallAudio;
6389
+ exports.useCallDuration = useCallDuration;
5586
6390
  exports.useDisplayName = useDisplayName;
5587
6391
  exports.usePoolseTheme = usePoolseTheme;
5588
6392
  exports.useVoipCalls = useVoipCalls;